cargo/ops/
cargo_compile.rs

1//! The Cargo "compile" operation.
2//!
3//! This module contains the entry point for starting the compilation process
4//! for commands like `build`, `test`, `doc`, `rustc`, etc.
5//!
6//! The `compile` function will do all the work to compile a workspace. A
7//! rough outline is:
8//!
9//! - Resolve the dependency graph (see `ops::resolve`).
10//! - Download any packages needed (see `PackageSet`). Note that dependency
11//!   downloads are deferred until `build_unit_dependencies`.
12//! - Generate a list of top-level "units" of work for the targets the user
13//!   requested on the command-line. Each `Unit` corresponds to a compiler
14//!   invocation. This is done in this module (`generate_targets`).
15//! - Create a `Context` which will perform the following steps:
16//!     - Build the graph of `Unit` dependencies (see
17//!       `core::compiler::context::unit_dependencies`).
18//!     - Prepare the `target` directory (see `Layout`).
19//!     - Create a job queue (see `JobQueue`). The queue checks the
20//!       fingerprint of each `Unit` to determine if it should run or be
21//!       skipped.
22//!     - Execute the queue. Each leaf in the queue's dependency graph is
23//!       executed, and then removed from the graph when finished. This
24//!       repeats until the queue is empty.
25
26use std::collections::{BTreeSet, HashMap, HashSet};
27use std::iter::FromIterator;
28use std::path::PathBuf;
29use std::sync::Arc;
30
31use crate::core::compiler::standard_lib;
32use crate::core::compiler::unit_dependencies::{
33    build_unit_dependencies, filter_locals, filter_roots,
34};
35use crate::core::compiler::unit_graph;
36use crate::core::compiler::{BuildConfig, BuildContext, Compilation, Context};
37use crate::core::compiler::{CompileKind, CompileMode, RustcTargetData, Unit};
38use crate::core::compiler::{DefaultExecutor, Executor, UnitInterner};
39use crate::core::profiles::{Profiles, UnitFor};
40use crate::core::resolver::features::{self, FeaturesFor};
41use crate::core::resolver::{HasDevUnits, Resolve, ResolveOpts};
42use crate::core::{LibKind, Package, PackageSet, Target};
43use crate::core::{PackageId, PackageIdSpec, TargetKind, Workspace};
44use crate::ops;
45use crate::ops::resolve::WorkspaceResolve;
46use crate::util::config::Config;
47use crate::util::{closest_msg, profile, CargoResult};
48
49/// Contains information about how a package should be compiled.
50#[derive(Debug)]
51pub struct CompileOptions {
52    /// Configuration information for a rustc build
53    pub build_config: BuildConfig,
54    /// Extra features to build for the root package
55    pub features: Vec<String>,
56    /// Flag whether all available features should be built for the root package
57    pub all_features: bool,
58    /// Flag if the default feature should be built for the root package
59    pub no_default_features: bool,
60    /// A set of packages to build.
61    pub spec: Packages,
62    /// Filter to apply to the root package to select which targets will be
63    /// built.
64    pub filter: CompileFilter,
65    /// Only build dependencies of the selected packages
66    pub deps_only: bool,
67    /// Only build remote dependencies of the selected packages
68    pub deps_remote_only: bool,
69    /// Extra arguments to be passed to rustdoc (single target only)
70    pub target_rustdoc_args: Option<Vec<String>>,
71    /// The specified target will be compiled with all the available arguments,
72    /// note that this only accounts for the *final* invocation of rustc
73    pub target_rustc_args: Option<Vec<String>>,
74    /// Extra arguments passed to all selected targets for rustdoc.
75    pub local_rustdoc_args: Option<Vec<String>>,
76    /// Whether the `--document-private-items` flags was specified and should
77    /// be forwarded to `rustdoc`.
78    pub rustdoc_document_private_items: bool,
79    /// The directory to copy final artifacts to. Note that even if `out_dir` is
80    /// set, a copy of artifacts still could be found a `target/(debug\release)`
81    /// as usual.
82    // Note that, although the cmd-line flag name is `out-dir`, in code we use
83    // `export_dir`, to avoid confusion with out dir at `target/debug/deps`.
84    pub export_dir: Option<PathBuf>,
85}
86
87impl<'a> CompileOptions {
88    pub fn new(config: &Config, mode: CompileMode) -> CargoResult<CompileOptions> {
89        Ok(CompileOptions {
90            build_config: BuildConfig::new(config, None, &None, mode)?,
91            features: Vec::new(),
92            all_features: false,
93            no_default_features: false,
94            spec: ops::Packages::Packages(Vec::new()),
95            filter: CompileFilter::Default {
96                required_features_filterable: false,
97            },
98            deps_only: false,
99            deps_remote_only: false,
100            target_rustdoc_args: None,
101            target_rustc_args: None,
102            local_rustdoc_args: None,
103            rustdoc_document_private_items: false,
104            export_dir: None,
105        })
106    }
107}
108
109#[derive(Clone, PartialEq, Eq, Debug)]
110pub enum Packages {
111    Default,
112    All,
113    OptOut(Vec<String>),
114    Packages(Vec<String>),
115}
116
117impl Packages {
118    pub fn from_flags(all: bool, exclude: Vec<String>, package: Vec<String>) -> CargoResult<Self> {
119        Ok(match (all, exclude.len(), package.len()) {
120            (false, 0, 0) => Packages::Default,
121            (false, 0, _) => Packages::Packages(package),
122            (false, _, _) => anyhow::bail!("--exclude can only be used together with --workspace"),
123            (true, 0, _) => Packages::All,
124            (true, _, _) => Packages::OptOut(exclude),
125        })
126    }
127
128    pub fn to_package_id_specs(&self, ws: &Workspace<'_>) -> CargoResult<Vec<PackageIdSpec>> {
129        let specs = match self {
130            Packages::All => ws
131                .members()
132                .map(Package::package_id)
133                .map(PackageIdSpec::from_package_id)
134                .collect(),
135            Packages::OptOut(opt_out) => {
136                let mut opt_out = BTreeSet::from_iter(opt_out.iter().cloned());
137                let packages = ws
138                    .members()
139                    .filter(|pkg| !opt_out.remove(pkg.name().as_str()))
140                    .map(Package::package_id)
141                    .map(PackageIdSpec::from_package_id)
142                    .collect();
143                if !opt_out.is_empty() {
144                    ws.config().shell().warn(format!(
145                        "excluded package(s) {} not found in workspace `{}`",
146                        opt_out
147                            .iter()
148                            .map(|x| x.as_ref())
149                            .collect::<Vec<_>>()
150                            .join(", "),
151                        ws.root().display(),
152                    ))?;
153                }
154                packages
155            }
156            Packages::Packages(packages) if packages.is_empty() => {
157                vec![PackageIdSpec::from_package_id(ws.current()?.package_id())]
158            }
159            Packages::Packages(packages) => packages
160                .iter()
161                .map(|p| PackageIdSpec::parse(p))
162                .collect::<CargoResult<Vec<_>>>()?,
163            Packages::Default => ws
164                .default_members()
165                .map(Package::package_id)
166                .map(PackageIdSpec::from_package_id)
167                .collect(),
168        };
169        if specs.is_empty() {
170            if ws.is_virtual() {
171                anyhow::bail!(
172                    "manifest path `{}` contains no package: The manifest is virtual, \
173                     and the workspace has no members.",
174                    ws.root().display()
175                )
176            }
177            anyhow::bail!("no packages to compile")
178        }
179        Ok(specs)
180    }
181
182    pub fn get_packages<'ws>(&self, ws: &'ws Workspace<'_>) -> CargoResult<Vec<&'ws Package>> {
183        let packages: Vec<_> = match self {
184            Packages::Default => ws.default_members().collect(),
185            Packages::All => ws.members().collect(),
186            Packages::OptOut(opt_out) => ws
187                .members()
188                .filter(|pkg| !opt_out.iter().any(|name| pkg.name().as_str() == name))
189                .collect(),
190            Packages::Packages(packages) => packages
191                .iter()
192                .map(|name| {
193                    ws.members()
194                        .find(|pkg| pkg.name().as_str() == name)
195                        .ok_or_else(|| {
196                            anyhow::format_err!(
197                                "package `{}` is not a member of the workspace",
198                                name
199                            )
200                        })
201                })
202                .collect::<CargoResult<Vec<_>>>()?,
203        };
204        Ok(packages)
205    }
206
207    /// Returns whether or not the user needs to pass a `-p` flag to target a
208    /// specific package in the workspace.
209    pub fn needs_spec_flag(&self, ws: &Workspace<'_>) -> bool {
210        match self {
211            Packages::Default => ws.default_members().count() > 1,
212            Packages::All => ws.members().count() > 1,
213            Packages::Packages(_) => true,
214            Packages::OptOut(_) => true,
215        }
216    }
217}
218
219#[derive(Debug, PartialEq, Eq)]
220pub enum LibRule {
221    /// Include the library, fail if not present
222    True,
223    /// Include the library if present
224    Default,
225    /// Exclude the library
226    False,
227}
228
229#[derive(Debug)]
230pub enum FilterRule {
231    All,
232    Just(Vec<String>),
233}
234
235#[derive(Debug)]
236pub enum CompileFilter {
237    Default {
238        /// Flag whether targets can be safely skipped when required-features are not satisfied.
239        required_features_filterable: bool,
240    },
241    Only {
242        all_targets: bool,
243        lib: LibRule,
244        bins: FilterRule,
245        examples: FilterRule,
246        tests: FilterRule,
247        benches: FilterRule,
248    },
249}
250
251pub fn compile<'a>(ws: &Workspace<'a>, options: &CompileOptions) -> CargoResult<Compilation<'a>> {
252    let exec: Arc<dyn Executor> = Arc::new(DefaultExecutor);
253    compile_with_exec(ws, options, &exec)
254}
255
256/// Like `compile` but allows specifying a custom `Executor` that will be able to intercept build
257/// calls and add custom logic. `compile` uses `DefaultExecutor` which just passes calls through.
258pub fn compile_with_exec<'a>(
259    ws: &Workspace<'a>,
260    options: &CompileOptions,
261    exec: &Arc<dyn Executor>,
262) -> CargoResult<Compilation<'a>> {
263    ws.emit_warnings()?;
264    compile_ws(ws, options, exec)
265}
266
267pub fn compile_ws<'a>(
268    ws: &Workspace<'a>,
269    options: &CompileOptions,
270    exec: &Arc<dyn Executor>,
271) -> CargoResult<Compilation<'a>> {
272    let CompileOptions {
273        ref build_config,
274        ref spec,
275        ref features,
276        all_features,
277        no_default_features,
278        ref filter,
279        deps_only,
280        deps_remote_only,
281        ref target_rustdoc_args,
282        ref target_rustc_args,
283        ref local_rustdoc_args,
284        rustdoc_document_private_items,
285        ref export_dir,
286    } = *options;
287    let config = ws.config();
288
289    match build_config.mode {
290        CompileMode::Test
291        | CompileMode::Build
292        | CompileMode::Check { .. }
293        | CompileMode::Bench
294        | CompileMode::RunCustomBuild => {
295            if std::env::var("RUST_FLAGS").is_ok() {
296                config.shell().warn(
297                    "Cargo does not read `RUST_FLAGS` environment variable. Did you mean `RUSTFLAGS`?",
298                )?;
299            }
300        }
301        CompileMode::Doc { .. } | CompileMode::Doctest => {
302            if std::env::var("RUSTDOC_FLAGS").is_ok() {
303                config.shell().warn(
304                    "Cargo does not read `RUSTDOC_FLAGS` environment variable. Did you mean `RUSTDOCFLAGS`?"
305                )?;
306            }
307        }
308    }
309
310    let profiles = Profiles::new(
311        ws.profiles(),
312        config,
313        build_config.requested_profile,
314        ws.features(),
315    )?;
316    let target_data = RustcTargetData::new(ws, build_config.requested_kind)?;
317
318    let specs = spec.to_package_id_specs(ws)?;
319    let dev_deps = ws.require_optional_deps() || filter.need_dev_deps(build_config.mode);
320    let opts = ResolveOpts::new(dev_deps, features, all_features, !no_default_features);
321    let has_dev_units = if filter.need_dev_deps(build_config.mode) {
322        HasDevUnits::Yes
323    } else {
324        HasDevUnits::No
325    };
326    let resolve = ops::resolve_ws_with_opts(
327        ws,
328        &target_data,
329        build_config.requested_kind,
330        &opts,
331        &specs,
332        has_dev_units,
333    )?;
334    let WorkspaceResolve {
335        mut pkg_set,
336        workspace_resolve,
337        targeted_resolve: resolve,
338        resolved_features,
339    } = resolve;
340
341    let std_resolve_features = if let Some(crates) = &config.cli_unstable().build_std {
342        if build_config.build_plan {
343            config
344                .shell()
345                .warn("-Zbuild-std does not currently fully support --build-plan")?;
346        }
347        if build_config.requested_kind.is_host() {
348            // TODO: This should eventually be fixed. Unfortunately it is not
349            // easy to get the host triple in BuildConfig. Consider changing
350            // requested_target to an enum, or some other approach.
351            anyhow::bail!("-Zbuild-std requires --target");
352        }
353        let (mut std_package_set, std_resolve, std_features) =
354            standard_lib::resolve_std(ws, &target_data, build_config.requested_kind, crates)?;
355        remove_dylib_crate_type(&mut std_package_set)?;
356        pkg_set.add_set(std_package_set);
357        Some((std_resolve, std_features))
358    } else {
359        None
360    };
361
362    // Find the packages in the resolver that the user wants to build (those
363    // passed in with `-p` or the defaults from the workspace), and convert
364    // Vec<PackageIdSpec> to a Vec<&PackageId>.
365    let to_build_ids = specs
366        .iter()
367        .map(|s| s.query(resolve.iter()))
368        .collect::<CargoResult<Vec<_>>>()?;
369
370    // Now get the `Package` for each `PackageId`. This may trigger a download
371    // if the user specified `-p` for a dependency that is not downloaded.
372    // Dependencies will be downloaded during build_unit_dependencies.
373    let mut to_builds = pkg_set.get_many(to_build_ids)?;
374
375    // The ordering here affects some error messages coming out of cargo, so
376    // let's be test and CLI friendly by always printing in the same order if
377    // there's an error.
378    to_builds.sort_by_key(|p| p.package_id());
379
380    for pkg in to_builds.iter() {
381        pkg.manifest().print_teapot(config);
382
383        if build_config.mode.is_any_test()
384            && !ws.is_member(pkg)
385            && pkg.dependencies().iter().any(|dep| !dep.is_transitive())
386        {
387            anyhow::bail!(
388                "package `{}` cannot be tested because it requires dev-dependencies \
389                 and is not a member of the workspace",
390                pkg.name()
391            );
392        }
393    }
394
395    let (extra_args, extra_args_name) = match (target_rustc_args, target_rustdoc_args) {
396        (&Some(ref args), _) => (Some(args.clone()), "rustc"),
397        (_, &Some(ref args)) => (Some(args.clone()), "rustdoc"),
398        _ => (None, ""),
399    };
400
401    if extra_args.is_some() && to_builds.len() != 1 {
402        panic!(
403            "`{}` should not accept multiple `-p` flags",
404            extra_args_name
405        );
406    }
407
408    profiles.validate_packages(
409        ws.profiles(),
410        &mut config.shell(),
411        workspace_resolve.as_ref().unwrap_or(&resolve),
412    )?;
413
414    let interner = UnitInterner::new();
415    let mut bcx = BuildContext::new(
416        ws,
417        &pkg_set,
418        config,
419        build_config,
420        profiles,
421        &interner,
422        HashMap::new(),
423        target_data,
424    )?;
425
426    let units = generate_targets(
427        ws,
428        &to_builds,
429        filter,
430        build_config.requested_kind,
431        &resolve,
432        &resolved_features,
433        &bcx,
434    )?;
435
436    let std_roots = if let Some(crates) = &config.cli_unstable().build_std {
437        // Only build libtest if it looks like it is needed.
438        let mut crates = crates.clone();
439        if !crates.iter().any(|c| c == "test")
440            && units
441                .iter()
442                .any(|unit| unit.mode.is_rustc_test() && unit.target.harness())
443        {
444            // Only build libtest when libstd is built (libtest depends on libstd)
445            if crates.iter().any(|c| c == "std") {
446                crates.push("test".to_string());
447            }
448        }
449        let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap();
450        standard_lib::generate_std_roots(
451            &bcx,
452            &crates,
453            std_resolve,
454            std_features,
455            build_config.requested_kind,
456        )?
457    } else {
458        Vec::new()
459    };
460
461    if let Some(args) = extra_args {
462        if units.len() != 1 {
463            anyhow::bail!(
464                "extra arguments to `{}` can only be passed to one \
465                 target, consider filtering\nthe package by passing, \
466                 e.g., `--lib` or `--bin NAME` to specify a single target",
467                extra_args_name
468            );
469        }
470        bcx.extra_compiler_args.insert(units[0], args);
471    }
472    for unit in &units {
473        if unit.mode.is_doc() || unit.mode.is_doc_test() {
474            let mut extra_args = local_rustdoc_args.clone();
475
476            // Add `--document-private-items` rustdoc flag if requested or if
477            // the target is a binary. Binary crates get their private items
478            // documented by default.
479            if rustdoc_document_private_items || unit.target.is_bin() {
480                let mut args = extra_args.take().unwrap_or_else(|| vec![]);
481                args.push("--document-private-items".into());
482                extra_args = Some(args);
483            }
484
485            if let Some(args) = extra_args {
486                bcx.extra_compiler_args.insert(*unit, args.clone());
487            }
488        }
489    }
490
491    let unit_dependencies = build_unit_dependencies(
492        &bcx,
493        &resolve,
494        &resolved_features,
495        std_resolve_features.as_ref(),
496        &units,
497        &std_roots,
498    )?;
499
500    if deps_only {
501        filter_roots(&bcx, ws, &unit_dependencies);
502    } else if deps_remote_only {
503        filter_locals(&bcx, &unit_dependencies);
504    }
505
506    if bcx.build_config.unit_graph {
507        unit_graph::emit_serialized_unit_graph(&units, &unit_dependencies)?;
508        return Ok(Compilation::new(&bcx, build_config.requested_kind)?);
509    }
510
511    let ret = {
512        let _p = profile::start("compiling");
513        let cx = Context::new(config, &bcx, unit_dependencies, build_config.requested_kind)?;
514        cx.compile(&units, export_dir.clone(), exec)?
515    };
516
517    Ok(ret)
518}
519
520impl FilterRule {
521    pub fn new(targets: Vec<String>, all: bool) -> FilterRule {
522        if all {
523            FilterRule::All
524        } else {
525            FilterRule::Just(targets)
526        }
527    }
528
529    pub fn none() -> FilterRule {
530        FilterRule::Just(Vec::new())
531    }
532
533    fn matches(&self, target: &Target) -> bool {
534        match *self {
535            FilterRule::All => true,
536            FilterRule::Just(ref targets) => targets.iter().any(|x| *x == target.name()),
537        }
538    }
539
540    fn is_specific(&self) -> bool {
541        match *self {
542            FilterRule::All => true,
543            FilterRule::Just(ref targets) => !targets.is_empty(),
544        }
545    }
546
547    pub fn try_collect(&self) -> Option<Vec<String>> {
548        match *self {
549            FilterRule::All => None,
550            FilterRule::Just(ref targets) => Some(targets.clone()),
551        }
552    }
553}
554
555impl CompileFilter {
556    /// Construct a CompileFilter from raw command line arguments.
557    pub fn from_raw_arguments(
558        lib_only: bool,
559        bins: Vec<String>,
560        all_bins: bool,
561        tsts: Vec<String>,
562        all_tsts: bool,
563        exms: Vec<String>,
564        all_exms: bool,
565        bens: Vec<String>,
566        all_bens: bool,
567        all_targets: bool,
568    ) -> CompileFilter {
569        if all_targets {
570            return CompileFilter::new_all_targets();
571        }
572        let rule_lib = if lib_only {
573            LibRule::True
574        } else {
575            LibRule::False
576        };
577        let rule_bins = FilterRule::new(bins, all_bins);
578        let rule_tsts = FilterRule::new(tsts, all_tsts);
579        let rule_exms = FilterRule::new(exms, all_exms);
580        let rule_bens = FilterRule::new(bens, all_bens);
581
582        CompileFilter::new(rule_lib, rule_bins, rule_tsts, rule_exms, rule_bens)
583    }
584
585    /// Construct a CompileFilter from underlying primitives.
586    pub fn new(
587        rule_lib: LibRule,
588        rule_bins: FilterRule,
589        rule_tsts: FilterRule,
590        rule_exms: FilterRule,
591        rule_bens: FilterRule,
592    ) -> CompileFilter {
593        if rule_lib == LibRule::True
594            || rule_bins.is_specific()
595            || rule_tsts.is_specific()
596            || rule_exms.is_specific()
597            || rule_bens.is_specific()
598        {
599            CompileFilter::Only {
600                all_targets: false,
601                lib: rule_lib,
602                bins: rule_bins,
603                examples: rule_exms,
604                benches: rule_bens,
605                tests: rule_tsts,
606            }
607        } else {
608            CompileFilter::Default {
609                required_features_filterable: true,
610            }
611        }
612    }
613
614    pub fn new_all_targets() -> CompileFilter {
615        CompileFilter::Only {
616            all_targets: true,
617            lib: LibRule::Default,
618            bins: FilterRule::All,
619            examples: FilterRule::All,
620            benches: FilterRule::All,
621            tests: FilterRule::All,
622        }
623    }
624
625    pub fn need_dev_deps(&self, mode: CompileMode) -> bool {
626        match mode {
627            CompileMode::Test | CompileMode::Doctest | CompileMode::Bench => true,
628            CompileMode::Check { test: true } => true,
629            CompileMode::Build | CompileMode::Doc { .. } | CompileMode::Check { test: false } => {
630                match *self {
631                    CompileFilter::Default { .. } => false,
632                    CompileFilter::Only {
633                        ref examples,
634                        ref tests,
635                        ref benches,
636                        ..
637                    } => examples.is_specific() || tests.is_specific() || benches.is_specific(),
638                }
639            }
640            CompileMode::RunCustomBuild => panic!("Invalid mode"),
641        }
642    }
643
644    // this selects targets for "cargo run". for logic to select targets for
645    // other subcommands, see generate_targets and filter_default_targets
646    pub fn target_run(&self, target: &Target) -> bool {
647        match *self {
648            CompileFilter::Default { .. } => true,
649            CompileFilter::Only {
650                ref lib,
651                ref bins,
652                ref examples,
653                ref tests,
654                ref benches,
655                ..
656            } => {
657                let rule = match *target.kind() {
658                    TargetKind::Bin => bins,
659                    TargetKind::Test => tests,
660                    TargetKind::Bench => benches,
661                    TargetKind::ExampleBin | TargetKind::ExampleLib(..) => examples,
662                    TargetKind::Lib(..) => {
663                        return match *lib {
664                            LibRule::True => true,
665                            LibRule::Default => true,
666                            LibRule::False => false,
667                        };
668                    }
669                    TargetKind::CustomBuild => return false,
670                };
671                rule.matches(target)
672            }
673        }
674    }
675
676    pub fn is_specific(&self) -> bool {
677        match *self {
678            CompileFilter::Default { .. } => false,
679            CompileFilter::Only { .. } => true,
680        }
681    }
682}
683
684/// A proposed target.
685///
686/// Proposed targets are later filtered into actual `Unit`s based on whether or
687/// not the target requires its features to be present.
688#[derive(Debug)]
689struct Proposal<'a> {
690    pkg: &'a Package,
691    target: &'a Target,
692    /// Indicates whether or not all required features *must* be present. If
693    /// false, and the features are not available, then it will be silently
694    /// skipped. Generally, targets specified by name (`--bin foo`) are
695    /// required, all others can be silently skipped if features are missing.
696    requires_features: bool,
697    mode: CompileMode,
698}
699
700/// Generates all the base targets for the packages the user has requested to
701/// compile. Dependencies for these targets are computed later in `unit_dependencies`.
702fn generate_targets<'a>(
703    ws: &Workspace<'_>,
704    packages: &[&'a Package],
705    filter: &CompileFilter,
706    default_arch_kind: CompileKind,
707    resolve: &'a Resolve,
708    resolved_features: &features::ResolvedFeatures,
709    bcx: &BuildContext<'a, '_>,
710) -> CargoResult<Vec<Unit<'a>>> {
711    // Helper for creating a `Unit` struct.
712    let new_unit = |pkg: &'a Package, target: &'a Target, target_mode: CompileMode| {
713        let unit_for = if target_mode.is_any_test() {
714            // NOTE: the `UnitFor` here is subtle. If you have a profile
715            // with `panic` set, the `panic` flag is cleared for
716            // tests/benchmarks and their dependencies. If this
717            // was `normal`, then the lib would get compiled three
718            // times (once with panic, once without, and once with
719            // `--test`).
720            //
721            // This would cause a problem for doc tests, which would fail
722            // because `rustdoc` would attempt to link with both libraries
723            // at the same time. Also, it's probably not important (or
724            // even desirable?) for rustdoc to link with a lib with
725            // `panic` set.
726            //
727            // As a consequence, Examples and Binaries get compiled
728            // without `panic` set. This probably isn't a bad deal.
729            //
730            // Forcing the lib to be compiled three times during `cargo
731            // test` is probably also not desirable.
732            UnitFor::new_test(bcx.config)
733        } else if target.for_host() {
734            // Proc macro / plugin should not have `panic` set.
735            UnitFor::new_compiler()
736        } else {
737            UnitFor::new_normal()
738        };
739        // Custom build units are added in `build_unit_dependencies`.
740        assert!(!target.is_custom_build());
741        let target_mode = match target_mode {
742            CompileMode::Test => {
743                if target.is_example() && !filter.is_specific() && !target.tested() {
744                    // Examples are included as regular binaries to verify
745                    // that they compile.
746                    CompileMode::Build
747                } else {
748                    CompileMode::Test
749                }
750            }
751            CompileMode::Build => match *target.kind() {
752                TargetKind::Test => CompileMode::Test,
753                TargetKind::Bench => CompileMode::Bench,
754                _ => CompileMode::Build,
755            },
756            // `CompileMode::Bench` is only used to inform `filter_default_targets`
757            // which command is being used (`cargo bench`). Afterwards, tests
758            // and benches are treated identically. Switching the mode allows
759            // de-duplication of units that are essentially identical. For
760            // example, `cargo build --all-targets --release` creates the units
761            // (lib profile:bench, mode:test) and (lib profile:bench, mode:bench)
762            // and since these are the same, we want them to be de-duplicated in
763            // `unit_dependencies`.
764            CompileMode::Bench => CompileMode::Test,
765            _ => target_mode,
766        };
767        let kind = default_arch_kind.for_target(target);
768        let profile =
769            bcx.profiles
770                .get_profile(pkg.package_id(), ws.is_member(pkg), unit_for, target_mode);
771
772        let features_for = if target.proc_macro() {
773            FeaturesFor::HostDep
774        } else {
775            // Root units are never build dependencies.
776            FeaturesFor::NormalOrDev
777        };
778        let features =
779            Vec::from(resolved_features.activated_features(pkg.package_id(), features_for));
780        bcx.units.intern(
781            pkg,
782            target,
783            profile,
784            kind,
785            target_mode,
786            features,
787            /*is_std*/ false,
788        )
789    };
790
791    // Create a list of proposed targets.
792    let mut proposals: Vec<Proposal<'_>> = Vec::new();
793
794    match *filter {
795        CompileFilter::Default {
796            required_features_filterable,
797        } => {
798            for pkg in packages {
799                let default = filter_default_targets(pkg.targets(), bcx.build_config.mode);
800                proposals.extend(default.into_iter().map(|target| Proposal {
801                    pkg,
802                    target,
803                    requires_features: !required_features_filterable,
804                    mode: bcx.build_config.mode,
805                }));
806                if bcx.build_config.mode == CompileMode::Test {
807                    if let Some(t) = pkg
808                        .targets()
809                        .iter()
810                        .find(|t| t.is_lib() && t.doctested() && t.doctestable())
811                    {
812                        proposals.push(Proposal {
813                            pkg,
814                            target: t,
815                            requires_features: false,
816                            mode: CompileMode::Doctest,
817                        });
818                    }
819                }
820            }
821        }
822        CompileFilter::Only {
823            all_targets,
824            ref lib,
825            ref bins,
826            ref examples,
827            ref tests,
828            ref benches,
829        } => {
830            if *lib != LibRule::False {
831                let mut libs = Vec::new();
832                for proposal in
833                    filter_targets(packages, Target::is_lib, false, bcx.build_config.mode)
834                {
835                    let Proposal { target, pkg, .. } = proposal;
836                    if bcx.build_config.mode.is_doc_test() && !target.doctestable() {
837                        ws.config().shell().warn(format!(
838                            "doc tests are not supported for crate type(s) `{}` in package `{}`",
839                            target.rustc_crate_types().join(", "),
840                            pkg.name()
841                        ))?;
842                    } else {
843                        libs.push(proposal)
844                    }
845                }
846                if !all_targets && libs.is_empty() && *lib == LibRule::True {
847                    let names = packages.iter().map(|pkg| pkg.name()).collect::<Vec<_>>();
848                    if names.len() == 1 {
849                        anyhow::bail!("no library targets found in package `{}`", names[0]);
850                    } else {
851                        anyhow::bail!("no library targets found in packages: {}", names.join(", "));
852                    }
853                }
854                proposals.extend(libs);
855            }
856
857            // If `--tests` was specified, add all targets that would be
858            // generated by `cargo test`.
859            let test_filter = match tests {
860                FilterRule::All => Target::tested,
861                FilterRule::Just(_) => Target::is_test,
862            };
863            let test_mode = match bcx.build_config.mode {
864                CompileMode::Build => CompileMode::Test,
865                CompileMode::Check { .. } => CompileMode::Check { test: true },
866                _ => bcx.build_config.mode,
867            };
868            // If `--benches` was specified, add all targets that would be
869            // generated by `cargo bench`.
870            let bench_filter = match benches {
871                FilterRule::All => Target::benched,
872                FilterRule::Just(_) => Target::is_bench,
873            };
874            let bench_mode = match bcx.build_config.mode {
875                CompileMode::Build => CompileMode::Bench,
876                CompileMode::Check { .. } => CompileMode::Check { test: true },
877                _ => bcx.build_config.mode,
878            };
879
880            proposals.extend(list_rule_targets(
881                packages,
882                bins,
883                "bin",
884                Target::is_bin,
885                bcx.build_config.mode,
886            )?);
887            proposals.extend(list_rule_targets(
888                packages,
889                examples,
890                "example",
891                Target::is_example,
892                bcx.build_config.mode,
893            )?);
894            proposals.extend(list_rule_targets(
895                packages,
896                tests,
897                "test",
898                test_filter,
899                test_mode,
900            )?);
901            proposals.extend(list_rule_targets(
902                packages,
903                benches,
904                "bench",
905                bench_filter,
906                bench_mode,
907            )?);
908        }
909    }
910
911    // Only include targets that are libraries or have all required
912    // features available.
913    //
914    // `features_map` is a map of &Package -> enabled_features
915    // It is computed by the set of enabled features for the package plus
916    // every enabled feature of every enabled dependency.
917    let mut features_map = HashMap::new();
918    let mut units = HashSet::new();
919    for Proposal {
920        pkg,
921        target,
922        requires_features,
923        mode,
924    } in proposals
925    {
926        let unavailable_features = match target.required_features() {
927            Some(rf) => {
928                let features = features_map.entry(pkg).or_insert_with(|| {
929                    resolve_all_features(
930                        resolve,
931                        resolved_features,
932                        &bcx.packages,
933                        pkg.package_id(),
934                    )
935                });
936                rf.iter().filter(|f| !features.contains(*f)).collect()
937            }
938            None => Vec::new(),
939        };
940        if target.is_lib() || unavailable_features.is_empty() {
941            let unit = new_unit(pkg, target, mode);
942            units.insert(unit);
943        } else if requires_features {
944            let required_features = target.required_features().unwrap();
945            let quoted_required_features: Vec<String> = required_features
946                .iter()
947                .map(|s| format!("`{}`", s))
948                .collect();
949            anyhow::bail!(
950                "target `{}` in package `{}` requires the features: {}\n\
951                 Consider enabling them by passing, e.g., `--features=\"{}\"`",
952                target.name(),
953                pkg.name(),
954                quoted_required_features.join(", "),
955                required_features.join(" ")
956            );
957        }
958        // else, silently skip target.
959    }
960    Ok(units.into_iter().collect())
961}
962
963/// Gets all of the features enabled for a package, plus its dependencies'
964/// features.
965///
966/// Dependencies are added as `dep_name/feat_name` because `required-features`
967/// wants to support that syntax.
968fn resolve_all_features(
969    resolve_with_overrides: &Resolve,
970    resolved_features: &features::ResolvedFeatures,
971    package_set: &PackageSet<'_>,
972    package_id: PackageId,
973) -> HashSet<String> {
974    let mut features: HashSet<String> = resolved_features
975        .activated_features(package_id, FeaturesFor::NormalOrDev)
976        .iter()
977        .map(|s| s.to_string())
978        .collect();
979
980    // Include features enabled for use by dependencies so targets can also use them with the
981    // required-features field when deciding whether to be built or skipped.
982    for (dep_id, deps) in resolve_with_overrides.deps(package_id) {
983        let is_proc_macro = package_set
984            .get_one(dep_id)
985            .expect("packages downloaded")
986            .proc_macro();
987        for dep in deps {
988            let features_for = if is_proc_macro || dep.is_build() {
989                FeaturesFor::HostDep
990            } else {
991                FeaturesFor::NormalOrDev
992            };
993            for feature in resolved_features.activated_features_unverified(dep_id, features_for) {
994                features.insert(format!("{}/{}", dep.name_in_toml(), feature));
995            }
996        }
997    }
998
999    features
1000}
1001
1002/// Given a list of all targets for a package, filters out only the targets
1003/// that are automatically included when the user doesn't specify any targets.
1004fn filter_default_targets(targets: &[Target], mode: CompileMode) -> Vec<&Target> {
1005    match mode {
1006        CompileMode::Bench => targets.iter().filter(|t| t.benched()).collect(),
1007        CompileMode::Test => targets
1008            .iter()
1009            .filter(|t| t.tested() || t.is_example())
1010            .collect(),
1011        CompileMode::Build | CompileMode::Check { .. } => targets
1012            .iter()
1013            .filter(|t| t.is_bin() || t.is_lib())
1014            .collect(),
1015        CompileMode::Doc { .. } => {
1016            // `doc` does lib and bins (bin with same name as lib is skipped).
1017            targets
1018                .iter()
1019                .filter(|t| {
1020                    t.documented()
1021                        && (!t.is_bin()
1022                            || !targets.iter().any(|l| l.is_lib() && l.name() == t.name()))
1023                })
1024                .collect()
1025        }
1026        CompileMode::Doctest | CompileMode::RunCustomBuild => panic!("Invalid mode {:?}", mode),
1027    }
1028}
1029
1030/// Returns a list of proposed targets based on command-line target selection flags.
1031fn list_rule_targets<'a>(
1032    packages: &[&'a Package],
1033    rule: &FilterRule,
1034    target_desc: &'static str,
1035    is_expected_kind: fn(&Target) -> bool,
1036    mode: CompileMode,
1037) -> CargoResult<Vec<Proposal<'a>>> {
1038    let mut proposals = Vec::new();
1039    match rule {
1040        FilterRule::All => {
1041            proposals.extend(filter_targets(packages, is_expected_kind, false, mode))
1042        }
1043        FilterRule::Just(names) => {
1044            for name in names {
1045                proposals.extend(find_named_targets(
1046                    packages,
1047                    name,
1048                    target_desc,
1049                    is_expected_kind,
1050                    mode,
1051                )?);
1052            }
1053        }
1054    }
1055    Ok(proposals)
1056}
1057
1058/// Finds the targets for a specifically named target.
1059fn find_named_targets<'a>(
1060    packages: &[&'a Package],
1061    target_name: &str,
1062    target_desc: &'static str,
1063    is_expected_kind: fn(&Target) -> bool,
1064    mode: CompileMode,
1065) -> CargoResult<Vec<Proposal<'a>>> {
1066    let filter = |t: &Target| t.name() == target_name && is_expected_kind(t);
1067    let proposals = filter_targets(packages, filter, true, mode);
1068    if proposals.is_empty() {
1069        let targets = packages.iter().flat_map(|pkg| {
1070            pkg.targets()
1071                .iter()
1072                .filter(|target| is_expected_kind(target))
1073        });
1074        let suggestion = closest_msg(target_name, targets, |t| t.name());
1075        anyhow::bail!(
1076            "no {} target named `{}`{}",
1077            target_desc,
1078            target_name,
1079            suggestion
1080        );
1081    }
1082    Ok(proposals)
1083}
1084
1085fn filter_targets<'a>(
1086    packages: &[&'a Package],
1087    predicate: impl Fn(&Target) -> bool,
1088    requires_features: bool,
1089    mode: CompileMode,
1090) -> Vec<Proposal<'a>> {
1091    let mut proposals = Vec::new();
1092    for pkg in packages {
1093        for target in pkg.targets().iter().filter(|t| predicate(t)) {
1094            proposals.push(Proposal {
1095                pkg,
1096                target,
1097                requires_features,
1098                mode,
1099            });
1100        }
1101    }
1102    proposals
1103}
1104
1105/// When using `-Zbuild-std` we're building the standard library, but a
1106/// technical detail of the standard library right now is that it builds itself
1107/// as both an `rlib` and a `dylib`. We don't actually want to really publicize
1108/// the `dylib` and in general it's a pain to work with, so when building libstd
1109/// we want to remove the `dylib` crate type.
1110///
1111/// Cargo doesn't have a fantastic way of doing that right now, so let's hack
1112/// around it a bit and (ab)use the fact that we have mutable access to
1113/// `PackageSet` here to rewrite downloaded packages. We iterate over all `path`
1114/// packages (which should download immediately and not actually cause blocking
1115/// here) and edit their manifests to only list one `LibKind` for an `Rlib`.
1116fn remove_dylib_crate_type(set: &mut PackageSet<'_>) -> CargoResult<()> {
1117    let ids = set
1118        .package_ids()
1119        .filter(|p| p.source_id().is_path())
1120        .collect::<Vec<_>>();
1121    set.get_many(ids.iter().cloned())?;
1122
1123    for id in ids {
1124        let pkg = set.lookup_mut(id).expect("should be downloaded now");
1125
1126        for target in pkg.manifest_mut().targets_mut() {
1127            if let TargetKind::Lib(crate_types) = target.kind_mut() {
1128                crate_types.truncate(0);
1129                crate_types.push(LibKind::Rlib);
1130            }
1131        }
1132    }
1133
1134    Ok(())
1135}