|
| 1 | +use std::env; |
| 2 | +use std::path::Path; |
| 3 | +use std::str::{self, FromStr}; |
| 4 | + |
| 5 | +use core::profiles::Profiles; |
| 6 | +use core::{Dependency, Workspace}; |
| 7 | +use core::{Package, PackageId, PackageSet, Resolve}; |
| 8 | +use util::errors::CargoResult; |
| 9 | +use util::{profile, Cfg, CfgExpr, Config}; |
| 10 | + |
| 11 | +use super::{BuildConfig, Kind, TargetConfig, Unit}; |
| 12 | + |
| 13 | +mod target_info; |
| 14 | +pub use self::target_info::{FileFlavor, TargetInfo}; |
| 15 | + |
| 16 | +/// The build context, containing all information about a build task |
| 17 | +pub struct BuildContext<'a, 'cfg: 'a> { |
| 18 | + /// The workspace the build is for |
| 19 | + pub ws: &'a Workspace<'cfg>, |
| 20 | + /// The cargo configuration |
| 21 | + pub config: &'cfg Config, |
| 22 | + /// The dependency graph for our build |
| 23 | + pub resolve: &'a Resolve, |
| 24 | + pub profiles: &'a Profiles, |
| 25 | + pub build_config: &'a BuildConfig, |
| 26 | + /// This is a workaround to carry the extra compiler args for either |
| 27 | + /// `rustc` or `rustdoc` given on the command-line for the commands `cargo |
| 28 | + /// rustc` and `cargo rustdoc`. These commands only support one target, |
| 29 | + /// but we don't want the args passed to any dependencies, so we include |
| 30 | + /// the `Unit` corresponding to the top-level target. |
| 31 | + pub extra_compiler_args: Option<(Unit<'a>, Vec<String>)>, |
| 32 | + pub packages: &'a PackageSet<'cfg>, |
| 33 | + |
| 34 | + pub target_info: TargetInfo, |
| 35 | + pub host_info: TargetInfo, |
| 36 | + pub incremental_env: Option<bool>, |
| 37 | +} |
| 38 | + |
| 39 | +impl<'a, 'cfg> BuildContext<'a, 'cfg> { |
| 40 | + pub fn new( |
| 41 | + ws: &'a Workspace<'cfg>, |
| 42 | + resolve: &'a Resolve, |
| 43 | + packages: &'a PackageSet<'cfg>, |
| 44 | + config: &'cfg Config, |
| 45 | + build_config: &'a BuildConfig, |
| 46 | + profiles: &'a Profiles, |
| 47 | + extra_compiler_args: Option<(Unit<'a>, Vec<String>)>, |
| 48 | + ) -> CargoResult<BuildContext<'a, 'cfg>> { |
| 49 | + let incremental_env = match env::var("CARGO_INCREMENTAL") { |
| 50 | + Ok(v) => Some(v == "1"), |
| 51 | + Err(_) => None, |
| 52 | + }; |
| 53 | + |
| 54 | + let (host_info, target_info) = { |
| 55 | + let _p = profile::start("BuildContext::probe_target_info"); |
| 56 | + debug!("probe_target_info"); |
| 57 | + let host_info = TargetInfo::new(config, &build_config, Kind::Host)?; |
| 58 | + let target_info = TargetInfo::new(config, &build_config, Kind::Target)?; |
| 59 | + (host_info, target_info) |
| 60 | + }; |
| 61 | + |
| 62 | + Ok(BuildContext { |
| 63 | + ws, |
| 64 | + resolve, |
| 65 | + packages, |
| 66 | + config, |
| 67 | + target_info, |
| 68 | + host_info, |
| 69 | + build_config, |
| 70 | + profiles, |
| 71 | + incremental_env, |
| 72 | + extra_compiler_args, |
| 73 | + }) |
| 74 | + } |
| 75 | + |
| 76 | + pub fn extern_crate_name(&self, unit: &Unit<'a>, dep: &Unit<'a>) -> CargoResult<String> { |
| 77 | + let deps = { |
| 78 | + let a = unit.pkg.package_id(); |
| 79 | + let b = dep.pkg.package_id(); |
| 80 | + if a == b { |
| 81 | + &[] |
| 82 | + } else { |
| 83 | + self.resolve.dependencies_listed(a, b) |
| 84 | + } |
| 85 | + }; |
| 86 | + |
| 87 | + let crate_name = dep.target.crate_name(); |
| 88 | + let mut names = deps.iter() |
| 89 | + .map(|d| d.rename().unwrap_or(&crate_name)); |
| 90 | + let name = names.next().unwrap_or(&crate_name); |
| 91 | + for n in names { |
| 92 | + if n == name { |
| 93 | + continue |
| 94 | + } |
| 95 | + bail!("multiple dependencies listed for the same crate must \ |
| 96 | + all have the same name, but the dependency on `{}` \ |
| 97 | + is listed as having different names", dep.pkg.package_id()); |
| 98 | + } |
| 99 | + Ok(name.to_string()) |
| 100 | + } |
| 101 | + |
| 102 | + /// Whether a dependency should be compiled for the host or target platform, |
| 103 | + /// specified by `Kind`. |
| 104 | + pub fn dep_platform_activated(&self, dep: &Dependency, kind: Kind) -> bool { |
| 105 | + // If this dependency is only available for certain platforms, |
| 106 | + // make sure we're only enabling it for that platform. |
| 107 | + let platform = match dep.platform() { |
| 108 | + Some(p) => p, |
| 109 | + None => return true, |
| 110 | + }; |
| 111 | + let (name, info) = match kind { |
| 112 | + Kind::Host => (self.build_config.host_triple(), &self.host_info), |
| 113 | + Kind::Target => (self.build_config.target_triple(), &self.target_info), |
| 114 | + }; |
| 115 | + platform.matches(name, info.cfg()) |
| 116 | + } |
| 117 | + |
| 118 | + /// Gets a package for the given package id. |
| 119 | + pub fn get_package(&self, id: &PackageId) -> CargoResult<&'a Package> { |
| 120 | + self.packages.get(id) |
| 121 | + } |
| 122 | + |
| 123 | + /// Get the user-specified linker for a particular host or target |
| 124 | + pub fn linker(&self, kind: Kind) -> Option<&Path> { |
| 125 | + self.target_config(kind).linker.as_ref().map(|s| s.as_ref()) |
| 126 | + } |
| 127 | + |
| 128 | + /// Get the user-specified `ar` program for a particular host or target |
| 129 | + pub fn ar(&self, kind: Kind) -> Option<&Path> { |
| 130 | + self.target_config(kind).ar.as_ref().map(|s| s.as_ref()) |
| 131 | + } |
| 132 | + |
| 133 | + /// Get the list of cfg printed out from the compiler for the specified kind |
| 134 | + pub fn cfg(&self, kind: Kind) -> &[Cfg] { |
| 135 | + let info = match kind { |
| 136 | + Kind::Host => &self.host_info, |
| 137 | + Kind::Target => &self.target_info, |
| 138 | + }; |
| 139 | + info.cfg().unwrap_or(&[]) |
| 140 | + } |
| 141 | + |
| 142 | + /// Get the target configuration for a particular host or target |
| 143 | + fn target_config(&self, kind: Kind) -> &TargetConfig { |
| 144 | + match kind { |
| 145 | + Kind::Host => &self.build_config.host, |
| 146 | + Kind::Target => &self.build_config.target, |
| 147 | + } |
| 148 | + } |
| 149 | + |
| 150 | + /// Number of jobs specified for this build |
| 151 | + pub fn jobs(&self) -> u32 { |
| 152 | + self.build_config.jobs |
| 153 | + } |
| 154 | + |
| 155 | + pub fn rustflags_args(&self, unit: &Unit) -> CargoResult<Vec<String>> { |
| 156 | + env_args( |
| 157 | + self.config, |
| 158 | + &self.build_config, |
| 159 | + self.info(&unit.kind).cfg(), |
| 160 | + unit.kind, |
| 161 | + "RUSTFLAGS", |
| 162 | + ) |
| 163 | + } |
| 164 | + |
| 165 | + pub fn rustdocflags_args(&self, unit: &Unit) -> CargoResult<Vec<String>> { |
| 166 | + env_args( |
| 167 | + self.config, |
| 168 | + &self.build_config, |
| 169 | + self.info(&unit.kind).cfg(), |
| 170 | + unit.kind, |
| 171 | + "RUSTDOCFLAGS", |
| 172 | + ) |
| 173 | + } |
| 174 | + |
| 175 | + pub fn show_warnings(&self, pkg: &PackageId) -> bool { |
| 176 | + pkg.source_id().is_path() || self.config.extra_verbose() |
| 177 | + } |
| 178 | + |
| 179 | + fn info(&self, kind: &Kind) -> &TargetInfo { |
| 180 | + match *kind { |
| 181 | + Kind::Host => &self.host_info, |
| 182 | + Kind::Target => &self.target_info, |
| 183 | + } |
| 184 | + } |
| 185 | + |
| 186 | + pub fn extra_args_for(&self, unit: &Unit<'a>) -> Option<&Vec<String>> { |
| 187 | + if let Some((ref args_unit, ref args)) = self.extra_compiler_args { |
| 188 | + if args_unit == unit { |
| 189 | + return Some(args); |
| 190 | + } |
| 191 | + } |
| 192 | + None |
| 193 | + } |
| 194 | +} |
| 195 | + |
| 196 | +/// Acquire extra flags to pass to the compiler from various locations. |
| 197 | +/// |
| 198 | +/// The locations are: |
| 199 | +/// |
| 200 | +/// - the `RUSTFLAGS` environment variable |
| 201 | +/// |
| 202 | +/// then if this was not found |
| 203 | +/// |
| 204 | +/// - `target.*.rustflags` from the manifest (Cargo.toml) |
| 205 | +/// - `target.cfg(..).rustflags` from the manifest |
| 206 | +/// |
| 207 | +/// then if neither of these were found |
| 208 | +/// |
| 209 | +/// - `build.rustflags` from the manifest |
| 210 | +/// |
| 211 | +/// Note that if a `target` is specified, no args will be passed to host code (plugins, build |
| 212 | +/// scripts, ...), even if it is the same as the target. |
| 213 | +fn env_args( |
| 214 | + config: &Config, |
| 215 | + build_config: &BuildConfig, |
| 216 | + target_cfg: Option<&[Cfg]>, |
| 217 | + kind: Kind, |
| 218 | + name: &str, |
| 219 | +) -> CargoResult<Vec<String>> { |
| 220 | + // We *want* to apply RUSTFLAGS only to builds for the |
| 221 | + // requested target architecture, and not to things like build |
| 222 | + // scripts and plugins, which may be for an entirely different |
| 223 | + // architecture. Cargo's present architecture makes it quite |
| 224 | + // hard to only apply flags to things that are not build |
| 225 | + // scripts and plugins though, so we do something more hacky |
| 226 | + // instead to avoid applying the same RUSTFLAGS to multiple targets |
| 227 | + // arches: |
| 228 | + // |
| 229 | + // 1) If --target is not specified we just apply RUSTFLAGS to |
| 230 | + // all builds; they are all going to have the same target. |
| 231 | + // |
| 232 | + // 2) If --target *is* specified then we only apply RUSTFLAGS |
| 233 | + // to compilation units with the Target kind, which indicates |
| 234 | + // it was chosen by the --target flag. |
| 235 | + // |
| 236 | + // This means that, e.g. even if the specified --target is the |
| 237 | + // same as the host, build scripts in plugins won't get |
| 238 | + // RUSTFLAGS. |
| 239 | + let compiling_with_target = build_config.requested_target.is_some(); |
| 240 | + let is_target_kind = kind == Kind::Target; |
| 241 | + |
| 242 | + if compiling_with_target && !is_target_kind { |
| 243 | + // This is probably a build script or plugin and we're |
| 244 | + // compiling with --target. In this scenario there are |
| 245 | + // no rustflags we can apply. |
| 246 | + return Ok(Vec::new()); |
| 247 | + } |
| 248 | + |
| 249 | + // First try RUSTFLAGS from the environment |
| 250 | + if let Ok(a) = env::var(name) { |
| 251 | + let args = a.split(' ') |
| 252 | + .map(str::trim) |
| 253 | + .filter(|s| !s.is_empty()) |
| 254 | + .map(str::to_string); |
| 255 | + return Ok(args.collect()); |
| 256 | + } |
| 257 | + |
| 258 | + let mut rustflags = Vec::new(); |
| 259 | + |
| 260 | + let name = name.chars() |
| 261 | + .flat_map(|c| c.to_lowercase()) |
| 262 | + .collect::<String>(); |
| 263 | + // Then the target.*.rustflags value... |
| 264 | + let target = build_config |
| 265 | + .requested_target |
| 266 | + .as_ref() |
| 267 | + .map(|s| s.as_str()) |
| 268 | + .unwrap_or(build_config.host_triple()); |
| 269 | + let key = format!("target.{}.{}", target, name); |
| 270 | + if let Some(args) = config.get_list_or_split_string(&key)? { |
| 271 | + let args = args.val.into_iter(); |
| 272 | + rustflags.extend(args); |
| 273 | + } |
| 274 | + // ...including target.'cfg(...)'.rustflags |
| 275 | + if let Some(target_cfg) = target_cfg { |
| 276 | + if let Some(table) = config.get_table("target")? { |
| 277 | + let cfgs = table.val.keys().filter_map(|t| { |
| 278 | + if t.starts_with("cfg(") && t.ends_with(')') { |
| 279 | + let cfg = &t[4..t.len() - 1]; |
| 280 | + CfgExpr::from_str(cfg).ok().and_then(|c| { |
| 281 | + if c.matches(target_cfg) { |
| 282 | + Some(t) |
| 283 | + } else { |
| 284 | + None |
| 285 | + } |
| 286 | + }) |
| 287 | + } else { |
| 288 | + None |
| 289 | + } |
| 290 | + }); |
| 291 | + |
| 292 | + // Note that we may have multiple matching `[target]` sections and |
| 293 | + // because we're passing flags to the compiler this can affect |
| 294 | + // cargo's caching and whether it rebuilds. Ensure a deterministic |
| 295 | + // ordering through sorting for now. We may perhaps one day wish to |
| 296 | + // ensure a deterministic ordering via the order keys were defined |
| 297 | + // in files perhaps. |
| 298 | + let mut cfgs = cfgs.collect::<Vec<_>>(); |
| 299 | + cfgs.sort(); |
| 300 | + |
| 301 | + for n in cfgs { |
| 302 | + let key = format!("target.{}.{}", n, name); |
| 303 | + if let Some(args) = config.get_list_or_split_string(&key)? { |
| 304 | + let args = args.val.into_iter(); |
| 305 | + rustflags.extend(args); |
| 306 | + } |
| 307 | + } |
| 308 | + } |
| 309 | + } |
| 310 | + |
| 311 | + if !rustflags.is_empty() { |
| 312 | + return Ok(rustflags); |
| 313 | + } |
| 314 | + |
| 315 | + // Then the build.rustflags value |
| 316 | + let key = format!("build.{}", name); |
| 317 | + if let Some(args) = config.get_list_or_split_string(&key)? { |
| 318 | + let args = args.val.into_iter(); |
| 319 | + return Ok(args.collect()); |
| 320 | + } |
| 321 | + |
| 322 | + Ok(Vec::new()) |
| 323 | +} |
0 commit comments