build.rs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. 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 *--------------------------------------------------------------------------------------------*/";
  6. use std::{
  7. collections::HashMap,
  8. env, fs, io,
  9. path::{Path, PathBuf},
  10. process::{self},
  11. str::FromStr,
  12. };
  13. use serde::{de::DeserializeOwned, Deserialize};
  14. use serde_json::Value;
  15. fn main() {
  16. let files = enumerate_source_files().expect("expected to enumerate files");
  17. ensure_file_headers(&files).expect("expected to ensure file headers");
  18. apply_build_environment_variables();
  19. }
  20. fn camel_case_to_constant_case(key: &str) -> String {
  21. let mut output = String::new();
  22. let mut prev_upper = false;
  23. for c in key.chars() {
  24. if c.is_uppercase() {
  25. if prev_upper {
  26. output.push(c.to_ascii_lowercase());
  27. } else {
  28. output.push('_');
  29. output.push(c.to_ascii_uppercase());
  30. }
  31. prev_upper = true;
  32. } else if c.is_lowercase() {
  33. output.push(c.to_ascii_uppercase());
  34. prev_upper = false;
  35. } else {
  36. output.push(c);
  37. prev_upper = false;
  38. }
  39. }
  40. output
  41. }
  42. fn set_env_vars_from_map_keys(prefix: &str, map: impl IntoIterator<Item = (String, Value)>) {
  43. let mut win32_app_ids = vec![];
  44. for (key, value) in map {
  45. //#region special handling
  46. let value = match key.as_str() {
  47. "tunnelServerQualities" | "serverLicense" => {
  48. Value::String(serde_json::to_string(&value).unwrap())
  49. }
  50. "nameLong" => {
  51. if let Value::String(s) = &value {
  52. let idx = s.find(" - ");
  53. println!(
  54. "cargo:rustc-env=VSCODE_CLI_QUALITYLESS_PRODUCT_NAME={}",
  55. idx.map(|i| &s[..i]).unwrap_or(s)
  56. );
  57. }
  58. value
  59. }
  60. "tunnelApplicationConfig" => {
  61. if let Value::Object(v) = value {
  62. set_env_vars_from_map_keys(&format!("{}_{}", prefix, "TUNNEL"), v);
  63. }
  64. continue;
  65. }
  66. _ => value,
  67. };
  68. if key.contains("win32") && key.contains("AppId") {
  69. if let Value::String(s) = value {
  70. win32_app_ids.push(s);
  71. continue;
  72. }
  73. }
  74. //#endregion
  75. if let Value::String(s) = value {
  76. println!(
  77. "cargo:rustc-env={}_{}={}",
  78. prefix,
  79. camel_case_to_constant_case(&key),
  80. s
  81. );
  82. }
  83. }
  84. if !win32_app_ids.is_empty() {
  85. println!(
  86. "cargo:rustc-env=VSCODE_CLI_WIN32_APP_IDS={}",
  87. win32_app_ids.join(",")
  88. );
  89. }
  90. }
  91. fn read_json_from_path<T>(path: &Path) -> T
  92. where
  93. T: DeserializeOwned,
  94. {
  95. let mut file = fs::File::open(path).expect("failed to open file");
  96. serde_json::from_reader(&mut file).expect("failed to deserialize JSON")
  97. }
  98. fn apply_build_from_product_json(path: &Path) {
  99. let json: HashMap<String, Value> = read_json_from_path(path);
  100. set_env_vars_from_map_keys("VSCODE_CLI", json);
  101. }
  102. #[derive(Deserialize)]
  103. struct PackageJson {
  104. pub version: String,
  105. }
  106. fn apply_build_environment_variables() {
  107. let repo_dir = env::current_dir().unwrap().join("..");
  108. let package_json = read_json_from_path::<PackageJson>(&repo_dir.join("package.json"));
  109. println!(
  110. "cargo:rustc-env=VSCODE_CLI_VERSION={}",
  111. package_json.version
  112. );
  113. match env::var("VSCODE_CLI_PRODUCT_JSON") {
  114. Ok(v) => {
  115. let path = if cfg!(windows) {
  116. PathBuf::from_str(&v.replace('/', "\\")).unwrap()
  117. } else {
  118. PathBuf::from_str(&v).unwrap()
  119. };
  120. println!("cargo:warning=loading product.json from <{:?}>", path);
  121. apply_build_from_product_json(&path);
  122. }
  123. Err(_) => {
  124. apply_build_from_product_json(&repo_dir.join("product.json"));
  125. let overrides = repo_dir.join("product.overrides.json");
  126. if overrides.exists() {
  127. apply_build_from_product_json(&overrides);
  128. }
  129. }
  130. };
  131. }
  132. fn ensure_file_headers(files: &[PathBuf]) -> Result<(), io::Error> {
  133. let mut ok = true;
  134. let crlf_header_str = str::replace(FILE_HEADER, "\n", "\r\n");
  135. let crlf_header = crlf_header_str.as_bytes();
  136. let lf_header = FILE_HEADER.as_bytes();
  137. for file in files {
  138. let contents = fs::read(file)?;
  139. if !(contents.starts_with(lf_header) || contents.starts_with(crlf_header)) {
  140. eprintln!("File missing copyright header: {}", file.display());
  141. ok = false;
  142. }
  143. }
  144. if !ok {
  145. process::exit(1);
  146. }
  147. Ok(())
  148. }
  149. /// Gets all "rs" files in the source directory
  150. fn enumerate_source_files() -> Result<Vec<PathBuf>, io::Error> {
  151. let mut files = vec![];
  152. let mut queue = vec![];
  153. let current_dir = env::current_dir()?.join("src");
  154. queue.push(current_dir);
  155. while !queue.is_empty() {
  156. for entry in fs::read_dir(queue.pop().unwrap())? {
  157. let entry = entry?;
  158. let ftype = entry.file_type()?;
  159. if ftype.is_dir() {
  160. queue.push(entry.path());
  161. } else if ftype.is_file() && entry.file_name().to_string_lossy().ends_with(".rs") {
  162. files.push(entry.path());
  163. }
  164. }
  165. }
  166. Ok(files)
  167. }