| 1 | use crate::util::ArrayDisplay; |
| 2 | use crate::{fmt_option_str, write_str_variable, write_variable}; |
| 3 | use std::{borrow, collections, env, ffi, fmt, fs, io, process}; |
| 4 | |
| 5 | pub struct EnvironmentMap(collections::HashMap<String, String>); |
| 6 | |
| 7 | fn get_version_from_cmd(executable: &ffi::OsStr) -> io::Result<String> { |
| 8 | let output: Output = process::Command::new(program:executable).arg("-V" ).output()?; |
| 9 | let mut v: String = String::from_utf8(vec:output.stdout).unwrap(); |
| 10 | v.pop(); // remove newline |
| 11 | Ok(v) |
| 12 | } |
| 13 | |
| 14 | impl EnvironmentMap { |
| 15 | pub fn new() -> Self { |
| 16 | let mut envmap = collections::HashMap::new(); |
| 17 | for (k, v) in env::vars_os() { |
| 18 | let k = k.into_string(); |
| 19 | let v = v.into_string(); |
| 20 | if let (Ok(k), Ok(v)) = (k, v) { |
| 21 | envmap.insert(k, v); |
| 22 | } |
| 23 | } |
| 24 | Self(envmap) |
| 25 | } |
| 26 | |
| 27 | pub fn write_ci(&self, mut w: &fs::File) -> io::Result<()> { |
| 28 | use io::Write; |
| 29 | |
| 30 | write_variable!( |
| 31 | w, |
| 32 | "CI_PLATFORM" , |
| 33 | "Option<&str>" , |
| 34 | fmt_option_str(self.detect_ci()), |
| 35 | "The Continuous Integration platform detected during compilation." |
| 36 | ); |
| 37 | Ok(()) |
| 38 | } |
| 39 | |
| 40 | pub fn write_env(&self, mut w: &fs::File) -> io::Result<()> { |
| 41 | use io::Write; |
| 42 | macro_rules! write_env_str { |
| 43 | ($(($name:ident, $env_name:expr,$doc:expr)),*) => {$( |
| 44 | write_str_variable!( |
| 45 | w, |
| 46 | stringify!($name), |
| 47 | self.0.get($env_name) |
| 48 | .expect(stringify!(Missing expected environment variable $env_name)), |
| 49 | $doc |
| 50 | ); |
| 51 | )*} |
| 52 | } |
| 53 | |
| 54 | write_env_str!( |
| 55 | (PKG_VERSION, "CARGO_PKG_VERSION" , "The full version." ), |
| 56 | ( |
| 57 | PKG_VERSION_MAJOR, |
| 58 | "CARGO_PKG_VERSION_MAJOR" , |
| 59 | "The major version." |
| 60 | ), |
| 61 | ( |
| 62 | PKG_VERSION_MINOR, |
| 63 | "CARGO_PKG_VERSION_MINOR" , |
| 64 | "The minor version." |
| 65 | ), |
| 66 | ( |
| 67 | PKG_VERSION_PATCH, |
| 68 | "CARGO_PKG_VERSION_PATCH" , |
| 69 | "The patch version." |
| 70 | ), |
| 71 | ( |
| 72 | PKG_VERSION_PRE, |
| 73 | "CARGO_PKG_VERSION_PRE" , |
| 74 | "The pre-release version." |
| 75 | ), |
| 76 | ( |
| 77 | PKG_AUTHORS, |
| 78 | "CARGO_PKG_AUTHORS" , |
| 79 | "A colon-separated list of authors." |
| 80 | ), |
| 81 | (PKG_NAME, "CARGO_PKG_NAME" , "The name of the package." ), |
| 82 | (PKG_DESCRIPTION, "CARGO_PKG_DESCRIPTION" , "The description." ), |
| 83 | (PKG_HOMEPAGE, "CARGO_PKG_HOMEPAGE" , "The homepage." ), |
| 84 | (PKG_LICENSE, "CARGO_PKG_LICENSE" , "The license." ), |
| 85 | ( |
| 86 | PKG_REPOSITORY, |
| 87 | "CARGO_PKG_REPOSITORY" , |
| 88 | "The source repository as advertised in Cargo.toml." |
| 89 | ), |
| 90 | ( |
| 91 | TARGET, |
| 92 | "TARGET" , |
| 93 | "The target triple that was being compiled for." |
| 94 | ), |
| 95 | (HOST, "HOST" , "The host triple of the rust compiler." ), |
| 96 | ( |
| 97 | PROFILE, |
| 98 | "PROFILE" , |
| 99 | "`release` for release builds, `debug` for other builds." |
| 100 | ), |
| 101 | (RUSTC, "RUSTC" , "The compiler that cargo resolved to use." ), |
| 102 | ( |
| 103 | RUSTDOC, |
| 104 | "RUSTDOC" , |
| 105 | "The documentation generator that cargo resolved to use." |
| 106 | ) |
| 107 | ); |
| 108 | write_str_variable!( |
| 109 | w, |
| 110 | "OPT_LEVEL" , |
| 111 | env::var("OPT_LEVEL" ).unwrap(), |
| 112 | "Value of OPT_LEVEL for the profile used during compilation." |
| 113 | ); |
| 114 | write_variable!( |
| 115 | w, |
| 116 | "NUM_JOBS" , |
| 117 | "u32" , |
| 118 | if env::var(crate::SOURCE_DATE_EPOCH).is_ok() { |
| 119 | borrow::Cow::Borrowed("1" ) |
| 120 | } else { |
| 121 | borrow::Cow::Owned(env::var("NUM_JOBS" ).unwrap()) |
| 122 | }, |
| 123 | "The parallelism that was specified during compilation." |
| 124 | ); |
| 125 | write_variable!( |
| 126 | w, |
| 127 | "DEBUG" , |
| 128 | "bool" , |
| 129 | env::var("DEBUG" ).unwrap() == "true" , |
| 130 | "Value of DEBUG for the profile used during compilation." |
| 131 | ); |
| 132 | Ok(()) |
| 133 | } |
| 134 | |
| 135 | pub fn write_features(&self, mut w: &fs::File) -> io::Result<()> { |
| 136 | use io::Write; |
| 137 | |
| 138 | let mut features = Vec::new(); |
| 139 | for name in self.0.keys() { |
| 140 | if let Some(feat) = name.strip_prefix("CARGO_FEATURE_" ) { |
| 141 | features.push(feat.to_owned()); |
| 142 | } |
| 143 | } |
| 144 | features.sort_unstable(); |
| 145 | |
| 146 | write_variable!( |
| 147 | w, |
| 148 | "FEATURES" , |
| 149 | format_args!("[&str; {}]" , features.len()), |
| 150 | ArrayDisplay(&features, |t, f| write!(f, " \"{}\"" , t.escape_default())), |
| 151 | "The features that were enabled during compilation." |
| 152 | ); |
| 153 | let features_str = features.join(", " ); |
| 154 | write_str_variable!( |
| 155 | w, |
| 156 | "FEATURES_STR" , |
| 157 | features_str, |
| 158 | "The features as a comma-separated string." |
| 159 | ); |
| 160 | |
| 161 | let mut lowercase_features = features |
| 162 | .iter() |
| 163 | .map(|name| name.to_lowercase()) |
| 164 | .collect::<Vec<_>>(); |
| 165 | lowercase_features.sort_unstable(); |
| 166 | |
| 167 | write_variable!( |
| 168 | w, |
| 169 | "FEATURES_LOWERCASE" , |
| 170 | format_args!("[&str; {}]" , lowercase_features.len()), |
| 171 | ArrayDisplay(&lowercase_features, |val, fmt| write!( |
| 172 | fmt, |
| 173 | " \"{}\"" , |
| 174 | val.escape_default() |
| 175 | )), |
| 176 | "The features as above, as lowercase strings." |
| 177 | ); |
| 178 | let lowercase_features_str = lowercase_features.join(", " ); |
| 179 | write_str_variable!( |
| 180 | w, |
| 181 | "FEATURES_LOWERCASE_STR" , |
| 182 | lowercase_features_str, |
| 183 | "The feature-string as above, from lowercase strings." |
| 184 | ); |
| 185 | |
| 186 | Ok(()) |
| 187 | } |
| 188 | |
| 189 | pub fn write_cfg(&self, mut w: &fs::File) -> io::Result<()> { |
| 190 | use io::Write; |
| 191 | |
| 192 | write_str_variable!( |
| 193 | w, |
| 194 | "CFG_TARGET_ARCH" , |
| 195 | self.0["CARGO_CFG_TARGET_ARCH" ], |
| 196 | "The target architecture, given by `CARGO_CFG_TARGET_ARCH`." |
| 197 | ); |
| 198 | |
| 199 | write_str_variable!( |
| 200 | w, |
| 201 | "CFG_ENDIAN" , |
| 202 | self.0["CARGO_CFG_TARGET_ENDIAN" ], |
| 203 | "The endianness, given by `CARGO_CFG_TARGET_ENDIAN`." |
| 204 | ); |
| 205 | |
| 206 | write_str_variable!( |
| 207 | w, |
| 208 | "CFG_ENV" , |
| 209 | self.0["CARGO_CFG_TARGET_ENV" ], |
| 210 | "The toolchain-environment, given by `CARGO_CFG_TARGET_ENV`." |
| 211 | ); |
| 212 | |
| 213 | write_str_variable!( |
| 214 | w, |
| 215 | "CFG_FAMILY" , |
| 216 | self.0 |
| 217 | .get("CARGO_CFG_TARGET_FAMILY" ) |
| 218 | .map(|s| s.as_str()) |
| 219 | .unwrap_or_default(), |
| 220 | "The OS-family, given by `CARGO_CFG_TARGET_FAMILY`." |
| 221 | ); |
| 222 | |
| 223 | write_str_variable!( |
| 224 | w, |
| 225 | "CFG_OS" , |
| 226 | self.0["CARGO_CFG_TARGET_OS" ], |
| 227 | "The operating system, given by `CARGO_CFG_TARGET_OS`." |
| 228 | ); |
| 229 | |
| 230 | write_str_variable!( |
| 231 | w, |
| 232 | "CFG_POINTER_WIDTH" , |
| 233 | self.0["CARGO_CFG_TARGET_POINTER_WIDTH" ], |
| 234 | "The pointer width, given by `CARGO_CFG_TARGET_POINTER_WIDTH`." |
| 235 | ); |
| 236 | |
| 237 | Ok(()) |
| 238 | } |
| 239 | |
| 240 | pub fn write_compiler_version(&self, mut w: &fs::File) -> io::Result<()> { |
| 241 | use std::io::Write; |
| 242 | |
| 243 | let rustc = &self.0["RUSTC" ]; |
| 244 | let rustdoc = &self.0["RUSTDOC" ]; |
| 245 | |
| 246 | let rustc_version = get_version_from_cmd(rustc.as_ref())?; |
| 247 | let rustdoc_version = get_version_from_cmd(rustdoc.as_ref()).unwrap_or_default(); |
| 248 | |
| 249 | write_str_variable!( |
| 250 | w, |
| 251 | "RUSTC_VERSION" , |
| 252 | rustc_version, |
| 253 | format_args!("The output of ` {rustc} -V`" ) |
| 254 | ); |
| 255 | |
| 256 | write_str_variable!( |
| 257 | w, |
| 258 | "RUSTDOC_VERSION" , |
| 259 | rustdoc_version, |
| 260 | format_args!( |
| 261 | "The output of ` {rustdoc} -V`; empty string if ` {rustdoc} -V` failed to execute" |
| 262 | ) |
| 263 | ); |
| 264 | Ok(()) |
| 265 | } |
| 266 | |
| 267 | pub fn detect_ci(&self) -> Option<CIPlatform> { |
| 268 | macro_rules! detect { |
| 269 | ($(($k:expr, $v:expr, $i:ident)),*) => {$( |
| 270 | if self.0.get($k).map_or(false, |v| v == $v) { |
| 271 | return Some(CIPlatform::$i); |
| 272 | } |
| 273 | )*}; |
| 274 | ($(($k:expr, $i:ident)),*) => {$( |
| 275 | if self.0.contains_key($k) { |
| 276 | return Some(CIPlatform::$i); |
| 277 | } |
| 278 | )*}; |
| 279 | ($($k:expr),*) => {$( |
| 280 | if self.0.contains_key($k) { |
| 281 | return Some(CIPlatform::Generic); |
| 282 | } |
| 283 | )*}; |
| 284 | } |
| 285 | // Variable names collected by watson/ci-info |
| 286 | detect!( |
| 287 | ("TRAVIS" , Travis), |
| 288 | ("CIRCLECI" , Circle), |
| 289 | ("GITLAB_CI" , GitLab), |
| 290 | ("APPVEYOR" , AppVeyor), |
| 291 | ("DRONE" , Drone), |
| 292 | ("MAGNUM" , Magnum), |
| 293 | ("SEMAPHORE" , Semaphore), |
| 294 | ("JENKINS_URL" , Jenkins), |
| 295 | ("bamboo_planKey" , Bamboo), |
| 296 | ("TF_BUILD" , TFS), |
| 297 | ("TEAMCITY_VERSION" , TeamCity), |
| 298 | ("BUILDKITE" , Buildkite), |
| 299 | ("HUDSON_URL" , Hudson), |
| 300 | ("GO_PIPELINE_LABEL" , GoCD), |
| 301 | ("BITBUCKET_COMMIT" , BitBucket), |
| 302 | ("GITHUB_ACTIONS" , GitHubActions) |
| 303 | ); |
| 304 | |
| 305 | if self.0.contains_key("TASK_ID" ) && self.0.contains_key("RUN_ID" ) { |
| 306 | return Some(CIPlatform::TaskCluster); |
| 307 | } |
| 308 | |
| 309 | detect!(("CI_NAME" , "codeship" , Codeship)); |
| 310 | |
| 311 | detect!( |
| 312 | "CI" , // Could be Travis, Circle, GitLab, AppVeyor or CodeShip |
| 313 | "CONTINUOUS_INTEGRATION" , // Probably Travis |
| 314 | "BUILD_NUMBER" // Jenkins, TeamCity |
| 315 | ); |
| 316 | None |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | /// Various Continuous Integration platforms whose presence can be detected. |
| 321 | pub enum CIPlatform { |
| 322 | /// <https://travis-ci.org> |
| 323 | Travis, |
| 324 | /// <https://circleci.com> |
| 325 | Circle, |
| 326 | /// <https://about.gitlab.com/gitlab-ci> |
| 327 | GitLab, |
| 328 | /// <https://www.appveyor.com> |
| 329 | AppVeyor, |
| 330 | /// <https://codeship.com> |
| 331 | Codeship, |
| 332 | /// <https://github.com/drone/drone> |
| 333 | Drone, |
| 334 | /// <https://magnum-ci.com> |
| 335 | Magnum, |
| 336 | /// <https://semaphoreci.com> |
| 337 | Semaphore, |
| 338 | /// <https://jenkins.io> |
| 339 | Jenkins, |
| 340 | /// <https://www.atlassian.com/software/bamboo> |
| 341 | Bamboo, |
| 342 | /// <https://www.visualstudio.com/de/tfs> |
| 343 | TFS, |
| 344 | /// <https://www.jetbrains.com/teamcity> |
| 345 | TeamCity, |
| 346 | /// <https://buildkite.com> |
| 347 | Buildkite, |
| 348 | /// <http://hudson-ci.org> |
| 349 | Hudson, |
| 350 | /// <https://github.com/taskcluster> |
| 351 | TaskCluster, |
| 352 | /// <https://www.gocd.io> |
| 353 | GoCD, |
| 354 | /// <https://bitbucket.org> |
| 355 | BitBucket, |
| 356 | /// <https://github.com/features/actions> |
| 357 | GitHubActions, |
| 358 | /// Unspecific |
| 359 | Generic, |
| 360 | } |
| 361 | |
| 362 | impl fmt::Display for CIPlatform { |
| 363 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 364 | f.write_str(match *self { |
| 365 | CIPlatform::Travis => "Travis CI" , |
| 366 | CIPlatform::Circle => "CircleCI" , |
| 367 | CIPlatform::GitLab => "GitLab" , |
| 368 | CIPlatform::AppVeyor => "AppVeyor" , |
| 369 | CIPlatform::Codeship => "CodeShip" , |
| 370 | CIPlatform::Drone => "Drone" , |
| 371 | CIPlatform::Magnum => "Magnum" , |
| 372 | CIPlatform::Semaphore => "Semaphore" , |
| 373 | CIPlatform::Jenkins => "Jenkins" , |
| 374 | CIPlatform::Bamboo => "Bamboo" , |
| 375 | CIPlatform::TFS => "Team Foundation Server" , |
| 376 | CIPlatform::TeamCity => "TeamCity" , |
| 377 | CIPlatform::Buildkite => "Buildkite" , |
| 378 | CIPlatform::Hudson => "Hudson" , |
| 379 | CIPlatform::TaskCluster => "TaskCluster" , |
| 380 | CIPlatform::GoCD => "GoCD" , |
| 381 | CIPlatform::BitBucket => "BitBucket" , |
| 382 | CIPlatform::GitHubActions => "GitHub Actions" , |
| 383 | CIPlatform::Generic => "Generic CI" , |
| 384 | }) |
| 385 | } |
| 386 | } |
| 387 | |