195 lines
5.0 KiB
Rust
195 lines
5.0 KiB
Rust
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
const FILE_HEADER: &str = "/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/";
|
|
|
|
use std::{
|
|
collections::HashMap,
|
|
env, fs, io,
|
|
path::{Path, PathBuf},
|
|
process::{self},
|
|
str::FromStr,
|
|
};
|
|
|
|
use serde::{de::DeserializeOwned, Deserialize};
|
|
use serde_json::Value;
|
|
|
|
fn main() {
|
|
let files = enumerate_source_files().expect("expected to enumerate files");
|
|
ensure_file_headers(&files).expect("expected to ensure file headers");
|
|
apply_build_environment_variables();
|
|
}
|
|
|
|
fn camel_case_to_constant_case(key: &str) -> String {
|
|
let mut output = String::new();
|
|
let mut prev_upper = false;
|
|
for c in key.chars() {
|
|
if c.is_uppercase() {
|
|
if prev_upper {
|
|
output.push(c.to_ascii_lowercase());
|
|
} else {
|
|
output.push('_');
|
|
output.push(c.to_ascii_uppercase());
|
|
}
|
|
prev_upper = true;
|
|
} else if c.is_lowercase() {
|
|
output.push(c.to_ascii_uppercase());
|
|
prev_upper = false;
|
|
} else {
|
|
output.push(c);
|
|
prev_upper = false;
|
|
}
|
|
}
|
|
|
|
output
|
|
}
|
|
|
|
fn set_env_vars_from_map_keys(prefix: &str, map: impl IntoIterator<Item = (String, Value)>) {
|
|
let mut win32_app_ids = vec![];
|
|
|
|
for (key, value) in map {
|
|
//#region special handling
|
|
let value = match key.as_str() {
|
|
"tunnelServerQualities" | "serverLicense" => {
|
|
Value::String(serde_json::to_string(&value).unwrap())
|
|
}
|
|
"nameLong" => {
|
|
if let Value::String(s) = &value {
|
|
let idx = s.find(" - ");
|
|
println!(
|
|
"cargo:rustc-env=VSCODE_CLI_QUALITYLESS_PRODUCT_NAME={}",
|
|
idx.map(|i| &s[..i]).unwrap_or(s)
|
|
);
|
|
}
|
|
|
|
value
|
|
}
|
|
"tunnelApplicationConfig" => {
|
|
if let Value::Object(v) = value {
|
|
set_env_vars_from_map_keys(&format!("{}_{}", prefix, "TUNNEL"), v);
|
|
}
|
|
continue;
|
|
}
|
|
_ => value,
|
|
};
|
|
if key.contains("win32") && key.contains("AppId") {
|
|
if let Value::String(s) = value {
|
|
win32_app_ids.push(s);
|
|
continue;
|
|
}
|
|
}
|
|
//#endregion
|
|
|
|
if let Value::String(s) = value {
|
|
println!(
|
|
"cargo:rustc-env={}_{}={}",
|
|
prefix,
|
|
camel_case_to_constant_case(&key),
|
|
s
|
|
);
|
|
}
|
|
}
|
|
|
|
if !win32_app_ids.is_empty() {
|
|
println!(
|
|
"cargo:rustc-env=VSCODE_CLI_WIN32_APP_IDS={}",
|
|
win32_app_ids.join(",")
|
|
);
|
|
}
|
|
}
|
|
|
|
fn read_json_from_path<T>(path: &Path) -> T
|
|
where
|
|
T: DeserializeOwned,
|
|
{
|
|
let mut file = fs::File::open(path).expect("failed to open file");
|
|
serde_json::from_reader(&mut file).expect("failed to deserialize JSON")
|
|
}
|
|
|
|
fn apply_build_from_product_json(path: &Path) {
|
|
let json: HashMap<String, Value> = read_json_from_path(path);
|
|
set_env_vars_from_map_keys("VSCODE_CLI", json);
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct PackageJson {
|
|
pub version: String,
|
|
}
|
|
|
|
fn apply_build_environment_variables() {
|
|
let repo_dir = env::current_dir().unwrap().join("..");
|
|
let package_json = read_json_from_path::<PackageJson>(&repo_dir.join("package.json"));
|
|
println!(
|
|
"cargo:rustc-env=VSCODE_CLI_VERSION={}",
|
|
package_json.version
|
|
);
|
|
|
|
match env::var("VSCODE_CLI_PRODUCT_JSON") {
|
|
Ok(v) => {
|
|
let path = if cfg!(windows) {
|
|
PathBuf::from_str(&v.replace('/', "\\")).unwrap()
|
|
} else {
|
|
PathBuf::from_str(&v).unwrap()
|
|
};
|
|
println!("cargo:warning=loading product.json from <{:?}>", path);
|
|
apply_build_from_product_json(&path);
|
|
}
|
|
|
|
Err(_) => {
|
|
apply_build_from_product_json(&repo_dir.join("product.json"));
|
|
|
|
let overrides = repo_dir.join("product.overrides.json");
|
|
if overrides.exists() {
|
|
apply_build_from_product_json(&overrides);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
fn ensure_file_headers(files: &[PathBuf]) -> Result<(), io::Error> {
|
|
let mut ok = true;
|
|
|
|
let crlf_header_str = str::replace(FILE_HEADER, "\n", "\r\n");
|
|
let crlf_header = crlf_header_str.as_bytes();
|
|
let lf_header = FILE_HEADER.as_bytes();
|
|
for file in files {
|
|
let contents = fs::read(file)?;
|
|
|
|
if !(contents.starts_with(lf_header) || contents.starts_with(crlf_header)) {
|
|
eprintln!("File missing copyright header: {}", file.display());
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
if !ok {
|
|
process::exit(1);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Gets all "rs" files in the source directory
|
|
fn enumerate_source_files() -> Result<Vec<PathBuf>, io::Error> {
|
|
let mut files = vec![];
|
|
let mut queue = vec![];
|
|
|
|
let current_dir = env::current_dir()?.join("src");
|
|
queue.push(current_dir);
|
|
|
|
while !queue.is_empty() {
|
|
for entry in fs::read_dir(queue.pop().unwrap())? {
|
|
let entry = entry?;
|
|
let ftype = entry.file_type()?;
|
|
if ftype.is_dir() {
|
|
queue.push(entry.path());
|
|
} else if ftype.is_file() && entry.file_name().to_string_lossy().ends_with(".rs") {
|
|
files.push(entry.path());
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(files)
|
|
}
|