    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78

    use crate::core::compiler::standard_lib;
    use crate::core::compiler::{BuildConfig, CompileMode, RustcTargetData};
    use crate::core::{PackageSet, Resolve, Workspace};
    use crate::ops;
    use crate::util::context::JobsConfig;
    use crate::util::CargoResult;
    use crate::util::GlobalContext;
    use std::collections::HashSet;

    pub struct FetchOptions<'a> {
        pub gctx: &'a GlobalContext,
        /// The target arch triple to fetch dependencies for
        pub targets: Vec<String>,
    }

    /// Executes `cargo fetch`.
    pub fn fetch<'a>(
        ws: &Workspace<'a>,
        options: &FetchOptions<'a>,
    ) -> CargoResult<(Resolve, PackageSet<'a>)> {
        ws.emit_warnings()?;
        let dry_run = false;
        let (mut packages, resolve) = ops::resolve_ws(ws, dry_run)?;

        let jobs = Some(JobsConfig::Integer(1));
        let keep_going = false;
        let gctx = ws.gctx();
        let build_config =
            BuildConfig::new(gctx, jobs, keep_going, &options.targets, CompileMode::Build)?;
        let mut data = RustcTargetData::new(ws, &build_config.requested_kinds)?;
        let mut fetched_packages = HashSet::new();
        let mut deps_to_fetch = ws.members().map(|p| p.package_id()).collect::<Vec<_>>();
        let mut to_download = Vec::new();

        while let Some(id) = deps_to_fetch.pop() {
            if !fetched_packages.insert(id) {
                continue;
            }

            to_download.push(id);
            let deps = resolve
                .deps(id)
                .filter(|&(_id, deps)| {
                    deps.iter().any(|d| {
                        // If no target was specified then all dependencies are
                        // fetched.
                        if options.targets.is_empty() {
                            return true;
                        }

                        // Otherwise we only download this dependency if any of the
                        // requested platforms would match this dependency. Note
                        // that this is a bit lossy because not all dependencies are
                        // always compiled for all platforms, but it should be
                        // "close enough" for now.
                        build_config
                            .requested_kinds
                            .iter()
                            .any(|kind| data.dep_platform_activated(d, *kind))
                    })
                })
                .map(|(id, _deps)| id);
            deps_to_fetch.extend(deps);
        }

        // If -Zbuild-std was passed, download dependencies for the standard library.
        // We don't know ahead of time what jobs we'll be running, so tell `std_crates` that.
        if let Some(crates) = standard_lib::std_crates(gctx, None) {
            let (std_package_set, _, _) =
                standard_lib::resolve_std(ws, &mut data, &build_config, &crates)?;
            packages.add_set(std_package_set);
        }

        packages.get_many(to_download)?;
        crate::core::gc::auto_gc(gctx);

        Ok((resolve, packages))
    }