]>
Commit | Line | Data |
---|---|---|
64ff29ff AC |
1 | //! |
2 | //! Cargo compile currently does the following steps: | |
3 | //! | |
4 | //! All configurations are already injected as environment variables via the | |
5 | //! main cargo command | |
6 | //! | |
7 | //! 1. Read the manifest | |
8 | //! 2. Shell out to `cargo-resolve` with a list of dependencies and sources as | |
9 | //! stdin | |
10 | //! | |
11 | //! a. Shell out to `--do update` and `--do list` for each source | |
12 | //! b. Resolve dependencies and return a list of name/version/source | |
13 | //! | |
14 | //! 3. Shell out to `--do download` for each source | |
15 | //! 4. Shell out to `--do get` for each source, and build up the list of paths | |
16 | //! to pass to rustc -L | |
17 | //! 5. Call `cargo-rustc` with the results of the resolver zipped together with | |
18 | //! the results of the `get` | |
19 | //! | |
20 | //! a. Topologically sort the dependencies | |
21 | //! b. Compile each dependency in order, passing in the -L's pointing at each | |
22 | //! previously compiled dependency | |
23 | //! | |
62bff631 | 24 | |
f3468348 JB |
25 | use std::collections::{HashMap, HashSet}; |
26 | use std::default::Default; | |
58ddb28a | 27 | use std::path::PathBuf; |
1ab19a5a | 28 | use std::sync::Arc; |
12af54e1 | 29 | |
f00e8901 | 30 | use core::{Source, Package, Target}; |
e21bb08f JB |
31 | use core::{Profile, TargetKind, Profiles, Workspace, PackageId, PackageIdSpec}; |
32 | use core::resolver::Resolve; | |
8c3b3605 | 33 | use ops::{self, BuildOutput, Executor, DefaultExecutor}; |
56db20df | 34 | use util::config::Config; |
f00e8901 | 35 | use util::{CargoResult, profile}; |
50f110a4 | 36 | |
bacb6be3 | 37 | /// Contains information about how a package should be compiled. |
2fe0bf83 AC |
38 | pub struct CompileOptions<'a> { |
39 | pub config: &'a Config, | |
c7641c24 | 40 | /// Number of concurrent jobs to use. |
55321111 | 41 | pub jobs: Option<u32>, |
c7641c24 | 42 | /// The target platform to compile for (example: `i686-unknown-linux-gnu`). |
1946c447 | 43 | pub target: Option<&'a str>, |
9e779198 | 44 | /// Extra features to build for the root package |
2b46d039 | 45 | pub features: &'a [String], |
1f8b3988 DA |
46 | /// Flag whether all available features should be built for the root package |
47 | pub all_features: bool, | |
9e779198 | 48 | /// Flag if the default feature should be built for the root package |
2b46d039 | 49 | pub no_default_features: bool, |
6b32c4d7 | 50 | /// A set of packages to build. |
62c04979 | 51 | pub spec: Packages<'a>, |
9e779198 AC |
52 | /// Filter to apply to the root package to select which targets will be |
53 | /// built. | |
54 | pub filter: CompileFilter<'a>, | |
9e779198 AC |
55 | /// Whether this is a release build or not |
56 | pub release: bool, | |
57 | /// Mode for this compile. | |
58 | pub mode: CompileMode, | |
9f98208d AK |
59 | /// `--error_format` flag for the compiler. |
60 | pub message_format: MessageFormat, | |
4816a2a5 | 61 | /// Extra arguments to be passed to rustdoc (for main crate and dependencies) |
54127ce8 | 62 | pub target_rustdoc_args: Option<&'a [String]>, |
77bb01ec SL |
63 | /// The specified target will be compiled with all the available arguments, |
64 | /// note that this only accounts for the *final* invocation of rustc | |
65 | pub target_rustc_args: Option<&'a [String]>, | |
9e779198 AC |
66 | } |
67 | ||
69caa63b | 68 | impl<'a> CompileOptions<'a> { |
1ab19a5a | 69 | pub fn default(config: &'a Config, mode: CompileMode) -> CompileOptions<'a> |
69caa63b | 70 | { |
1ab19a5a | 71 | CompileOptions { |
69caa63b NC |
72 | config: config, |
73 | jobs: None, | |
74 | target: None, | |
75 | features: &[], | |
76 | all_features: false, | |
77 | no_default_features: false, | |
78 | spec: ops::Packages::Packages(&[]), | |
79 | mode: mode, | |
80 | release: false, | |
2a82378a | 81 | filter: CompileFilter::Everything { required_features_filterable: false }, |
69caa63b NC |
82 | message_format: MessageFormat::Human, |
83 | target_rustdoc_args: None, | |
84 | target_rustc_args: None, | |
1ab19a5a | 85 | } |
69caa63b NC |
86 | } |
87 | } | |
88 | ||
856641cc | 89 | #[derive(Clone, Copy, PartialEq, Debug)] |
9e779198 AC |
90 | pub enum CompileMode { |
91 | Test, | |
92 | Build, | |
4b82fdc0 | 93 | Check, |
9e779198 | 94 | Bench, |
8350cd17 | 95 | Doc { deps: bool }, |
fb1736ef | 96 | Doctest, |
9e779198 AC |
97 | } |
98 | ||
10373f40 | 99 | #[derive(Clone, Copy, PartialEq, Eq, Deserialize)] |
9f98208d AK |
100 | pub enum MessageFormat { |
101 | Human, | |
102 | Json | |
103 | } | |
104 | ||
261ae46f | 105 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] |
62c04979 AR |
106 | pub enum Packages<'a> { |
107 | All, | |
1f57e2ca | 108 | OptOut(&'a [String]), |
62c04979 AR |
109 | Packages(&'a [String]), |
110 | } | |
111 | ||
6b32c4d7 | 112 | impl<'a> Packages<'a> { |
33431d71 | 113 | pub fn from_flags(virtual_ws: bool, all: bool, exclude: &'a Vec<String>, package: &'a Vec<String>) |
7ffd1cfc | 114 | -> CargoResult<Self> |
115 | { | |
33431d71 | 116 | let all = all || (virtual_ws && package.is_empty()); |
117 | ||
7ffd1cfc | 118 | let packages = match (all, &exclude) { |
119 | (true, exclude) if exclude.is_empty() => Packages::All, | |
120 | (true, exclude) => Packages::OptOut(exclude), | |
121 | (false, exclude) if !exclude.is_empty() => bail!("--exclude can only be used together \ | |
122 | with --all"), | |
123 | _ => Packages::Packages(package), | |
124 | }; | |
125 | ||
126 | Ok(packages) | |
127 | } | |
128 | ||
6b32c4d7 AK |
129 | pub fn into_package_id_specs(self, ws: &Workspace) -> CargoResult<Vec<PackageIdSpec>> { |
130 | let specs = match self { | |
131 | Packages::All => { | |
132 | ws.members() | |
133 | .map(Package::package_id) | |
134 | .map(PackageIdSpec::from_package_id) | |
135 | .collect() | |
136 | } | |
1f57e2ca | 137 | Packages::OptOut(opt_out) => { |
138 | ws.members() | |
139 | .map(Package::package_id) | |
140 | .map(PackageIdSpec::from_package_id) | |
141 | .filter(|p| opt_out.iter().position(|x| *x == p.name()).is_none()) | |
142 | .collect() | |
143 | } | |
6b32c4d7 AK |
144 | Packages::Packages(packages) => { |
145 | packages.iter().map(|p| PackageIdSpec::parse(&p)).collect::<CargoResult<Vec<_>>>()? | |
146 | } | |
147 | }; | |
148 | Ok(specs) | |
149 | } | |
150 | } | |
151 | ||
2a82378a BW |
152 | #[derive(Clone, Copy)] |
153 | pub enum FilterRule<'a> { | |
154 | All, | |
155 | Just (&'a [String]), | |
156 | } | |
157 | ||
9e779198 | 158 | pub enum CompileFilter<'a> { |
25e50b58 JB |
159 | Everything { |
160 | /// Flag whether targets can be safely skipped when required-features are not satisfied. | |
161 | required_features_filterable: bool, | |
162 | }, | |
9e779198 AC |
163 | Only { |
164 | lib: bool, | |
2a82378a BW |
165 | bins: FilterRule<'a>, |
166 | examples: FilterRule<'a>, | |
167 | tests: FilterRule<'a>, | |
168 | benches: FilterRule<'a>, | |
9e779198 | 169 | } |
a1980dc7 YKCL |
170 | } |
171 | ||
58ddb28a | 172 | pub fn compile<'a>(ws: &Workspace<'a>, options: &CompileOptions<'a>) |
8a8ea1e8 | 173 | -> CargoResult<ops::Compilation<'a>> { |
1ab19a5a | 174 | compile_with_exec(ws, options, Arc::new(DefaultExecutor)) |
8c3b3605 NC |
175 | } |
176 | ||
1ab19a5a | 177 | pub fn compile_with_exec<'a>(ws: &Workspace<'a>, |
261ae46f | 178 | options: &CompileOptions<'a>, |
1ab19a5a NC |
179 | exec: Arc<Executor>) |
180 | -> CargoResult<ops::Compilation<'a>> { | |
81e5c764 AR |
181 | for member in ws.members() { |
182 | for key in member.manifest().warnings().iter() { | |
62c04979 AR |
183 | options.config.shell().warn(key)? |
184 | } | |
9fba127e | 185 | } |
8c3b3605 | 186 | compile_ws(ws, None, options, exec) |
9fba127e AC |
187 | } |
188 | ||
1ab19a5a NC |
189 | pub fn compile_ws<'a>(ws: &Workspace<'a>, |
190 | source: Option<Box<Source + 'a>>, | |
191 | options: &CompileOptions<'a>, | |
192 | exec: Arc<Executor>) | |
193 | -> CargoResult<ops::Compilation<'a>> { | |
f0647ea2 | 194 | let CompileOptions { config, jobs, target, spec, features, |
1f8b3988 | 195 | all_features, no_default_features, |
9f98208d | 196 | release, mode, message_format, |
9f6a8a78 | 197 | ref filter, |
54127ce8 | 198 | ref target_rustdoc_args, |
77bb01ec | 199 | ref target_rustc_args } = *options; |
3656bec8 | 200 | |
9fba127e | 201 | let target = target.map(|s| s.to_string()); |
9fba127e | 202 | |
5d0cb3f2 | 203 | if jobs == Some(0) { |
7ab18e3a | 204 | bail!("jobs must be at least 1") |
5d0cb3f2 | 205 | } |
a2aa2bf9 | 206 | |
151f12fc | 207 | let profiles = ws.profiles(); |
a9562433 | 208 | |
6b32c4d7 | 209 | let specs = spec.into_package_id_specs(ws)?; |
c7211de3 | 210 | let resolve = ops::resolve_ws_precisely(ws, |
f00e8901 AK |
211 | source, |
212 | features, | |
213 | all_features, | |
214 | no_default_features, | |
215 | &specs)?; | |
6b32c4d7 | 216 | let (packages, resolve_with_overrides) = resolve; |
9c296792 | 217 | |
ab9f64dd | 218 | let mut pkgids = Vec::new(); |
6b32c4d7 AK |
219 | if specs.len() > 0 { |
220 | for p in specs.iter() { | |
1e0c29c2 | 221 | pkgids.push(p.query(resolve_with_overrides.iter())?); |
ab9f64dd | 222 | } |
b3ade7c7 | 223 | } else { |
62c04979 | 224 | let root_package = ws.current()?; |
0002d440 JB |
225 | let all_features = resolve_all_features(&resolve_with_overrides, |
226 | root_package.package_id()); | |
e21bb08f | 227 | generate_targets(root_package, profiles, mode, filter, &all_features, release)?; |
ab9f64dd | 228 | pkgids.push(root_package.package_id()); |
69bc3d05 | 229 | }; |
b3ade7c7 | 230 | |
82655b46 | 231 | let to_builds = pkgids.iter().map(|id| { |
b56b61c5 | 232 | packages.get(id) |
82655b46 | 233 | }).collect::<CargoResult<Vec<_>>>()?; |
f0647ea2 FH |
234 | |
235 | let mut general_targets = Vec::new(); | |
236 | let mut package_targets = Vec::new(); | |
237 | ||
54127ce8 AC |
238 | match (*target_rustc_args, *target_rustdoc_args) { |
239 | (Some(..), _) | | |
240 | (_, Some(..)) if to_builds.len() != 1 => { | |
241 | panic!("`rustc` and `rustdoc` should not accept multiple `-p` flags") | |
242 | } | |
243 | (Some(args), _) => { | |
0002d440 JB |
244 | let all_features = resolve_all_features(&resolve_with_overrides, |
245 | to_builds[0].package_id()); | |
82655b46 | 246 | let targets = generate_targets(to_builds[0], profiles, |
e21bb08f | 247 | mode, filter, &all_features, release)?; |
54127ce8 AC |
248 | if targets.len() == 1 { |
249 | let (target, profile) = targets[0]; | |
250 | let mut profile = profile.clone(); | |
251 | profile.rustc_args = Some(args.to_vec()); | |
252 | general_targets.push((target, profile)); | |
253 | } else { | |
7ab18e3a AC |
254 | bail!("extra arguments to `rustc` can only be passed to one \ |
255 | target, consider filtering\nthe package by passing \ | |
256 | e.g. `--lib` or `--bin NAME` to specify a single target") | |
54127ce8 AC |
257 | } |
258 | } | |
259 | (None, Some(args)) => { | |
0002d440 JB |
260 | let all_features = resolve_all_features(&resolve_with_overrides, |
261 | to_builds[0].package_id()); | |
82655b46 | 262 | let targets = generate_targets(to_builds[0], profiles, |
e21bb08f | 263 | mode, filter, &all_features, release)?; |
54127ce8 AC |
264 | if targets.len() == 1 { |
265 | let (target, profile) = targets[0]; | |
266 | let mut profile = profile.clone(); | |
267 | profile.rustdoc_args = Some(args.to_vec()); | |
268 | general_targets.push((target, profile)); | |
f0647ea2 | 269 | } else { |
7ab18e3a AC |
270 | bail!("extra arguments to `rustdoc` can only be passed to one \ |
271 | target, consider filtering\nthe package by passing e.g. \ | |
272 | `--lib` or `--bin NAME` to specify a single target") | |
b3ade7c7 | 273 | } |
f0647ea2 | 274 | } |
54127ce8 | 275 | (None, None) => { |
f0647ea2 | 276 | for &to_build in to_builds.iter() { |
0002d440 JB |
277 | let all_features = resolve_all_features(&resolve_with_overrides, |
278 | to_build.package_id()); | |
82655b46 | 279 | let targets = generate_targets(to_build, profiles, mode, |
e21bb08f | 280 | filter, &all_features, release)?; |
f0647ea2 | 281 | package_targets.push((to_build, targets)); |
b3ade7c7 | 282 | } |
f0647ea2 FH |
283 | } |
284 | }; | |
b3ade7c7 | 285 | |
f0647ea2 | 286 | for &(target, ref profile) in &general_targets { |
b3ade7c7 FH |
287 | for &to_build in to_builds.iter() { |
288 | package_targets.push((to_build, vec![(target, profile)])); | |
8a8ea1e8 | 289 | } |
b3ade7c7 | 290 | } |
582e9d94 | 291 | |
b3ade7c7 | 292 | let mut ret = { |
4f12a2b5 | 293 | let _p = profile::start("compiling"); |
82655b46 | 294 | let mut build_config = scrape_build_config(config, jobs, target)?; |
9e779198 | 295 | build_config.release = release; |
0f44202b | 296 | build_config.test = mode == CompileMode::Test || mode == CompileMode::Bench; |
9f6d7989 | 297 | build_config.json_messages = message_format == MessageFormat::Json; |
8350cd17 AC |
298 | if let CompileMode::Doc { deps } = mode { |
299 | build_config.doc_all = deps; | |
300 | } | |
e1434913 | 301 | |
82655b46 | 302 | ops::compile_targets(ws, |
9c7ef529 SG |
303 | &package_targets, |
304 | &packages, | |
305 | &resolve_with_overrides, | |
306 | config, | |
307 | build_config, | |
8c3b3605 NC |
308 | profiles, |
309 | exec)? | |
0484cd88 | 310 | }; |
8b840393 | 311 | |
b3ade7c7 FH |
312 | ret.to_doc_test = to_builds.iter().map(|&p| p.clone()).collect(); |
313 | ||
e21bb08f JB |
314 | return Ok(ret); |
315 | ||
316 | fn resolve_all_features(resolve_with_overrides: &Resolve, | |
317 | package_id: &PackageId) | |
318 | -> HashSet<String> { | |
50f1c172 | 319 | let mut features = resolve_with_overrides.features(package_id).clone(); |
e21bb08f JB |
320 | |
321 | // Include features enabled for use by dependencies so targets can also use them with the | |
322 | // required-features field when deciding whether to be built or skipped. | |
323 | let deps = resolve_with_overrides.deps(package_id); | |
324 | for dep in deps { | |
50f1c172 AK |
325 | for feature in resolve_with_overrides.features(dep) { |
326 | features.insert(dep.name().to_string() + "/" + feature); | |
e21bb08f JB |
327 | } |
328 | } | |
329 | ||
330 | features | |
331 | } | |
62bff631 | 332 | } |
51c9cf0f | 333 | |
2a82378a BW |
334 | impl<'a> FilterRule<'a> { |
335 | pub fn new(targets: &'a [String], all: bool) -> FilterRule<'a> { | |
336 | if all { | |
337 | FilterRule::All | |
338 | } else { | |
339 | FilterRule::Just(targets) | |
340 | } | |
341 | } | |
342 | ||
343 | fn matches(&self, target: &Target) -> bool { | |
344 | match *self { | |
345 | FilterRule::All => true, | |
346 | FilterRule::Just(targets) => { | |
347 | targets.iter().any(|x| *x == target.name()) | |
348 | }, | |
349 | } | |
350 | } | |
351 | ||
352 | fn is_specific(&self) -> bool { | |
353 | match *self { | |
354 | FilterRule::All => true, | |
355 | FilterRule::Just(targets) => !targets.is_empty(), | |
356 | } | |
357 | } | |
358 | ||
359 | pub fn try_collect(&self) -> Option<Vec<String>> { | |
360 | match *self { | |
361 | FilterRule::All => None, | |
362 | FilterRule::Just(targets) => Some(targets.iter().map(|t| t.clone()).collect()), | |
363 | } | |
364 | } | |
365 | } | |
366 | ||
9e779198 | 367 | impl<'a> CompileFilter<'a> { |
7a1d8d94 | 368 | pub fn new(lib_only: bool, |
2a82378a BW |
369 | bins: &'a [String], all_bins: bool, |
370 | tsts: &'a [String], all_tsts: bool, | |
371 | exms: &'a [String], all_exms: bool, | |
372 | bens: &'a [String], all_bens: bool) -> CompileFilter<'a> { | |
373 | let rule_bins = FilterRule::new(bins, all_bins); | |
374 | let rule_tsts = FilterRule::new(tsts, all_tsts); | |
375 | let rule_exms = FilterRule::new(exms, all_exms); | |
376 | let rule_bens = FilterRule::new(bens, all_bens); | |
377 | ||
378 | if lib_only || rule_bins.is_specific() || rule_tsts.is_specific() | |
379 | || rule_exms.is_specific() || rule_bens.is_specific() { | |
7a1d8d94 | 380 | CompileFilter::Only { |
2a82378a BW |
381 | lib: lib_only, bins: rule_bins, |
382 | examples: rule_exms, benches: rule_bens, | |
383 | tests: rule_tsts, | |
7a1d8d94 AC |
384 | } |
385 | } else { | |
25e50b58 JB |
386 | CompileFilter::Everything { |
387 | required_features_filterable: true, | |
388 | } | |
7a1d8d94 AC |
389 | } |
390 | } | |
391 | ||
9e779198 AC |
392 | pub fn matches(&self, target: &Target) -> bool { |
393 | match *self { | |
25e50b58 | 394 | CompileFilter::Everything { .. } => true, |
9e779198 | 395 | CompileFilter::Only { lib, bins, examples, tests, benches } => { |
2a82378a | 396 | let rule = match *target.kind() { |
9e779198 AC |
397 | TargetKind::Bin => bins, |
398 | TargetKind::Test => tests, | |
399 | TargetKind::Bench => benches, | |
47b25fd3 KA |
400 | TargetKind::ExampleBin | |
401 | TargetKind::ExampleLib(..) => examples, | |
9e779198 AC |
402 | TargetKind::Lib(..) => return lib, |
403 | TargetKind::CustomBuild => return false, | |
404 | }; | |
2a82378a | 405 | rule.matches(target) |
9e779198 AC |
406 | } |
407 | } | |
408 | } | |
c98b8c4c BW |
409 | |
410 | pub fn is_specific(&self) -> bool { | |
411 | match *self { | |
412 | CompileFilter::Everything { .. } => false, | |
413 | CompileFilter::Only { .. } => true, | |
414 | } | |
415 | } | |
9e779198 AC |
416 | } |
417 | ||
2a82378a BW |
418 | #[derive(Clone, Copy, Debug)] |
419 | struct BuildProposal<'a> { | |
420 | target: &'a Target, | |
421 | profile: &'a Profile, | |
422 | required: bool, | |
423 | } | |
424 | ||
425 | fn generate_auto_targets<'a>(mode: CompileMode, targets: &'a [Target], | |
426 | profile: &'a Profile, | |
427 | dep: &'a Profile, | |
428 | required_features_filterable: bool) -> Vec<BuildProposal<'a>> { | |
429 | match mode { | |
430 | CompileMode::Bench => { | |
431 | targets.iter().filter(|t| t.benched()).map(|t| { | |
432 | BuildProposal { | |
433 | target: t, | |
434 | profile: profile, | |
435 | required: !required_features_filterable, | |
436 | } | |
437 | }).collect::<Vec<_>>() | |
438 | } | |
439 | CompileMode::Test => { | |
440 | let mut base = targets.iter().filter(|t| { | |
441 | t.tested() | |
442 | }).map(|t| { | |
443 | BuildProposal { | |
444 | target: t, | |
445 | profile: if t.is_example() {dep} else {profile}, | |
446 | required: !required_features_filterable, | |
447 | } | |
448 | }).collect::<Vec<_>>(); | |
449 | ||
450 | // Always compile the library if we're testing everything as | |
451 | // it'll be needed for doctests | |
452 | if let Some(t) = targets.iter().find(|t| t.is_lib()) { | |
453 | if t.doctested() { | |
454 | base.push(BuildProposal { | |
455 | target: t, | |
456 | profile: dep, | |
457 | required: !required_features_filterable, | |
458 | }); | |
459 | } | |
460 | } | |
461 | base | |
462 | } | |
463 | CompileMode::Build | CompileMode::Check => { | |
464 | targets.iter().filter(|t| { | |
465 | t.is_bin() || t.is_lib() | |
466 | }).map(|t| BuildProposal { | |
467 | target: t, | |
468 | profile: profile, | |
469 | required: !required_features_filterable, | |
470 | }).collect() | |
471 | } | |
472 | CompileMode::Doc { .. } => { | |
473 | targets.iter().filter(|t| { | |
474 | t.documented() | |
475 | }).map(|t| BuildProposal { | |
476 | target: t, | |
477 | profile: profile, | |
478 | required: !required_features_filterable, | |
479 | }).collect() | |
480 | } | |
481 | CompileMode::Doctest => { | |
482 | if let Some(t) = targets.iter().find(|t| t.is_lib()) { | |
483 | if t.doctested() { | |
484 | return vec![BuildProposal { | |
485 | target: t, | |
486 | profile: profile, | |
487 | required: !required_features_filterable, | |
488 | }]; | |
489 | } | |
490 | } | |
491 | ||
492 | Vec::new() | |
493 | } | |
494 | } | |
495 | } | |
496 | ||
497 | /// Given a filter rule and some context, propose a list of targets | |
498 | fn propose_indicated_targets<'a>(pkg: &'a Package, | |
499 | rule: FilterRule, | |
500 | desc: &'static str, | |
501 | is_expected_kind: fn(&Target) -> bool, | |
502 | profile: &'a Profile) -> CargoResult<Vec<BuildProposal<'a>>> { | |
503 | match rule { | |
504 | FilterRule::All => { | |
505 | let result = pkg.targets().iter().filter(|t| is_expected_kind(t)).map(|t| { | |
506 | BuildProposal { | |
507 | target: t, | |
508 | profile: profile, | |
509 | required: false, | |
510 | } | |
511 | }); | |
512 | return Ok(result.collect()); | |
513 | } | |
514 | FilterRule::Just(names) => { | |
515 | let mut targets = Vec::new(); | |
516 | for name in names { | |
517 | let target = pkg.targets().iter().find(|t| { | |
518 | t.name() == *name && is_expected_kind(t) | |
519 | }); | |
520 | let t = match target { | |
521 | Some(t) => t, | |
522 | None => { | |
523 | let suggestion = pkg.find_closest_target(name, is_expected_kind); | |
524 | match suggestion { | |
525 | Some(s) => { | |
526 | let suggested_name = s.name(); | |
527 | bail!("no {} target named `{}`\n\nDid you mean `{}`?", | |
528 | desc, name, suggested_name) | |
529 | } | |
530 | None => bail!("no {} target named `{}`", desc, name), | |
531 | } | |
532 | } | |
533 | }; | |
534 | debug!("found {} `{}`", desc, name); | |
535 | targets.push(BuildProposal { | |
536 | target: t, | |
537 | profile: profile, | |
538 | required: true, | |
539 | }); | |
540 | } | |
541 | return Ok(targets); | |
542 | } | |
543 | } | |
544 | } | |
545 | ||
546 | /// Collect the targets that are libraries or have all required features available. | |
547 | fn filter_compatible_targets<'a>(mut proposals: Vec<BuildProposal<'a>>, | |
548 | features: &HashSet<String>) | |
549 | -> CargoResult<Vec<(&'a Target, &'a Profile)>> { | |
550 | let mut compatible = Vec::with_capacity(proposals.len()); | |
551 | for proposal in proposals.drain(..) { | |
552 | let unavailable_features = match proposal.target.required_features() { | |
553 | Some(rf) => rf.iter().filter(|f| !features.contains(*f)).collect(), | |
554 | None => Vec::new(), | |
555 | }; | |
556 | if proposal.target.is_lib() || unavailable_features.is_empty() { | |
557 | compatible.push((proposal.target, proposal.profile)); | |
558 | } else if proposal.required { | |
559 | let required_features = proposal.target.required_features().unwrap(); | |
560 | let quoted_required_features: Vec<String> = required_features.iter() | |
561 | .map(|s| format!("`{}`",s)) | |
562 | .collect(); | |
563 | bail!("target `{}` requires the features: {}\n\ | |
564 | Consider enabling them by passing e.g. `--features=\"{}\"`", | |
565 | proposal.target.name(), | |
566 | quoted_required_features.join(", "), | |
567 | required_features.join(" ")); | |
568 | } | |
569 | } | |
570 | Ok(compatible) | |
571 | } | |
572 | ||
9e779198 AC |
573 | /// Given the configuration for a build, this function will generate all |
574 | /// target/profile combinations needed to be built. | |
575 | fn generate_targets<'a>(pkg: &'a Package, | |
e50ccf73 | 576 | profiles: &'a Profiles, |
9e779198 AC |
577 | mode: CompileMode, |
578 | filter: &CompileFilter, | |
e21bb08f | 579 | features: &HashSet<String>, |
9e779198 AC |
580 | release: bool) |
581 | -> CargoResult<Vec<(&'a Target, &'a Profile)>> { | |
9e779198 | 582 | let build = if release {&profiles.release} else {&profiles.dev}; |
26cf0049 | 583 | let test = if release {&profiles.bench} else {&profiles.test}; |
9e779198 | 584 | let profile = match mode { |
26cf0049 | 585 | CompileMode::Test => test, |
9e779198 AC |
586 | CompileMode::Bench => &profiles.bench, |
587 | CompileMode::Build => build, | |
4b82fdc0 | 588 | CompileMode::Check => &profiles.check, |
8350cd17 | 589 | CompileMode::Doc { .. } => &profiles.doc, |
fb1736ef | 590 | CompileMode::Doctest => &profiles.doctest, |
9e779198 | 591 | }; |
fb1736ef | 592 | |
2a82378a BW |
593 | let targets = match *filter { |
594 | CompileFilter::Everything { required_features_filterable } => { | |
595 | let deps = if release { | |
596 | &profiles.bench_deps | |
597 | } else { | |
598 | &profiles.test_deps | |
599 | }; | |
600 | generate_auto_targets(mode, pkg.targets(), profile, deps, required_features_filterable) | |
9e779198 AC |
601 | } |
602 | CompileFilter::Only { lib, bins, examples, tests, benches } => { | |
603 | let mut targets = Vec::new(); | |
604 | ||
605 | if lib { | |
606 | if let Some(t) = pkg.targets().iter().find(|t| t.is_lib()) { | |
2a82378a BW |
607 | targets.push(BuildProposal { |
608 | target: t, | |
609 | profile: profile, | |
610 | required: true, | |
611 | }); | |
9e779198 | 612 | } else { |
7ab18e3a | 613 | bail!("no library targets found") |
9e779198 AC |
614 | } |
615 | } | |
616 | ||
2a82378a BW |
617 | targets.append(&mut propose_indicated_targets( |
618 | pkg, bins, "bin", Target::is_bin, profile)?); | |
619 | targets.append(&mut propose_indicated_targets( | |
620 | pkg, examples, "example", Target::is_example, build)?); | |
621 | targets.append(&mut propose_indicated_targets( | |
622 | pkg, tests, "test", Target::is_test, test)?); | |
623 | targets.append(&mut propose_indicated_targets( | |
624 | pkg, benches, "bench", Target::is_bench, &profiles.bench)?); | |
f3468348 JB |
625 | targets |
626 | } | |
627 | }; | |
628 | ||
2a82378a | 629 | filter_compatible_targets(targets, features) |
9e779198 AC |
630 | } |
631 | ||
9e779198 AC |
632 | /// Parse all config files to learn about build configuration. Currently |
633 | /// configured options are: | |
634 | /// | |
635 | /// * build.jobs | |
74420506 | 636 | /// * build.target |
9e779198 AC |
637 | /// * target.$target.ar |
638 | /// * target.$target.linker | |
639 | /// * target.$target.libfoo.metadata | |
ac4eddbb | 640 | fn scrape_build_config(config: &Config, |
5d0cb3f2 | 641 | jobs: Option<u32>, |
9e779198 AC |
642 | target: Option<String>) |
643 | -> CargoResult<ops::BuildConfig> { | |
cbf25a9b AC |
644 | if jobs.is_some() && config.jobserver_from_env().is_some() { |
645 | config.shell().warn("a `-j` argument was passed to Cargo but Cargo is \ | |
646 | also configured with an external jobserver in \ | |
647 | its environment, ignoring the `-j` parameter")?; | |
648 | } | |
82655b46 | 649 | let cfg_jobs = match config.get_i64("build.jobs")? { |
455f800c AC |
650 | Some(v) => { |
651 | if v.val <= 0 { | |
652 | bail!("build.jobs must be positive, but found {} in {}", | |
653 | v.val, v.definition) | |
654 | } else if v.val >= u32::max_value() as i64 { | |
655 | bail!("build.jobs is too large: found {} in {}", v.val, | |
656 | v.definition) | |
80fe0e6d | 657 | } else { |
455f800c | 658 | Some(v.val as u32) |
fa561275 AC |
659 | } |
660 | } | |
661 | None => None, | |
662 | }; | |
80fe0e6d | 663 | let jobs = jobs.or(cfg_jobs).unwrap_or(::num_cpus::get() as u32); |
82655b46 | 664 | let cfg_target = config.get_string("build.target")?.map(|s| s.val); |
74420506 | 665 | let target = target.or(cfg_target); |
5d0cb3f2 | 666 | let mut base = ops::BuildConfig { |
82655b46 | 667 | host_triple: config.rustc()?.host.clone(), |
5d0cb3f2 | 668 | requested_target: target.clone(), |
d469bbcb | 669 | jobs: jobs, |
5d0cb3f2 AC |
670 | ..Default::default() |
671 | }; | |
82655b46 | 672 | base.host = scrape_target_config(config, &base.host_triple)?; |
5d0cb3f2 | 673 | base.target = match target.as_ref() { |
82655b46 | 674 | Some(triple) => scrape_target_config(config, &triple)?, |
5d0cb3f2 | 675 | None => base.host.clone(), |
ac4eddbb | 676 | }; |
5d0cb3f2 | 677 | Ok(base) |
ac4eddbb AC |
678 | } |
679 | ||
a468236a | 680 | fn scrape_target_config(config: &Config, triple: &str) |
ac4eddbb | 681 | -> CargoResult<ops::TargetConfig> { |
d9190d98 | 682 | |
a468236a | 683 | let key = format!("target.{}", triple); |
ac4eddbb | 684 | let mut ret = ops::TargetConfig { |
82655b46 SG |
685 | ar: config.get_path(&format!("{}.ar", key))?.map(|v| v.val), |
686 | linker: config.get_path(&format!("{}.linker", key))?.map(|v| v.val), | |
ac4eddbb AC |
687 | overrides: HashMap::new(), |
688 | }; | |
82655b46 | 689 | let table = match config.get_table(&key)? { |
455f800c | 690 | Some(table) => table.val, |
a468236a AC |
691 | None => return Ok(ret), |
692 | }; | |
53b74226 | 693 | for (lib_name, value) in table { |
e1a85a60 IS |
694 | match lib_name.as_str() { |
695 | "ar" | "linker" | "runner" | "rustflags" => { | |
696 | continue | |
697 | }, | |
698 | _ => {} | |
455f800c | 699 | } |
a468236a AC |
700 | |
701 | let mut output = BuildOutput { | |
702 | library_paths: Vec::new(), | |
703 | library_links: Vec::new(), | |
61a0ace4 | 704 | cfgs: Vec::new(), |
8b744a05 | 705 | env: Vec::new(), |
a468236a | 706 | metadata: Vec::new(), |
7c97c5bf | 707 | rerun_if_changed: Vec::new(), |
fe8bbb7a | 708 | rerun_if_env_changed: Vec::new(), |
a30f612e | 709 | warnings: Vec::new(), |
a468236a | 710 | }; |
d38e4d31 NJ |
711 | // We require deterministic order of evaluation, so we must sort the pairs by key first. |
712 | let mut pairs = Vec::new(); | |
82655b46 | 713 | for (k, value) in value.table(&lib_name)?.0 { |
d38e4d31 NJ |
714 | pairs.push((k,value)); |
715 | } | |
716 | pairs.sort_by_key( |p| p.0 ); | |
717 | for (k,value) in pairs{ | |
a468236a | 718 | let key = format!("{}.{}", key, k); |
56db20df AC |
719 | match &k[..] { |
720 | "rustc-flags" => { | |
82655b46 | 721 | let (flags, definition) = value.string(&k)?; |
56db20df | 722 | let whence = format!("in `{}` (in {})", key, |
53b74226 | 723 | definition.display()); |
9f6d7989 | 724 | let (paths, links) = |
53b74226 | 725 | BuildOutput::parse_rustc_flags(&flags, &whence) |
82655b46 | 726 | ?; |
53b74226 AC |
727 | output.library_paths.extend(paths); |
728 | output.library_links.extend(links); | |
56db20df AC |
729 | } |
730 | "rustc-link-lib" => { | |
82655b46 | 731 | let list = value.list(&k)?; |
53b74226 AC |
732 | output.library_links.extend(list.iter() |
733 | .map(|v| v.0.clone())); | |
56db20df AC |
734 | } |
735 | "rustc-link-search" => { | |
82655b46 | 736 | let list = value.list(&k)?; |
53b74226 | 737 | output.library_paths.extend(list.iter().map(|v| { |
56db20df AC |
738 | PathBuf::from(&v.0) |
739 | })); | |
740 | } | |
741 | "rustc-cfg" => { | |
82655b46 | 742 | let list = value.list(&k)?; |
53b74226 | 743 | output.cfgs.extend(list.iter().map(|v| v.0.clone())); |
56db20df | 744 | } |
f1ae9f84 KK |
745 | "rustc-env" => { |
746 | for (name, val) in value.table(&k)?.0 { | |
747 | let val = val.string(name)?.0; | |
748 | output.env.push((name.clone(), val.to_string())); | |
749 | } | |
750 | } | |
fe8bbb7a AC |
751 | "warning" | |
752 | "rerun-if-changed" | | |
753 | "rerun-if-env-changed" => { | |
8a286df9 VP |
754 | bail!("`{}` is not supported in build script overrides", k); |
755 | } | |
56db20df | 756 | _ => { |
82655b46 | 757 | let val = value.string(&k)?.0; |
53b74226 | 758 | output.metadata.push((k.clone(), val.to_string())); |
56db20df | 759 | } |
63915360 | 760 | } |
12af54e1 | 761 | } |
a468236a | 762 | ret.overrides.insert(lib_name, output); |
12af54e1 AC |
763 | } |
764 | ||
63915360 | 765 | Ok(ret) |
5569835e | 766 | } |