]>
Commit | Line | Data |
---|---|---|
0cc39aae AC |
1 | #![allow(deprecated)] |
2 | ||
3f7b09cc | 3 | use std::collections::{HashSet, HashMap, BTreeSet}; |
4cfaf9a5 | 4 | use std::collections::hash_map::Entry; |
e17933ec | 5 | use std::env; |
0cc39aae AC |
6 | use std::fmt; |
7 | use std::hash::{Hasher, Hash, SipHasher}; | |
5a04a4d4 | 8 | use std::path::{Path, PathBuf}; |
f5d786e0 | 9 | use std::str::{self, FromStr}; |
800d9eb8 | 10 | use std::sync::Arc; |
4cfaf9a5 | 11 | use std::cell::RefCell; |
1e1412fd | 12 | |
cbf25a9b AC |
13 | use jobserver::Client; |
14 | ||
2d25fcac | 15 | use core::{Package, PackageId, PackageSet, Resolve, Target, Profile}; |
0cc39aae | 16 | use core::{TargetKind, Profiles, Dependency, Workspace}; |
b43d2869 | 17 | use core::dependency::Kind as DepKind; |
4cfaf9a5 | 18 | use util::{self, ProcessBuilder, internal, Config, profile, Cfg, CfgExpr}; |
e95044e3 | 19 | use util::errors::{CargoResult, CargoResultExt}; |
1e1412fd | 20 | |
ac4eddbb | 21 | use super::TargetConfig; |
fe8bbb7a | 22 | use super::custom_build::{BuildState, BuildScripts, BuildDeps}; |
a40d3b03 | 23 | use super::fingerprint::Fingerprint; |
41579bad | 24 | use super::layout::Layout; |
5ea7a97d | 25 | use super::links::Links; |
a40d3b03 | 26 | use super::{Kind, Compilation, BuildConfig}; |
9f5d9b81 | 27 | |
d2ca374c | 28 | #[derive(Clone, Copy, Eq, PartialEq, Hash)] |
659f8244 AC |
29 | pub struct Unit<'a> { |
30 | pub pkg: &'a Package, | |
31 | pub target: &'a Target, | |
32 | pub profile: &'a Profile, | |
33 | pub kind: Kind, | |
ff19a482 AC |
34 | } |
35 | ||
8a8ea1e8 | 36 | pub struct Context<'a, 'cfg: 'a> { |
0ae3a2b4 | 37 | pub ws: &'a Workspace<'cfg>, |
8a8ea1e8 | 38 | pub config: &'cfg Config, |
f6d22b64 | 39 | pub resolve: &'a Resolve, |
8a8ea1e8 | 40 | pub compilation: Compilation<'cfg>, |
2d25fcac | 41 | pub packages: &'a PackageSet<'cfg>, |
800d9eb8 | 42 | pub build_state: Arc<BuildState>, |
f90d21f6 | 43 | pub build_script_overridden: HashSet<(PackageId, Kind)>, |
fe8bbb7a | 44 | pub build_explicit_deps: HashMap<Unit<'a>, BuildDeps>, |
c447e9d0 | 45 | pub fingerprints: HashMap<Unit<'a>, Arc<Fingerprint>>, |
659f8244 | 46 | pub compiled: HashSet<Unit<'a>>, |
9e779198 | 47 | pub build_config: BuildConfig, |
659f8244 | 48 | pub build_scripts: HashMap<Unit<'a>, Arc<BuildScripts>>, |
5ea7a97d | 49 | pub links: Links<'a>, |
e9c6b0a0 | 50 | pub used_in_plugin: HashSet<Unit<'a>>, |
cbf25a9b | 51 | pub jobserver: Client, |
1e1412fd | 52 | |
9f5d9b81 AC |
53 | host: Layout, |
54 | target: Option<Layout>, | |
f5d786e0 AC |
55 | target_info: TargetInfo, |
56 | host_info: TargetInfo, | |
9e779198 | 57 | profiles: &'a Profiles, |
a5bf3b88 | 58 | incremental_enabled: bool, |
67b3b444 | 59 | |
e6649dfa | 60 | target_filenames: HashMap<Unit<'a>, Arc<Vec<(PathBuf, Option<PathBuf>, bool)>>>, |
67b3b444 | 61 | target_metadatas: HashMap<Unit<'a>, Option<Metadata>>, |
1e1412fd AC |
62 | } |
63 | ||
3f7b09cc | 64 | #[derive(Clone, Default)] |
f5d786e0 | 65 | struct TargetInfo { |
4cfaf9a5 MS |
66 | crate_type_process: Option<ProcessBuilder>, |
67 | crate_types: RefCell<HashMap<String, Option<(String, String)>>>, | |
f5d786e0 AC |
68 | cfg: Option<Vec<Cfg>>, |
69 | } | |
70 | ||
4cfaf9a5 MS |
71 | impl TargetInfo { |
72 | fn discover_crate_type(&self, crate_type: &str) -> CargoResult<Option<(String, String)>> { | |
73 | let mut process = self.crate_type_process.clone().unwrap(); | |
74 | ||
75 | process.arg("--crate-type").arg(crate_type); | |
76 | ||
77 | let output = process.exec_with_output().chain_err(|| { | |
78 | format!("failed to run `rustc` to learn about \ | |
79 | crate-type {} information", crate_type) | |
80 | })?; | |
81 | ||
82 | let error = str::from_utf8(&output.stderr).unwrap(); | |
83 | let output = str::from_utf8(&output.stdout).unwrap(); | |
84 | Ok(parse_crate_type(crate_type, error, &mut output.lines())?) | |
85 | } | |
86 | } | |
87 | ||
46a5fe9a | 88 | #[derive(Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] |
0cc39aae AC |
89 | pub struct Metadata(u64); |
90 | ||
8a8ea1e8 | 91 | impl<'a, 'cfg> Context<'a, 'cfg> { |
0ae3a2b4 | 92 | pub fn new(ws: &'a Workspace<'cfg>, |
77720032 | 93 | resolve: &'a Resolve, |
2d25fcac | 94 | packages: &'a PackageSet<'cfg>, |
8a8ea1e8 | 95 | config: &'cfg Config, |
9e779198 | 96 | build_config: BuildConfig, |
8a8ea1e8 | 97 | profiles: &'a Profiles) -> CargoResult<Context<'a, 'cfg>> { |
77720032 AK |
98 | |
99 | let dest = if build_config.release { "release" } else { "debug" }; | |
23591fe5 | 100 | let host_layout = Layout::new(ws, None, dest)?; |
77720032 | 101 | let target_layout = match build_config.requested_target.as_ref() { |
23591fe5 | 102 | Some(target) => Some(Layout::new(ws, Some(target), dest)?), |
77720032 AK |
103 | None => None, |
104 | }; | |
105 | ||
a5bf3b88 NM |
106 | // Enable incremental builds if the user opts in. For now, |
107 | // this is an environment variable until things stabilize a | |
108 | // bit more. | |
74cb8636 MS |
109 | let incremental_enabled = match env::var("CARGO_INCREMENTAL") { |
110 | Ok(v) => v == "1", | |
111 | Err(_) => false, | |
112 | }; | |
a5bf3b88 | 113 | |
357bb2de JG |
114 | // -Z can only be used on nightly builds; other builds complain loudly. |
115 | // Since incremental builds only work on nightly anyway, we silently | |
116 | // ignore CARGO_INCREMENTAL on anything but nightly. This allows users | |
117 | // to always have CARGO_INCREMENTAL set without getting unexpected | |
118 | // errors on stable/beta builds. | |
213a2bf0 JG |
119 | let is_nightly = |
120 | config.rustc()?.verbose_version.contains("-nightly") || | |
121 | config.rustc()?.verbose_version.contains("-dev"); | |
122 | let incremental_enabled = incremental_enabled && is_nightly; | |
357bb2de | 123 | |
cbf25a9b AC |
124 | // Load up the jobserver that we'll use to manage our parallelism. This |
125 | // is the same as the GNU make implementation of a jobserver, and | |
126 | // intentionally so! It's hoped that we can interact with GNU make and | |
127 | // all share the same jobserver. | |
128 | // | |
129 | // Note that if we don't have a jobserver in our environment then we | |
130 | // create our own, and we create it with `n-1` tokens because one token | |
131 | // is ourself, a running process. | |
132 | let jobserver = match config.jobserver_from_env() { | |
133 | Some(c) => c.clone(), | |
134 | None => Client::new(build_config.jobs as usize - 1).chain_err(|| { | |
135 | "failed to create jobserver" | |
136 | })?, | |
137 | }; | |
138 | ||
1e1412fd | 139 | Ok(Context { |
0ae3a2b4 | 140 | ws: ws, |
77720032 | 141 | host: host_layout, |
5d0cb3f2 | 142 | target: target_layout, |
1e1412fd | 143 | resolve: resolve, |
2d25fcac | 144 | packages: packages, |
1e1412fd | 145 | config: config, |
3f7b09cc AC |
146 | target_info: TargetInfo::default(), |
147 | host_info: TargetInfo::default(), | |
b3ade7c7 | 148 | compilation: Compilation::new(config), |
919cddeb | 149 | build_state: Arc::new(BuildState::new(&build_config)), |
ac4eddbb | 150 | build_config: build_config, |
a40d3b03 | 151 | fingerprints: HashMap::new(), |
9e779198 AC |
152 | profiles: profiles, |
153 | compiled: HashSet::new(), | |
5a953eb0 | 154 | build_scripts: HashMap::new(), |
7c97c5bf | 155 | build_explicit_deps: HashMap::new(), |
5ea7a97d | 156 | links: Links::new(), |
e9c6b0a0 | 157 | used_in_plugin: HashSet::new(), |
a5bf3b88 | 158 | incremental_enabled: incremental_enabled, |
cbf25a9b | 159 | jobserver: jobserver, |
f90d21f6 | 160 | build_script_overridden: HashSet::new(), |
67b3b444 NK |
161 | |
162 | // TODO: Pre-Calculate these with a topo-sort, rather than lazy-calculating | |
e6649dfa | 163 | target_filenames: HashMap::new(), |
67b3b444 | 164 | target_metadatas: HashMap::new(), |
1e1412fd AC |
165 | }) |
166 | } | |
167 | ||
3f7b09cc AC |
168 | /// Prepare this context, ensuring that all filesystem directories are in |
169 | /// place. | |
0863469c | 170 | pub fn prepare(&mut self) -> CargoResult<()> { |
3f7b09cc AC |
171 | let _p = profile::start("preparing layout"); |
172 | ||
e95044e3 | 173 | self.host.prepare().chain_err(|| { |
23591fe5 | 174 | internal("couldn't prepare build directories") |
82655b46 | 175 | })?; |
23591fe5 LL |
176 | if let Some(ref mut target) = self.target { |
177 | target.prepare().chain_err(|| { | |
178 | internal("couldn't prepare build directories") | |
179 | })?; | |
3f7b09cc AC |
180 | } |
181 | ||
3b5ec888 | 182 | self.compilation.host_deps_output = self.host.deps().to_path_buf(); |
7165bd58 | 183 | |
0863469c AC |
184 | let layout = self.target.as_ref().unwrap_or(&self.host); |
185 | self.compilation.root_output = layout.dest().to_path_buf(); | |
186 | self.compilation.deps_output = layout.deps().to_path_buf(); | |
3f7b09cc AC |
187 | Ok(()) |
188 | } | |
189 | ||
190 | /// Ensure that we've collected all target-specific information to compile | |
191 | /// all the units mentioned in `units`. | |
192 | pub fn probe_target_info(&mut self, units: &[Unit<'a>]) -> CargoResult<()> { | |
193 | let mut crate_types = BTreeSet::new(); | |
6e53b55d | 194 | let mut visited_units = HashSet::new(); |
3f7b09cc AC |
195 | // pre-fill with `bin` for learning about tests (nothing may be |
196 | // explicitly `bin`) as well as `rlib` as it's the coalesced version of | |
197 | // `lib` in the compiler and we're not sure which we'll see. | |
198 | crate_types.insert("bin".to_string()); | |
199 | crate_types.insert("rlib".to_string()); | |
200 | for unit in units { | |
6e53b55d | 201 | self.visit_crate_type(unit, &mut crate_types, &mut visited_units)?; |
3f7b09cc | 202 | } |
47b25fd3 | 203 | debug!("probe_target_info: crate_types={:?}", crate_types); |
82655b46 | 204 | self.probe_target_info_kind(&crate_types, Kind::Target)?; |
d469bbcb | 205 | if self.requested_target().is_none() { |
3f7b09cc AC |
206 | self.host_info = self.target_info.clone(); |
207 | } else { | |
82655b46 | 208 | self.probe_target_info_kind(&crate_types, Kind::Host)?; |
3f7b09cc AC |
209 | } |
210 | Ok(()) | |
211 | } | |
212 | ||
213 | fn visit_crate_type(&self, | |
214 | unit: &Unit<'a>, | |
6e53b55d GS |
215 | crate_types: &mut BTreeSet<String>, |
216 | visited_units: &mut HashSet<Unit<'a>>) | |
3f7b09cc | 217 | -> CargoResult<()> { |
6e53b55d GS |
218 | if !visited_units.insert(*unit) { |
219 | return Ok(()); | |
220 | } | |
3f7b09cc AC |
221 | for target in unit.pkg.manifest().targets() { |
222 | crate_types.extend(target.rustc_crate_types().iter().map(|s| { | |
223 | if *s == "lib" { | |
224 | "rlib".to_string() | |
225 | } else { | |
226 | s.to_string() | |
227 | } | |
228 | })); | |
229 | } | |
23591fe5 | 230 | for dep in self.dep_targets(unit)? { |
6e53b55d | 231 | self.visit_crate_type(&dep, crate_types, visited_units)?; |
3f7b09cc AC |
232 | } |
233 | Ok(()) | |
234 | } | |
235 | ||
236 | fn probe_target_info_kind(&mut self, | |
237 | crate_types: &BTreeSet<String>, | |
238 | kind: Kind) | |
239 | -> CargoResult<()> { | |
82655b46 | 240 | let rustflags = env_args(self.config, |
02625ba8 | 241 | &self.build_config, |
23591fe5 | 242 | self.info(&kind), |
02625ba8 NC |
243 | kind, |
244 | "RUSTFLAGS")?; | |
82655b46 | 245 | let mut process = self.config.rustc()?.process(); |
a6dad622 | 246 | process.arg("-") |
856641cc | 247 | .arg("--crate-name").arg("___") |
5955ea8a | 248 | .arg("--print=file-names") |
f639d381 | 249 | .args(&rustflags) |
5955ea8a | 250 | .env_remove("RUST_LOG"); |
3f7b09cc | 251 | |
3f7b09cc | 252 | if kind == Kind::Target { |
d469bbcb | 253 | process.arg("--target").arg(&self.target_triple()); |
3f7b09cc | 254 | } |
f5d786e0 | 255 | |
4cfaf9a5 MS |
256 | let crate_type_process = process.clone(); |
257 | ||
258 | for crate_type in crate_types { | |
259 | process.arg("--crate-type").arg(crate_type); | |
260 | } | |
261 | ||
f5d786e0 | 262 | let mut with_cfg = process.clone(); |
b1a96167 | 263 | with_cfg.arg("--print=sysroot"); |
f5d786e0 AC |
264 | with_cfg.arg("--print=cfg"); |
265 | ||
b1a96167 | 266 | let mut has_cfg_and_sysroot = true; |
82655b46 | 267 | let output = with_cfg.exec_with_output().or_else(|_| { |
b1a96167 | 268 | has_cfg_and_sysroot = false; |
f5d786e0 | 269 | process.exec_with_output() |
e95044e3 | 270 | }).chain_err(|| { |
23591fe5 | 271 | "failed to run `rustc` to learn about target-specific information" |
82655b46 | 272 | })?; |
1e1412fd | 273 | |
a6dad622 AC |
274 | let error = str::from_utf8(&output.stderr).unwrap(); |
275 | let output = str::from_utf8(&output.stdout).unwrap(); | |
a162c088 | 276 | let mut lines = output.lines(); |
3f7b09cc AC |
277 | let mut map = HashMap::new(); |
278 | for crate_type in crate_types { | |
4cfaf9a5 MS |
279 | let out = parse_crate_type(crate_type, error, &mut lines)?; |
280 | map.insert(crate_type.to_string(), out); | |
856641cc | 281 | } |
3cbf141b | 282 | |
b1a96167 JM |
283 | if has_cfg_and_sysroot { |
284 | let line = match lines.next() { | |
285 | Some(line) => line, | |
286 | None => bail!("output of --print=sysroot missing when learning about \ | |
287 | target-specific information from rustc"), | |
288 | }; | |
289 | let mut rustlib = PathBuf::from(line); | |
784d3154 JM |
290 | if kind == Kind::Host { |
291 | if cfg!(windows) { | |
292 | rustlib.push("bin"); | |
293 | } else { | |
294 | rustlib.push("lib"); | |
295 | } | |
296 | self.compilation.host_dylib_path = Some(rustlib); | |
297 | } else { | |
298 | rustlib.push("lib"); | |
299 | rustlib.push("rustlib"); | |
300 | rustlib.push(self.target_triple()); | |
301 | rustlib.push("lib"); | |
302 | self.compilation.target_dylib_path = Some(rustlib); | |
303 | } | |
b1a96167 JM |
304 | } |
305 | ||
306 | let cfg = if has_cfg_and_sysroot { | |
f5d786e0 AC |
307 | Some(try!(lines.map(Cfg::from_str).collect())) |
308 | } else { | |
309 | None | |
310 | }; | |
311 | ||
3f7b09cc AC |
312 | let info = match kind { |
313 | Kind::Target => &mut self.target_info, | |
314 | Kind::Host => &mut self.host_info, | |
315 | }; | |
4cfaf9a5 MS |
316 | info.crate_type_process = Some(crate_type_process); |
317 | info.crate_types = RefCell::new(map); | |
3f7b09cc | 318 | info.cfg = cfg; |
3a852a0f | 319 | Ok(()) |
1e1412fd AC |
320 | } |
321 | ||
e9c6b0a0 AC |
322 | /// Builds up the `used_in_plugin` internal to this context from the list of |
323 | /// top-level units. | |
324 | /// | |
325 | /// This will recursively walk `units` and all of their dependencies to | |
326 | /// determine which crate are going to be used in plugins or not. | |
327 | pub fn build_used_in_plugin_map(&mut self, units: &[Unit<'a>]) | |
328 | -> CargoResult<()> { | |
329 | let mut visited = HashSet::new(); | |
330 | for unit in units { | |
82655b46 | 331 | self.walk_used_in_plugin_map(unit, |
4b82fdc0 NC |
332 | unit.target.for_host(), |
333 | &mut visited)?; | |
e9c6b0a0 AC |
334 | } |
335 | Ok(()) | |
336 | } | |
337 | ||
338 | fn walk_used_in_plugin_map(&mut self, | |
339 | unit: &Unit<'a>, | |
340 | is_plugin: bool, | |
341 | visited: &mut HashSet<(Unit<'a>, bool)>) | |
342 | -> CargoResult<()> { | |
343 | if !visited.insert((*unit, is_plugin)) { | |
344 | return Ok(()) | |
345 | } | |
346 | if is_plugin { | |
347 | self.used_in_plugin.insert(*unit); | |
348 | } | |
82655b46 SG |
349 | for unit in self.dep_targets(unit)? { |
350 | self.walk_used_in_plugin_map(&unit, | |
4b82fdc0 NC |
351 | is_plugin || unit.target.for_host(), |
352 | visited)?; | |
e9c6b0a0 AC |
353 | } |
354 | Ok(()) | |
355 | } | |
356 | ||
9f5d9b81 | 357 | /// Returns the appropriate directory layout for either a plugin or not. |
41579bad AC |
358 | fn layout(&self, kind: Kind) -> &Layout { |
359 | match kind { | |
360 | Kind::Host => &self.host, | |
361 | Kind::Target => self.target.as_ref().unwrap_or(&self.host) | |
ff19a482 AC |
362 | } |
363 | } | |
364 | ||
41579bad AC |
365 | /// Returns the directories where Rust crate dependencies are found for the |
366 | /// specified unit. | |
367 | pub fn deps_dir(&self, unit: &Unit) -> &Path { | |
368 | self.layout(unit.kind).deps() | |
369 | } | |
370 | ||
371 | /// Returns the directory for the specified unit where fingerprint | |
372 | /// information is stored. | |
67b3b444 | 373 | pub fn fingerprint_dir(&mut self, unit: &Unit<'a>) -> PathBuf { |
41579bad AC |
374 | let dir = self.pkg_dir(unit); |
375 | self.layout(unit.kind).fingerprint().join(dir) | |
376 | } | |
377 | ||
378 | /// Returns the appropriate directory layout for either a plugin or not. | |
67b3b444 | 379 | pub fn build_script_dir(&mut self, unit: &Unit<'a>) -> PathBuf { |
41579bad AC |
380 | assert!(unit.target.is_custom_build()); |
381 | assert!(!unit.profile.run_custom_build); | |
382 | let dir = self.pkg_dir(unit); | |
383 | self.layout(Kind::Host).build().join(dir) | |
384 | } | |
385 | ||
386 | /// Returns the appropriate directory layout for either a plugin or not. | |
67b3b444 | 387 | pub fn build_script_out_dir(&mut self, unit: &Unit<'a>) -> PathBuf { |
41579bad AC |
388 | assert!(unit.target.is_custom_build()); |
389 | assert!(unit.profile.run_custom_build); | |
390 | let dir = self.pkg_dir(unit); | |
391 | self.layout(unit.kind).build().join(dir).join("out") | |
392 | } | |
393 | ||
a298346d AC |
394 | pub fn host_deps(&self) -> &Path { |
395 | self.host.deps() | |
396 | } | |
397 | ||
b8158cfd JM |
398 | /// Return the root of the build output tree |
399 | pub fn target_root(&self) -> &Path { | |
400 | self.host.dest() | |
401 | } | |
402 | ||
d15aaa82 AC |
403 | /// Returns the appropriate output directory for the specified package and |
404 | /// target. | |
67b3b444 | 405 | pub fn out_dir(&mut self, unit: &Unit<'a>) -> PathBuf { |
2c089cf6 | 406 | if unit.profile.doc { |
41579bad AC |
407 | self.layout(unit.kind).root().parent().unwrap().join("doc") |
408 | } else if unit.target.is_custom_build() { | |
409 | self.build_script_dir(unit) | |
410 | } else if unit.target.is_example() { | |
411 | self.layout(unit.kind).examples().to_path_buf() | |
2c089cf6 | 412 | } else { |
41579bad AC |
413 | self.deps_dir(unit).to_path_buf() |
414 | } | |
415 | } | |
416 | ||
67b3b444 | 417 | fn pkg_dir(&mut self, unit: &Unit<'a>) -> String { |
41579bad AC |
418 | let name = unit.pkg.package_id().name(); |
419 | match self.target_metadata(unit) { | |
420 | Some(meta) => format!("{}-{}", name, meta), | |
dd02aa34 | 421 | None => format!("{}-{}", name, self.target_short_hash(unit)), |
2c089cf6 | 422 | } |
d15aaa82 AC |
423 | } |
424 | ||
a2d677ce AK |
425 | /// Return the host triple for this context |
426 | pub fn host_triple(&self) -> &str { | |
d469bbcb | 427 | &self.build_config.host_triple |
a2d677ce AK |
428 | } |
429 | ||
1a985a5b AC |
430 | /// Return the target triple which this context is targeting. |
431 | pub fn target_triple(&self) -> &str { | |
23591fe5 | 432 | self.requested_target().unwrap_or_else(|| self.host_triple()) |
1a985a5b AC |
433 | } |
434 | ||
a2d677ce AK |
435 | /// Requested (not actual) target for the build |
436 | pub fn requested_target(&self) -> Option<&str> { | |
437 | self.build_config.requested_target.as_ref().map(|s| &s[..]) | |
438 | } | |
439 | ||
dd02aa34 TH |
440 | /// Get the short hash based only on the PackageId |
441 | /// Used for the metadata when target_metadata returns None | |
442 | pub fn target_short_hash(&self, unit: &Unit) -> String { | |
443 | let hashable = unit.pkg.package_id().stable_hash(self.ws.root()); | |
444 | util::short_hash(&hashable) | |
445 | } | |
446 | ||
9e779198 | 447 | /// Get the metadata for a target in a specific profile |
4650194d NK |
448 | /// We build to the path: "{filename}-{target_metadata}" |
449 | /// We use a linking step to link/copy to a predictable filename | |
450 | /// like `target/debug/libfoo.{a,so,rlib}` and such. | |
67b3b444 NK |
451 | pub fn target_metadata(&mut self, unit: &Unit<'a>) -> Option<Metadata> { |
452 | if let Some(cache) = self.target_metadatas.get(unit) { | |
453 | return cache.clone() | |
454 | } | |
455 | ||
456 | let metadata = self.calc_target_metadata(unit); | |
457 | self.target_metadatas.insert(*unit, metadata.clone()); | |
458 | metadata | |
459 | } | |
460 | ||
461 | fn calc_target_metadata(&mut self, unit: &Unit<'a>) -> Option<Metadata> { | |
530f2dd4 NK |
462 | // No metadata for dylibs because of a couple issues |
463 | // - OSX encodes the dylib name in the executable | |
464 | // - Windows rustc multiple files of which we can't easily link all of them | |
4650194d | 465 | // |
43833213 | 466 | // Two exceptions |
4650194d NK |
467 | // 1) Upstream dependencies (we aren't exporting + need to resolve name conflict) |
468 | // 2) __CARGO_DEFAULT_LIB_METADATA env var | |
469 | // | |
470 | // Note, though, that the compiler's build system at least wants | |
471 | // path dependencies (eg libstd) to have hashes in filenames. To account for | |
472 | // that we have an extra hack here which reads the | |
dc8d5d38 | 473 | // `__CARGO_DEFAULT_LIB_METADATA` environment variable and creates a |
4650194d NK |
474 | // hash in the filename if that's present. |
475 | // | |
476 | // This environment variable should not be relied on! It's | |
477 | // just here for rustbuild. We need a more principled method | |
478 | // doing this eventually. | |
dc8d5d38 | 479 | let __cargo_default_lib_metadata = env::var("__CARGO_DEFAULT_LIB_METADATA"); |
4650194d | 480 | if !unit.profile.test && |
fccaffb1 | 481 | (unit.target.is_dylib() || unit.target.is_cdylib()) && |
4650194d | 482 | unit.pkg.package_id().source_id().is_path() && |
dc8d5d38 | 483 | !__cargo_default_lib_metadata.is_ok() { |
530f2dd4 NK |
484 | return None; |
485 | } | |
486 | ||
0cc39aae AC |
487 | let mut hasher = SipHasher::new_with_keys(0, 0); |
488 | ||
489 | // Unique metadata per (name, source, version) triple. This'll allow us | |
490 | // to pull crates from anywhere w/o worrying about conflicts | |
dd02aa34 | 491 | unit.pkg.package_id().stable_hash(self.ws.root()).hash(&mut hasher); |
0cc39aae | 492 | |
d70ca210 AL |
493 | // Add package properties which map to environment variables |
494 | // exposed by Cargo | |
495 | let manifest_metadata = unit.pkg.manifest().metadata(); | |
496 | manifest_metadata.authors.hash(&mut hasher); | |
497 | manifest_metadata.description.hash(&mut hasher); | |
498 | manifest_metadata.homepage.hash(&mut hasher); | |
499 | ||
0cc39aae AC |
500 | // Also mix in enabled features to our metadata. This'll ensure that |
501 | // when changing feature sets each lib is separately cached. | |
50f1c172 | 502 | self.resolve.features_sorted(unit.pkg.package_id()).hash(&mut hasher); |
0cc39aae | 503 | |
69e0d9f7 | 504 | // Mix in the target-metadata of all the dependencies of this target |
f90d21f6 | 505 | if let Ok(deps) = self.dep_targets(unit) { |
7336e322 NK |
506 | let mut deps_metadata = deps.into_iter().map(|dep_unit| { |
507 | self.target_metadata(&dep_unit) | |
508 | }).collect::<Vec<_>>(); | |
509 | deps_metadata.sort(); | |
510 | deps_metadata.hash(&mut hasher); | |
46a5fe9a NK |
511 | } |
512 | ||
0cc39aae AC |
513 | // Throw in the profile we're compiling with. This helps caching |
514 | // panic=abort and panic=unwind artifacts, additionally with various | |
515 | // settings like debuginfo and whatnot. | |
516 | unit.profile.hash(&mut hasher); | |
517 | ||
73216cb9 AC |
518 | // Artifacts compiled for the host should have a different metadata |
519 | // piece than those compiled for the target, so make sure we throw in | |
520 | // the unit's `kind` as well | |
521 | unit.kind.hash(&mut hasher); | |
522 | ||
0cc39aae AC |
523 | // Finally throw in the target name/kind. This ensures that concurrent |
524 | // compiles of targets in the same crate don't collide. | |
525 | unit.target.name().hash(&mut hasher); | |
526 | unit.target.kind().hash(&mut hasher); | |
527 | ||
23591fe5 | 528 | if let Ok(rustc) = self.config.rustc() { |
fee7c68e MAP |
529 | rustc.verbose_version.hash(&mut hasher); |
530 | } | |
531 | ||
dc8d5d38 MAP |
532 | // Seed the contents of __CARGO_DEFAULT_LIB_METADATA to the hasher if present. |
533 | // This should be the release channel, to get a different hash for each channel. | |
534 | if let Ok(ref channel) = __cargo_default_lib_metadata { | |
535 | channel.hash(&mut hasher); | |
536 | } | |
537 | ||
d2ca374c | 538 | Some(Metadata(hasher.finish())) |
9e779198 AC |
539 | } |
540 | ||
76ff7baa | 541 | /// Returns the file stem for a given target/profile combo (with metadata) |
67b3b444 | 542 | pub fn file_stem(&mut self, unit: &Unit<'a>) -> String { |
659f8244 | 543 | match self.target_metadata(unit) { |
0cc39aae AC |
544 | Some(ref metadata) => format!("{}-{}", unit.target.crate_name(), |
545 | metadata), | |
530f2dd4 NK |
546 | None => self.bin_stem(unit), |
547 | } | |
548 | } | |
549 | ||
76ff7baa | 550 | /// Returns the bin stem for a given target (without metadata) |
530f2dd4 NK |
551 | fn bin_stem(&self, unit: &Unit) -> String { |
552 | if unit.target.allows_underscores() { | |
553 | unit.target.name().to_string() | |
554 | } else { | |
555 | unit.target.crate_name() | |
556 | } | |
557 | } | |
558 | ||
76ff7baa NK |
559 | /// Returns a tuple with the directory and name of the hard link we expect |
560 | /// our target to be copied to. Eg, file_stem may be out_dir/deps/foo-abcdef | |
561 | /// and link_stem would be out_dir/foo | |
56967a26 | 562 | /// This function returns it in two parts so the caller can add prefix/suffix |
76ff7baa NK |
563 | /// to filename separately |
564 | ||
565 | /// Returns an Option because in some cases we don't want to link | |
566 | /// (eg a dependent lib) | |
67b3b444 | 567 | pub fn link_stem(&mut self, unit: &Unit<'a>) -> Option<(PathBuf, String)> { |
530f2dd4 NK |
568 | let src_dir = self.out_dir(unit); |
569 | let bin_stem = self.bin_stem(unit); | |
570 | let file_stem = self.file_stem(unit); | |
571 | ||
572 | // We currently only lift files up from the `deps` directory. If | |
573 | // it was compiled into something like `example/` or `doc/` then | |
574 | // we don't want to link it up. | |
575 | if src_dir.ends_with("deps") { | |
576 | // Don't lift up library dependencies | |
3cbf141b AC |
577 | if self.ws.members().find(|&p| p == unit.pkg).is_none() && |
578 | !unit.target.is_bin() { | |
530f2dd4 NK |
579 | None |
580 | } else { | |
581 | Some(( | |
582 | src_dir.parent().unwrap().to_owned(), | |
583 | if unit.profile.test {file_stem} else {bin_stem}, | |
584 | )) | |
659f8244 | 585 | } |
530f2dd4 NK |
586 | } else if bin_stem == file_stem { |
587 | None | |
23591fe5 LL |
588 | } else if src_dir.ends_with("examples") |
589 | || src_dir.parent().unwrap().ends_with("build") { | |
530f2dd4 NK |
590 | Some((src_dir, bin_stem)) |
591 | } else { | |
592 | None | |
9e779198 AC |
593 | } |
594 | } | |
595 | ||
596 | /// Return the filenames that the given target for the given profile will | |
530f2dd4 NK |
597 | /// generate as a list of 3-tuples (filename, link_dst, linkable) |
598 | /// filename: filename rustc compiles to. (Often has metadata suffix). | |
599 | /// link_dst: Optional file to link/copy the result to (without metadata suffix) | |
600 | /// linkable: Whether possible to link against file (eg it's a library) | |
e6649dfa AC |
601 | pub fn target_filenames(&mut self, unit: &Unit<'a>) |
602 | -> CargoResult<Arc<Vec<(PathBuf, Option<PathBuf>, bool)>>> { | |
603 | if let Some(cache) = self.target_filenames.get(unit) { | |
23591fe5 | 604 | return Ok(Arc::clone(cache)) |
e6649dfa AC |
605 | } |
606 | ||
67b3b444 NK |
607 | let result = self.calc_target_filenames(unit); |
608 | if let Ok(ref ret) = result { | |
23591fe5 | 609 | self.target_filenames.insert(*unit, Arc::clone(ret)); |
67b3b444 NK |
610 | } |
611 | result | |
612 | } | |
613 | ||
614 | fn calc_target_filenames(&mut self, unit: &Unit<'a>) | |
615 | -> CargoResult<Arc<Vec<(PathBuf, Option<PathBuf>, bool)>>> { | |
530f2dd4 | 616 | let out_dir = self.out_dir(unit); |
659f8244 | 617 | let stem = self.file_stem(unit); |
530f2dd4 | 618 | let link_stem = self.link_stem(unit); |
3f7b09cc AC |
619 | let info = if unit.target.for_host() { |
620 | &self.host_info | |
659f8244 | 621 | } else { |
3f7b09cc | 622 | &self.target_info |
659f8244 | 623 | }; |
1e1412fd | 624 | |
8defed61 | 625 | let mut ret = Vec::new(); |
3f7b09cc AC |
626 | let mut unsupported = Vec::new(); |
627 | { | |
4b82fdc0 | 628 | if unit.profile.check { |
655decc0 NC |
629 | let filename = out_dir.join(format!("lib{}.rmeta", stem)); |
630 | let link_dst = link_stem.clone().map(|(ld, ls)| { | |
631 | ld.join(format!("lib{}.rmeta", ls)) | |
632 | }); | |
633 | ret.push((filename, link_dst, true)); | |
4b82fdc0 | 634 | } else { |
655decc0 NC |
635 | let mut add = |crate_type: &str, linkable: bool| -> CargoResult<()> { |
636 | let crate_type = if crate_type == "lib" {"rlib"} else {crate_type}; | |
4cfaf9a5 MS |
637 | let mut crate_types = info.crate_types.borrow_mut(); |
638 | let entry = crate_types.entry(crate_type.to_string()); | |
639 | let crate_type_info = match entry { | |
640 | Entry::Occupied(o) => &*o.into_mut(), | |
641 | Entry::Vacant(v) => { | |
23591fe5 | 642 | let value = info.discover_crate_type(v.key())?; |
4cfaf9a5 MS |
643 | &*v.insert(value) |
644 | } | |
645 | }; | |
646 | match *crate_type_info { | |
647 | Some((ref prefix, ref suffix)) => { | |
655decc0 NC |
648 | let filename = out_dir.join(format!("{}{}{}", prefix, stem, suffix)); |
649 | let link_dst = link_stem.clone().map(|(ld, ls)| { | |
650 | ld.join(format!("{}{}{}", prefix, ls, suffix)) | |
651 | }); | |
652 | ret.push((filename, link_dst, linkable)); | |
653 | Ok(()) | |
654 | } | |
655 | // not supported, don't worry about it | |
4cfaf9a5 | 656 | None => { |
655decc0 NC |
657 | unsupported.push(crate_type.to_string()); |
658 | Ok(()) | |
659 | } | |
655decc0 NC |
660 | } |
661 | }; | |
662 | ||
4b82fdc0 | 663 | match *unit.target.kind() { |
4b82fdc0 NC |
664 | TargetKind::Bin | |
665 | TargetKind::CustomBuild | | |
47b25fd3 | 666 | TargetKind::ExampleBin | |
4b82fdc0 NC |
667 | TargetKind::Bench | |
668 | TargetKind::Test => { | |
669 | add("bin", false)?; | |
670 | } | |
47b25fd3 KA |
671 | TargetKind::Lib(..) | |
672 | TargetKind::ExampleLib(..) | |
673 | if unit.profile.test => { | |
4b82fdc0 NC |
674 | add("bin", false)?; |
675 | } | |
47b25fd3 KA |
676 | TargetKind::ExampleLib(ref kinds) | |
677 | TargetKind::Lib(ref kinds) => { | |
678 | for kind in kinds { | |
679 | add(kind.crate_type(), kind.linkable())?; | |
4b82fdc0 | 680 | } |
7c60da4e | 681 | } |
7c60da4e | 682 | } |
85b0d0a4 | 683 | } |
1e1412fd | 684 | } |
3f7b09cc | 685 | if ret.is_empty() { |
23591fe5 | 686 | if !unsupported.is_empty() { |
3f7b09cc AC |
687 | bail!("cannot produce {} for `{}` as the target `{}` \ |
688 | does not support these crate types", | |
d469bbcb | 689 | unsupported.join(", "), unit.pkg, self.target_triple()) |
3f7b09cc AC |
690 | } |
691 | bail!("cannot compile `{}` as the target `{}` does not \ | |
692 | support any of the output crate types", | |
d469bbcb | 693 | unit.pkg, self.target_triple()); |
3f7b09cc | 694 | } |
530f2dd4 | 695 | info!("Target filenames: {:?}", ret); |
e6649dfa | 696 | |
67b3b444 | 697 | Ok(Arc::new(ret)) |
1e1412fd AC |
698 | } |
699 | ||
f90d21f6 AC |
700 | /// For a package, return all targets which are registered as dependencies |
701 | /// for that package. | |
702 | pub fn dep_targets(&self, unit: &Unit<'a>) -> CargoResult<Vec<Unit<'a>>> { | |
703 | if unit.profile.run_custom_build { | |
704 | return self.dep_run_custom_build(unit) | |
705 | } else if unit.profile.doc && !unit.profile.test { | |
706 | return self.doc_deps(unit); | |
707 | } | |
708 | ||
659f8244 | 709 | let id = unit.pkg.package_id(); |
54d738b0 | 710 | let deps = self.resolve.deps(id); |
f90d21f6 | 711 | let mut ret = deps.filter(|dep| { |
659f8244 | 712 | unit.pkg.dependencies().iter().filter(|d| { |
0a4fbbf2 | 713 | d.name() == dep.name() && d.version_req().matches(dep.version()) |
35a7006a | 714 | }).any(|d| { |
cfe8223c JK |
715 | // If this target is a build command, then we only want build |
716 | // dependencies, otherwise we want everything *other than* build | |
717 | // dependencies. | |
659f8244 AC |
718 | if unit.target.is_custom_build() != d.is_build() { |
719 | return false | |
720 | } | |
cfe8223c JK |
721 | |
722 | // If this dependency is *not* a transitive dependency, then it | |
723 | // only applies to test/example targets | |
659f8244 AC |
724 | if !d.is_transitive() && !unit.target.is_test() && |
725 | !unit.target.is_example() && !unit.profile.test { | |
726 | return false | |
727 | } | |
cfe8223c | 728 | |
06589a86 NF |
729 | // If this dependency is only available for certain platforms, |
730 | // make sure we're only enabling it for that platform. | |
659f8244 AC |
731 | if !self.dep_platform_activated(d, unit.kind) { |
732 | return false | |
733 | } | |
06589a86 | 734 | |
70152d80 AC |
735 | // If the dependency is optional, then we're only activating it |
736 | // if the corresponding feature was activated | |
50f1c172 AK |
737 | if d.is_optional() && !self.resolve.features(id).contains(d.name()) { |
738 | return false; | |
659f8244 | 739 | } |
70152d80 | 740 | |
659f8244 AC |
741 | // If we've gotten past all that, then this dependency is |
742 | // actually used! | |
743 | true | |
35a7006a | 744 | }) |
dc5671d3 | 745 | }).filter_map(|id| { |
078b6707 AC |
746 | match self.get_package(id) { |
747 | Ok(pkg) => { | |
748 | pkg.targets().iter().find(|t| t.is_lib()).map(|t| { | |
4b82fdc0 | 749 | let unit = Unit { |
078b6707 AC |
750 | pkg: pkg, |
751 | target: t, | |
02625ba8 | 752 | profile: self.lib_or_check_profile(unit, t), |
078b6707 | 753 | kind: unit.kind.for_target(t), |
4b82fdc0 NC |
754 | }; |
755 | Ok(unit) | |
078b6707 | 756 | }) |
659f8244 | 757 | } |
078b6707 AC |
758 | Err(e) => Some(Err(e)) |
759 | } | |
f90d21f6 | 760 | }).collect::<CargoResult<Vec<_>>>()?; |
b5f23288 | 761 | |
ef3215ab AC |
762 | // If this target is a build script, then what we've collected so far is |
763 | // all we need. If this isn't a build script, then it depends on the | |
764 | // build script if there is one. | |
659f8244 | 765 | if unit.target.is_custom_build() { |
078b6707 | 766 | return Ok(ret) |
9e779198 | 767 | } |
ef3215ab | 768 | ret.extend(self.dep_build_script(unit)); |
9e779198 | 769 | |
b5f23288 AC |
770 | // If this target is a binary, test, example, etc, then it depends on |
771 | // the library of the same package. The call to `resolve.deps` above | |
772 | // didn't include `pkg` in the return values, so we need to special case | |
773 | // it here and see if we need to push `(pkg, pkg_lib_target)`. | |
fb1736ef | 774 | if unit.target.is_lib() && !unit.profile.doc { |
078b6707 | 775 | return Ok(ret) |
b5f23288 | 776 | } |
659f8244 | 777 | ret.extend(self.maybe_lib(unit)); |
9e779198 | 778 | |
fb8a3e32 | 779 | // Integration tests/benchmarks require binaries to be built |
659f8244 AC |
780 | if unit.profile.test && |
781 | (unit.target.is_test() || unit.target.is_bench()) { | |
5f0d8a55 JB |
782 | ret.extend(unit.pkg.targets().iter().filter(|t| { |
783 | let no_required_features = Vec::new(); | |
784 | ||
785 | t.is_bin() && | |
786 | // Skip binaries with required features that have not been selected. | |
787 | t.required_features().unwrap_or(&no_required_features).iter().all(|f| { | |
fbc00c65 | 788 | self.resolve.features(id).contains(f) |
5f0d8a55 JB |
789 | }) |
790 | }).map(|t| { | |
659f8244 AC |
791 | Unit { |
792 | pkg: unit.pkg, | |
793 | target: t, | |
830b8af4 | 794 | profile: self.lib_profile(), |
659f8244 AC |
795 | kind: unit.kind.for_target(t), |
796 | } | |
797 | })); | |
9e779198 | 798 | } |
078b6707 | 799 | Ok(ret) |
1e1412fd | 800 | } |
9f5d9b81 | 801 | |
ef3215ab AC |
802 | /// Returns the dependencies needed to run a build script. |
803 | /// | |
804 | /// The `unit` provided must represent an execution of a build script, and | |
805 | /// the returned set of units must all be run before `unit` is run. | |
078b6707 AC |
806 | pub fn dep_run_custom_build(&self, unit: &Unit<'a>) |
807 | -> CargoResult<Vec<Unit<'a>>> { | |
ef3215ab AC |
808 | // If this build script's execution has been overridden then we don't |
809 | // actually depend on anything, we've reached the end of the dependency | |
810 | // chain as we've got all the info we're gonna get. | |
811 | let key = (unit.pkg.package_id().clone(), unit.kind); | |
f90d21f6 | 812 | if self.build_script_overridden.contains(&key) { |
078b6707 | 813 | return Ok(Vec::new()) |
ef3215ab AC |
814 | } |
815 | ||
816 | // When not overridden, then the dependencies to run a build script are: | |
817 | // | |
818 | // 1. Compiling the build script itself | |
819 | // 2. For each immediate dependency of our package which has a `links` | |
820 | // key, the execution of that build script. | |
659f8244 AC |
821 | let not_custom_build = unit.pkg.targets().iter().find(|t| { |
822 | !t.is_custom_build() | |
823 | }).unwrap(); | |
824 | let tmp = Unit { | |
825 | target: not_custom_build, | |
826 | profile: &self.profiles.dev, | |
827 | ..*unit | |
828 | }; | |
82655b46 | 829 | let deps = self.dep_targets(&tmp)?; |
078b6707 | 830 | Ok(deps.iter().filter_map(|unit| { |
659f8244 AC |
831 | if !unit.target.linkable() || unit.pkg.manifest().links().is_none() { |
832 | return None | |
833 | } | |
ef3215ab AC |
834 | self.dep_build_script(unit) |
835 | }).chain(Some(Unit { | |
659f8244 | 836 | profile: self.build_script_profile(unit.pkg.package_id()), |
ef3215ab | 837 | kind: Kind::Host, // build scripts always compiled for the host |
659f8244 | 838 | ..*unit |
078b6707 | 839 | })).collect()) |
659f8244 AC |
840 | } |
841 | ||
8350cd17 | 842 | /// Returns the dependencies necessary to document a package |
078b6707 | 843 | fn doc_deps(&self, unit: &Unit<'a>) -> CargoResult<Vec<Unit<'a>>> { |
54d738b0 | 844 | let deps = self.resolve.deps(unit.pkg.package_id()).filter(|dep| { |
659f8244 | 845 | unit.pkg.dependencies().iter().filter(|d| { |
8350cd17 | 846 | d.name() == dep.name() |
d6a9aaf3 | 847 | }).any(|dep| { |
b43d2869 | 848 | match dep.kind() { |
659f8244 AC |
849 | DepKind::Normal => self.dep_platform_activated(dep, |
850 | unit.kind), | |
b43d2869 AC |
851 | _ => false, |
852 | } | |
d6a9aaf3 | 853 | }) |
078b6707 AC |
854 | }).map(|dep| { |
855 | self.get_package(dep) | |
8350cd17 AC |
856 | }); |
857 | ||
858 | // To document a library, we depend on dependencies actually being | |
859 | // built. If we're documenting *all* libraries, then we also depend on | |
860 | // the documentation of the library being built. | |
861 | let mut ret = Vec::new(); | |
078b6707 | 862 | for dep in deps { |
82655b46 | 863 | let dep = dep?; |
078b6707 AC |
864 | let lib = match dep.targets().iter().find(|t| t.is_lib()) { |
865 | Some(lib) => lib, | |
866 | None => continue, | |
867 | }; | |
659f8244 AC |
868 | ret.push(Unit { |
869 | pkg: dep, | |
870 | target: lib, | |
830b8af4 | 871 | profile: self.lib_profile(), |
659f8244 AC |
872 | kind: unit.kind.for_target(lib), |
873 | }); | |
8350cd17 | 874 | if self.build_config.doc_all { |
659f8244 AC |
875 | ret.push(Unit { |
876 | pkg: dep, | |
877 | target: lib, | |
878 | profile: &self.profiles.doc, | |
879 | kind: unit.kind.for_target(lib), | |
880 | }); | |
8350cd17 AC |
881 | } |
882 | } | |
883 | ||
884 | // Be sure to build/run the build script for documented libraries as | |
ef3215ab | 885 | ret.extend(self.dep_build_script(unit)); |
12730033 AC |
886 | |
887 | // If we document a binary, we need the library available | |
659f8244 AC |
888 | if unit.target.is_bin() { |
889 | ret.extend(self.maybe_lib(unit)); | |
12730033 | 890 | } |
078b6707 | 891 | Ok(ret) |
8350cd17 AC |
892 | } |
893 | ||
ef3215ab AC |
894 | /// If a build script is scheduled to be run for the package specified by |
895 | /// `unit`, this function will return the unit to run that build script. | |
659f8244 | 896 | /// |
ef3215ab AC |
897 | /// Overriding a build script simply means that the running of the build |
898 | /// script itself doesn't have any dependencies, so even in that case a unit | |
899 | /// of work is still returned. `None` is only returned if the package has no | |
900 | /// build script. | |
901 | fn dep_build_script(&self, unit: &Unit<'a>) -> Option<Unit<'a>> { | |
902 | unit.pkg.targets().iter().find(|t| t.is_custom_build()).map(|t| { | |
903 | Unit { | |
904 | pkg: unit.pkg, | |
905 | target: t, | |
906 | profile: &self.profiles.custom_build, | |
907 | kind: unit.kind, | |
908 | } | |
659f8244 AC |
909 | }) |
910 | } | |
911 | ||
912 | fn maybe_lib(&self, unit: &Unit<'a>) -> Option<Unit<'a>> { | |
913 | unit.pkg.targets().iter().find(|t| t.linkable()).map(|t| { | |
914 | Unit { | |
915 | pkg: unit.pkg, | |
916 | target: t, | |
02625ba8 | 917 | profile: self.lib_or_check_profile(unit, t), |
659f8244 AC |
918 | kind: unit.kind.for_target(t), |
919 | } | |
920 | }) | |
921 | } | |
922 | ||
a3d056db AC |
923 | fn dep_platform_activated(&self, dep: &Dependency, kind: Kind) -> bool { |
924 | // If this dependency is only available for certain platforms, | |
925 | // make sure we're only enabling it for that platform. | |
f5d786e0 AC |
926 | let platform = match dep.platform() { |
927 | Some(p) => p, | |
928 | None => return true, | |
929 | }; | |
930 | let (name, info) = match kind { | |
a2d677ce AK |
931 | Kind::Host => (self.host_triple(), &self.host_info), |
932 | Kind::Target => (self.target_triple(), &self.target_info), | |
f5d786e0 AC |
933 | }; |
934 | platform.matches(name, info.cfg.as_ref().map(|cfg| &cfg[..])) | |
a3d056db AC |
935 | } |
936 | ||
c2b23512 | 937 | /// Gets a package for the given package id. |
078b6707 AC |
938 | pub fn get_package(&self, id: &PackageId) -> CargoResult<&'a Package> { |
939 | self.packages.get(id) | |
c2b23512 AC |
940 | } |
941 | ||
ac4eddbb | 942 | /// Get the user-specified linker for a particular host or target |
5a04a4d4 MB |
943 | pub fn linker(&self, kind: Kind) -> Option<&Path> { |
944 | self.target_config(kind).linker.as_ref().map(|s| s.as_ref()) | |
ac4eddbb AC |
945 | } |
946 | ||
947 | /// Get the user-specified `ar` program for a particular host or target | |
5a04a4d4 MB |
948 | pub fn ar(&self, kind: Kind) -> Option<&Path> { |
949 | self.target_config(kind).ar.as_ref().map(|s| s.as_ref()) | |
ac4eddbb AC |
950 | } |
951 | ||
23587ee7 AC |
952 | /// Get the list of cfg printed out from the compiler for the specified kind |
953 | pub fn cfg(&self, kind: Kind) -> &[Cfg] { | |
954 | let info = match kind { | |
955 | Kind::Host => &self.host_info, | |
956 | Kind::Target => &self.target_info, | |
957 | }; | |
958 | info.cfg.as_ref().map(|s| &s[..]).unwrap_or(&[]) | |
959 | } | |
960 | ||
ac4eddbb AC |
961 | /// Get the target configuration for a particular host or target |
962 | fn target_config(&self, kind: Kind) -> &TargetConfig { | |
963 | match kind { | |
38d14a59 AC |
964 | Kind::Host => &self.build_config.host, |
965 | Kind::Target => &self.build_config.target, | |
ac4eddbb AC |
966 | } |
967 | } | |
5d0cb3f2 AC |
968 | |
969 | /// Number of jobs specified for this build | |
970 | pub fn jobs(&self) -> u32 { self.build_config.jobs } | |
971 | ||
830b8af4 | 972 | pub fn lib_profile(&self) -> &'a Profile { |
75848a2a AC |
973 | let (normal, test) = if self.build_config.release { |
974 | (&self.profiles.release, &self.profiles.bench_deps) | |
9e779198 | 975 | } else { |
75848a2a AC |
976 | (&self.profiles.dev, &self.profiles.test_deps) |
977 | }; | |
978 | if self.build_config.test { | |
979 | test | |
980 | } else { | |
981 | normal | |
a40d3b03 | 982 | } |
9e779198 AC |
983 | } |
984 | ||
02625ba8 NC |
985 | pub fn lib_or_check_profile(&self, unit: &Unit, target: &Target) -> &'a Profile { |
986 | if unit.profile.check && !target.is_custom_build() && !target.for_host() { | |
987 | &self.profiles.check | |
988 | } else { | |
989 | self.lib_profile() | |
990 | } | |
991 | } | |
992 | ||
830b8af4 | 993 | pub fn build_script_profile(&self, _pkg: &PackageId) -> &'a Profile { |
183c59c0 | 994 | // TODO: should build scripts always be built with the same library |
9e779198 | 995 | // profile? How is this controlled at the CLI layer? |
830b8af4 | 996 | self.lib_profile() |
a40d3b03 | 997 | } |
e17933ec | 998 | |
30e91b57 | 999 | pub fn incremental_args(&self, unit: &Unit) -> CargoResult<Vec<String>> { |
3d19b89c MW |
1000 | if self.incremental_enabled { |
1001 | if unit.pkg.package_id().source_id().is_path() { | |
1002 | // Only enable incremental compilation for sources the user can modify. | |
1003 | // For things that change infrequently, non-incremental builds yield | |
1004 | // better performance. | |
1005 | // (see also https://github.com/rust-lang/cargo/issues/3972) | |
1006 | return Ok(vec![format!("-Zincremental={}", | |
1007 | self.layout(unit.kind).incremental().display())]); | |
23591fe5 LL |
1008 | } else if unit.profile.codegen_units.is_none() { |
1009 | // For non-incremental builds we set a higher number of | |
1010 | // codegen units so we get faster compiles. It's OK to do | |
1011 | // so because the user has already opted into slower | |
1012 | // runtime code by setting CARGO_INCREMENTAL. | |
1013 | return Ok(vec![format!("-Ccodegen-units={}", ::num_cpus::get())]); | |
3d19b89c | 1014 | } |
a5bf3b88 | 1015 | } |
3d19b89c MW |
1016 | |
1017 | Ok(vec![]) | |
a5bf3b88 NM |
1018 | } |
1019 | ||
2f01868a | 1020 | pub fn rustflags_args(&self, unit: &Unit) -> CargoResult<Vec<String>> { |
e6e6f10e | 1021 | env_args(self.config, &self.build_config, self.info(&unit.kind), unit.kind, "RUSTFLAGS") |
f639d381 AC |
1022 | } |
1023 | ||
1024 | pub fn rustdocflags_args(&self, unit: &Unit) -> CargoResult<Vec<String>> { | |
e6e6f10e | 1025 | env_args(self.config, &self.build_config, self.info(&unit.kind), unit.kind, "RUSTDOCFLAGS") |
c25c1ae1 | 1026 | } |
a30f612e AC |
1027 | |
1028 | pub fn show_warnings(&self, pkg: &PackageId) -> bool { | |
62161797 | 1029 | pkg.source_id().is_path() || self.config.extra_verbose() |
a30f612e | 1030 | } |
e6e6f10e | 1031 | |
1032 | fn info(&self, kind: &Kind) -> &TargetInfo { | |
1033 | match *kind { | |
1034 | Kind::Host => &self.host_info, | |
1035 | Kind::Target => &self.target_info, | |
1036 | } | |
1037 | } | |
c25c1ae1 | 1038 | } |
e17933ec | 1039 | |
c25c1ae1 AC |
1040 | // Acquire extra flags to pass to the compiler from the |
1041 | // RUSTFLAGS environment variable and similar config values | |
f639d381 AC |
1042 | fn env_args(config: &Config, |
1043 | build_config: &BuildConfig, | |
e6e6f10e | 1044 | target_info: &TargetInfo, |
f639d381 AC |
1045 | kind: Kind, |
1046 | name: &str) -> CargoResult<Vec<String>> { | |
c25c1ae1 AC |
1047 | // We *want* to apply RUSTFLAGS only to builds for the |
1048 | // requested target architecture, and not to things like build | |
1049 | // scripts and plugins, which may be for an entirely different | |
1050 | // architecture. Cargo's present architecture makes it quite | |
1051 | // hard to only apply flags to things that are not build | |
1052 | // scripts and plugins though, so we do something more hacky | |
1053 | // instead to avoid applying the same RUSTFLAGS to multiple targets | |
1054 | // arches: | |
1055 | // | |
1056 | // 1) If --target is not specified we just apply RUSTFLAGS to | |
1057 | // all builds; they are all going to have the same target. | |
1058 | // | |
1059 | // 2) If --target *is* specified then we only apply RUSTFLAGS | |
1060 | // to compilation units with the Target kind, which indicates | |
1061 | // it was chosen by the --target flag. | |
1062 | // | |
1063 | // This means that, e.g. even if the specified --target is the | |
1064 | // same as the host, build scripts in plugins won't get | |
1065 | // RUSTFLAGS. | |
1066 | let compiling_with_target = build_config.requested_target.is_some(); | |
1067 | let is_target_kind = kind == Kind::Target; | |
1068 | ||
1069 | if compiling_with_target && !is_target_kind { | |
1070 | // This is probably a build script or plugin and we're | |
1071 | // compiling with --target. In this scenario there are | |
1072 | // no rustflags we can apply. | |
1073 | return Ok(Vec::new()); | |
1074 | } | |
e17933ec | 1075 | |
c25c1ae1 | 1076 | // First try RUSTFLAGS from the environment |
23591fe5 LL |
1077 | if let Ok(a) = env::var(name) { |
1078 | let args = a.split(' ') | |
c25c1ae1 AC |
1079 | .map(str::trim) |
1080 | .filter(|s| !s.is_empty()) | |
1081 | .map(str::to_string); | |
1082 | return Ok(args.collect()); | |
1083 | } | |
e17933ec | 1084 | |
e6e6f10e | 1085 | let mut rustflags = Vec::new(); |
1086 | ||
f639d381 | 1087 | let name = name.chars().flat_map(|c| c.to_lowercase()).collect::<String>(); |
fccaffb1 | 1088 | // Then the target.*.rustflags value... |
e9a5fb14 JA |
1089 | let target = build_config.requested_target.as_ref().unwrap_or(&build_config.host_triple); |
1090 | let key = format!("target.{}.{}", target, name); | |
f440704e IB |
1091 | if let Some(args) = config.get_list_or_split_string(&key)? { |
1092 | let args = args.val.into_iter(); | |
e6e6f10e | 1093 | rustflags.extend(args); |
1094 | } | |
1095 | // ...including target.'cfg(...)'.rustflags | |
1096 | if let Some(ref target_cfg) = target_info.cfg { | |
1097 | if let Some(table) = config.get_table("target")? { | |
828247ac | 1098 | let cfgs = table.val.keys().filter_map(|t| { |
23591fe5 | 1099 | if t.starts_with("cfg(") && t.ends_with(')') { |
828247ac | 1100 | let cfg = &t[4..t.len() - 1]; |
1101 | CfgExpr::from_str(cfg) | |
1102 | .ok() | |
1103 | .and_then(|c| if c.matches(target_cfg) { Some(t) } else { None }) | |
1104 | } else { | |
1105 | None | |
1106 | } | |
1107 | }); | |
1108 | for n in cfgs { | |
1109 | let key = format!("target.{}.{}", n, name); | |
e6e6f10e | 1110 | if let Some(args) = config.get_list_or_split_string(&key)? { |
1111 | let args = args.val.into_iter(); | |
1112 | rustflags.extend(args); | |
1113 | } | |
1114 | } | |
1115 | } | |
1116 | } | |
1117 | ||
1118 | if !rustflags.is_empty() { | |
1119 | return Ok(rustflags); | |
e9a5fb14 JA |
1120 | } |
1121 | ||
1122 | // Then the build.rustflags value | |
f639d381 | 1123 | let key = format!("build.{}", name); |
f440704e IB |
1124 | if let Some(args) = config.get_list_or_split_string(&key)? { |
1125 | let args = args.val.into_iter(); | |
c25c1ae1 | 1126 | return Ok(args.collect()); |
e17933ec | 1127 | } |
c25c1ae1 AC |
1128 | |
1129 | Ok(Vec::new()) | |
1e1412fd | 1130 | } |
0cc39aae AC |
1131 | |
1132 | impl fmt::Display for Metadata { | |
1133 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
1134 | write!(f, "{:016x}", self.0) | |
1135 | } | |
1136 | } | |
4cfaf9a5 MS |
1137 | |
1138 | fn parse_crate_type( | |
1139 | crate_type: &str, | |
1140 | error: &str, | |
1141 | lines: &mut str::Lines, | |
1142 | ) -> CargoResult<Option<(String, String)>> { | |
1143 | let not_supported = error.lines().any(|line| { | |
1144 | (line.contains("unsupported crate type") || | |
1145 | line.contains("unknown crate type")) && | |
1146 | line.contains(crate_type) | |
1147 | }); | |
1148 | if not_supported { | |
1149 | return Ok(None); | |
1150 | } | |
1151 | let line = match lines.next() { | |
1152 | Some(line) => line, | |
1153 | None => bail!("malformed output when learning about \ | |
1154 | crate-type {} information", crate_type), | |
1155 | }; | |
1156 | let mut parts = line.trim().split("___"); | |
1157 | let prefix = parts.next().unwrap(); | |
1158 | let suffix = match parts.next() { | |
1159 | Some(part) => part, | |
1160 | None => bail!("output of --print=file-names has changed in \ | |
1161 | the compiler, cannot parse"), | |
1162 | }; | |
1163 | ||
1164 | Ok(Some((prefix.to_string(), suffix.to_string()))) | |
1165 | } |