Skip to main content

cargo/workspace/
package.rs

1use std::cell::OnceCell;
2use std::cell::{Cell, Ref, RefCell};
3use std::cmp::Ordering;
4use std::collections::{BTreeMap, BTreeSet};
5use std::fmt;
6use std::hash;
7use std::path::{Path, PathBuf};
8use std::rc::Rc;
9use std::time::{Duration, Instant};
10
11use crate::util::data_structures::{HashMap, HashSet};
12use anyhow::Context as _;
13use cargo_util_schemas::manifest::{FeatureDefinition, Hints, RustVersion};
14use futures::FutureExt;
15use futures::TryStreamExt;
16use futures::stream::FuturesUnordered;
17use http::Request;
18use semver::Version;
19use serde::Serialize;
20use tracing::debug;
21
22use crate::compiler::{CompileKind, RustcTargetData};
23use crate::resolver::features::ForceAllTargets;
24use crate::resolver::{HasDevUnits, Resolve};
25use crate::sources::source::{MaybePackage, SourceMap};
26use crate::util::HumanBytes;
27use crate::util::cache_lock::{CacheLock, CacheLockMode};
28use crate::util::errors::{CargoResult, HttpNotSuccessful};
29use crate::util::interning::InternedString;
30use crate::util::network::retry::{Retry, RetryResult};
31use crate::util::{self, GlobalContext, Progress, ProgressStyle, internal};
32use crate::workspace::dependency::DepKind;
33use crate::workspace::{
34    CliUnstable, Dependency, Feature, Features, Manifest, PackageId, PackageIdSpec,
35    SerializedDependency, SourceId, Target,
36};
37use crate::workspace::{Summary, Workspace};
38
39/// Information about a package that is available somewhere in the file system.
40///
41/// A package is a `Cargo.toml` file plus all the files that are part of it.
42#[derive(Clone)]
43pub struct Package {
44    inner: Rc<PackageInner>,
45}
46
47#[derive(Clone)]
48// TODO: is `manifest_path` a relic?
49struct PackageInner {
50    /// The package's manifest.
51    manifest: Manifest,
52    /// The root of the package.
53    manifest_path: PathBuf,
54}
55
56impl Ord for Package {
57    fn cmp(&self, other: &Package) -> Ordering {
58        self.package_id().cmp(&other.package_id())
59    }
60}
61
62impl PartialOrd for Package {
63    fn partial_cmp(&self, other: &Package) -> Option<Ordering> {
64        Some(self.cmp(other))
65    }
66}
67
68/// A Package in a form where `Serialize` can be derived.
69#[derive(Serialize)]
70pub struct SerializedPackage {
71    name: InternedString,
72    version: Version,
73    id: PackageIdSpec,
74    license: Option<String>,
75    license_file: Option<String>,
76    description: Option<String>,
77    source: SourceId,
78    dependencies: Vec<SerializedDependency>,
79    targets: Vec<Target>,
80    features: BTreeMap<InternedString, Vec<InternedString>>,
81    #[serde(skip_serializing_if = "Option::is_none")]
82    features_v2: Option<BTreeMap<InternedString, SerializedFeature>>,
83    manifest_path: PathBuf,
84    metadata: Option<toml::Value>,
85    publish: Option<Vec<String>>,
86    authors: Vec<String>,
87    categories: Vec<String>,
88    keywords: Vec<String>,
89    readme: Option<String>,
90    repository: Option<String>,
91    homepage: Option<String>,
92    documentation: Option<String>,
93    edition: String,
94    links: Option<String>,
95    #[serde(skip_serializing_if = "Option::is_none")]
96    metabuild: Option<Vec<String>>,
97    default_run: Option<String>,
98    rust_version: Option<RustVersion>,
99    #[serde(skip_serializing_if = "Option::is_none")]
100    hints: Option<Hints>,
101}
102
103#[derive(Serialize)]
104struct SerializedFeature {
105    enables: Vec<InternedString>,
106    #[serde(skip_serializing_if = "Option::is_none")]
107    doc: Option<String>,
108}
109
110impl Package {
111    /// Creates a package from a manifest and its location.
112    pub fn new(manifest: Manifest, manifest_path: &Path) -> Package {
113        Package {
114            inner: Rc::new(PackageInner {
115                manifest,
116                manifest_path: manifest_path.to_path_buf(),
117            }),
118        }
119    }
120
121    /// Gets the manifest dependencies.
122    pub fn dependencies(&self) -> &[Dependency] {
123        self.manifest().dependencies()
124    }
125    /// Gets the manifest.
126    pub fn manifest(&self) -> &Manifest {
127        &self.inner.manifest
128    }
129    /// Gets the manifest.
130    pub fn manifest_mut(&mut self) -> &mut Manifest {
131        &mut Rc::make_mut(&mut self.inner).manifest
132    }
133    /// Gets the path to the manifest.
134    pub fn manifest_path(&self) -> &Path {
135        &self.inner.manifest_path
136    }
137    /// Gets the name of the package.
138    pub fn name(&self) -> InternedString {
139        self.package_id().name()
140    }
141    /// Gets the `PackageId` object for the package (fully defines a package).
142    pub fn package_id(&self) -> PackageId {
143        self.manifest().package_id()
144    }
145    /// Gets the root folder of the package.
146    pub fn root(&self) -> &Path {
147        self.manifest_path().parent().unwrap()
148    }
149    /// Gets the summary for the package.
150    pub fn summary(&self) -> &Summary {
151        self.manifest().summary()
152    }
153    /// Gets the targets specified in the manifest.
154    pub fn targets(&self) -> &[Target] {
155        self.manifest().targets()
156    }
157    /// Gets the library crate for this package, if it exists.
158    pub fn library(&self) -> Option<&Target> {
159        self.targets().iter().find(|t| t.is_lib())
160    }
161    /// Gets the current package version.
162    pub fn version(&self) -> &Version {
163        self.package_id().version()
164    }
165    /// Gets the package authors.
166    pub fn authors(&self) -> &Vec<String> {
167        &self.manifest().metadata().authors
168    }
169
170    /// Returns `None` if the package is set to publish.
171    /// Returns `Some(allowed_registries)` if publishing is limited to specified
172    /// registries or if package is set to not publish.
173    pub fn publish(&self) -> &Option<Vec<String>> {
174        self.manifest().publish()
175    }
176    /// Returns `true` if this package is a proc-macro.
177    pub fn proc_macro(&self) -> bool {
178        self.targets().iter().any(|target| target.proc_macro())
179    }
180    /// Gets the package's minimum Rust version.
181    pub fn rust_version(&self) -> Option<&RustVersion> {
182        self.manifest().rust_version()
183    }
184
185    /// Gets the package's hints.
186    pub fn hints(&self) -> Option<&Hints> {
187        self.manifest().hints()
188    }
189
190    /// Returns `true` if the package uses a custom build script for any target.
191    pub fn has_custom_build(&self) -> bool {
192        self.targets().iter().any(|t| t.is_custom_build())
193    }
194
195    pub fn map_source(self, to_replace: SourceId, replace_with: SourceId) -> Package {
196        Package {
197            inner: Rc::new(PackageInner {
198                manifest: self.manifest().clone().map_source(to_replace, replace_with),
199                manifest_path: self.manifest_path().to_owned(),
200            }),
201        }
202    }
203
204    pub fn serialized(
205        &self,
206        unstable_flags: &CliUnstable,
207        cargo_features: &Features,
208    ) -> SerializedPackage {
209        let summary = self.manifest().summary();
210        let package_id = summary.package_id();
211        let manmeta = self.manifest().metadata();
212        // Filter out metabuild targets. They are an internal implementation
213        // detail that is probably not relevant externally. There's also not a
214        // real path to show in `src_path`, and this avoids changing the format.
215        let targets: Vec<Target> = self
216            .manifest()
217            .targets()
218            .iter()
219            .filter(|t| t.src_path().is_path())
220            .cloned()
221            .collect();
222        let manifest_features = self.manifest().normalized_toml().features();
223        let mut crate_features = BTreeMap::new();
224        let mut features_v2 = cargo_features
225            .is_enabled(Feature::feature_metadata())
226            .then(BTreeMap::new);
227        for (k, v) in summary.features() {
228            let enables: Vec<InternedString> = v.iter().map(|fv| fv.to_string().into()).collect();
229            if let Some(features_v2) = &mut features_v2 {
230                let doc = manifest_features
231                    .and_then(|mf| mf.get(k.as_str()))
232                    .and_then(|feat_def| match feat_def {
233                        FeatureDefinition::Metadata(meta) => meta.doc.clone(),
234                        FeatureDefinition::Array(_) => None,
235                    });
236                features_v2.insert(
237                    *k,
238                    SerializedFeature {
239                        enables: enables.clone(),
240                        doc,
241                    },
242                );
243            }
244            crate_features.insert(*k, enables);
245        }
246
247        SerializedPackage {
248            name: package_id.name(),
249            version: package_id.version().clone(),
250            id: package_id.to_spec(),
251            license: manmeta.license.clone(),
252            license_file: manmeta.license_file.clone(),
253            description: manmeta.description.clone(),
254            source: summary.source_id(),
255            dependencies: summary
256                .dependencies()
257                .iter()
258                .map(|dep| dep.serialized(unstable_flags, cargo_features))
259                .collect(),
260            targets,
261            features: crate_features,
262            features_v2,
263            manifest_path: self.manifest_path().to_path_buf(),
264            metadata: self.manifest().custom_metadata().cloned(),
265            authors: manmeta.authors.clone(),
266            categories: manmeta.categories.clone(),
267            keywords: manmeta.keywords.clone(),
268            readme: manmeta.readme.clone(),
269            repository: manmeta.repository.clone(),
270            homepage: manmeta.homepage.clone(),
271            documentation: manmeta.documentation.clone(),
272            edition: self.manifest().edition().to_string(),
273            links: self.manifest().links().map(|s| s.to_owned()),
274            metabuild: self.manifest().metabuild().cloned(),
275            publish: self.publish().as_ref().cloned(),
276            default_run: self.manifest().default_run().map(|s| s.to_owned()),
277            rust_version: self.rust_version().cloned(),
278            hints: self.hints().cloned(),
279        }
280    }
281}
282
283impl fmt::Display for Package {
284    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285        write!(f, "{}", self.summary().package_id())
286    }
287}
288
289impl fmt::Debug for Package {
290    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291        f.debug_struct("Package")
292            .field("id", &self.summary().package_id())
293            .field("..", &"..")
294            .finish()
295    }
296}
297
298impl PartialEq for Package {
299    fn eq(&self, other: &Package) -> bool {
300        self.package_id() == other.package_id()
301    }
302}
303
304impl Eq for Package {}
305
306impl hash::Hash for Package {
307    fn hash<H: hash::Hasher>(&self, into: &mut H) {
308        self.package_id().hash(into)
309    }
310}
311
312/// A set of packages, with the intent to download.
313///
314/// This is primarily used to convert a set of `PackageId`s to `Package`s. It
315/// will download as needed, or used the cached download if available.
316pub struct PackageSet<'gctx> {
317    packages: HashMap<PackageId, OnceCell<Package>>,
318    sources: RefCell<SourceMap<'gctx>>,
319    gctx: &'gctx GlobalContext,
320}
321
322/// Helper for downloading crates.
323pub struct Downloads<'a, 'gctx> {
324    set: &'a PackageSet<'gctx>,
325    /// Progress bar.
326    progress: RefCell<Progress<'gctx>>,
327    /// Flag for keeping track of whether we've printed the Downloading message.
328    first: Cell<bool>,
329    /// Size (in bytes) and package name of the largest downloaded package.
330    largest: Cell<Option<(u64, InternedString)>>,
331    /// Number of downloads that have successfully finished.
332    downloads_finished: Cell<u64>,
333    /// Total bytes for all successfully downloaded packages.
334    downloaded_bytes: Cell<u64>,
335    /// Number of currently pending downloads.
336    pending: Cell<u64>,
337    /// Time when downloading started.
338    start: Instant,
339    /// Global filesystem lock to ensure only one Cargo is downloading one at a time.
340    _lock: CacheLock<'gctx>,
341}
342
343impl<'a, 'gctx> Downloads<'a, 'gctx> {
344    pub async fn download(
345        set: &'a PackageSet<'gctx>,
346        ids: impl IntoIterator<Item = PackageId>,
347    ) -> CargoResult<Vec<&'a Package>> {
348        let progress = RefCell::new(Progress::with_style(
349            "Downloading",
350            ProgressStyle::Ratio,
351            set.gctx,
352        ));
353        let dl = Downloads {
354            set,
355            progress,
356            first: Cell::new(true),
357            largest: Cell::new(None),
358            downloads_finished: Cell::new(0),
359            downloaded_bytes: Cell::new(0),
360            pending: Cell::new(0),
361            start: Instant::now(),
362            _lock: set
363                .gctx
364                .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?,
365        };
366        dl.run(ids).await
367    }
368
369    async fn run(&self, ids: impl IntoIterator<Item = PackageId>) -> CargoResult<Vec<&'a Package>> {
370        let mut futures: FuturesUnordered<_> =
371            ids.into_iter().map(|id| self.get_package(id)).collect();
372
373        // Wait for downloads to complete, or the timer to expire.
374        // This ensure that we call the tick function at a fast
375        // enough rate to give the user progress updates.
376        let mut out = Vec::new();
377        loop {
378            futures::select! {
379                pkg = futures.try_next() => {
380                    match pkg? {
381                        Some(pkg) => out.push(pkg),
382                        None => break,
383                    }
384                },
385                _ = futures_timer::Delay::new(Duration::from_millis(200)).fuse() => {
386                    self.tick(WhyTick::DownloadUpdate)?;
387                },
388            }
389        }
390        self.print_summary()?;
391        self.set
392            .gctx
393            .deferred_global_last_use()?
394            .save_no_error(self.set.gctx);
395        Ok(out)
396    }
397
398    /// Get the existing package, or find the URL to download the .crate
399    /// file and start the download.
400    async fn get_package(&self, id: PackageId) -> CargoResult<&'a Package> {
401        let slot = self
402            .set
403            .packages
404            .get(&id)
405            .ok_or_else(|| internal(format!("couldn't find `{}` in package set", id)))?;
406        if let Some(pkg) = slot.get() {
407            return CargoResult::Ok(pkg);
408        }
409        let source = self
410            .set
411            .sources
412            .borrow()
413            .get(id.source_id())
414            .ok_or_else(|| internal(format!("couldn't find source for `{}`", id)))?
415            .clone();
416        let pkg = match source
417            .download(id)
418            .await
419            .context("unable to get packages from source")
420            .with_context(|| format!("failed to download `{}`", id))?
421        {
422            MaybePackage::Ready(package) => CargoResult::Ok(package),
423            MaybePackage::Download {
424                url,
425                descriptor,
426                authorization,
427            } => {
428                let mut r = Retry::new(self.set.gctx)?;
429                let contents = loop {
430                    self.tick(WhyTick::DownloadStarted)?;
431                    self.pending.update(|v| v + 1);
432                    let response = self
433                        .fetch(&url, authorization.as_deref(), &descriptor, &id)
434                        .await;
435                    self.pending.update(|v| v - 1);
436                    match r.r#try(|| response) {
437                        RetryResult::Success(result) => break result,
438                        RetryResult::Err(error) => {
439                            debug!(target: "network", "final failure for {url}");
440                            return Err(error);
441                        }
442                        RetryResult::Retry(delay_ms) => {
443                            debug!(target: "network", "download retry {url} for {delay_ms}ms");
444                            futures_timer::Delay::new(Duration::from_millis(delay_ms)).await;
445                        }
446                    }
447                };
448                self.downloads_finished.update(|v| v + 1);
449                self.downloaded_bytes.update(|v| v + contents.len() as u64);
450
451                // We're about to synchronously extract the crate below. While we're
452                // doing that our download progress won't actually be updated, nor do we
453                // have a great view into the progress of the extraction. Let's prepare
454                // the user for this CPU-heavy step if it looks like it'll take some
455                // time to do so.
456                let kib_400 = 1024 * 400;
457                if contents.len() < kib_400 {
458                    self.tick(WhyTick::DownloadFinished)?;
459                } else {
460                    self.tick(WhyTick::Extracting(&id.name()))?;
461                }
462
463                Ok(source.finish_download(id, contents).await?)
464            }
465        }?;
466
467        assert!(slot.set(pkg).is_ok());
468        Ok(slot.get().unwrap())
469    }
470
471    /// Perform the request to download the .crate file.
472    async fn fetch(
473        &self,
474        url: &str,
475        authorization: Option<&str>,
476        descriptor: &str,
477        id: &PackageId,
478    ) -> CargoResult<Vec<u8>> {
479        // http::Uri doesn't support file urls without an authority, even though it's optional.
480        // so we insert localhost here to make it work.
481        let mut request = if let Some(file_url) = url.strip_prefix("file:///") {
482            Request::get(format!("file://localhost/{file_url}"))
483        } else {
484            Request::get(url)
485        };
486        if let Some(authorization) = authorization {
487            request = request.header(http::header::AUTHORIZATION, authorization);
488        }
489        let client = self
490            .set
491            .gctx
492            .http_async()
493            .with_context(|| format!("failed to download `{}`", id))?;
494
495        // If the progress bar isn't enabled then it may be awhile before the
496        // first crate finishes downloading so we inform immediately that we're
497        // downloading crates here.
498        if self.first.get() && !self.progress.borrow().is_enabled() {
499            self.first.set(false);
500            self.set.gctx.shell().status("Downloading", "crates ...")?;
501        }
502
503        let response = client
504            .request(request.body(Vec::new())?)
505            .await
506            .with_context(|| format!("failed to download from `{}`", url))?;
507
508        let previous_largest = self.largest.get().map(|(v, _)| v).unwrap_or_default();
509        let len = response.body().len() as u64;
510        if len > previous_largest {
511            self.largest.set(Some((len, id.name())));
512        }
513
514        if response.status() != http::StatusCode::OK {
515            return Err(HttpNotSuccessful::new_from_response(response, &url))
516                .with_context(|| format!("failed to download from `{}`", url))?;
517        }
518        // If the progress bar isn't enabled then we still want to provide some
519        // semblance of progress of how we're downloading crates, and if the
520        // progress bar is enabled this provides a good log of what's happening.
521        // progress.clear();
522        self.set.gctx.shell().status("Downloaded", descriptor)?;
523
524        Ok(response.into_body())
525    }
526
527    fn tick(&self, why: WhyTick<'_>) -> CargoResult<()> {
528        let mut progress = self.progress.borrow_mut();
529
530        if let WhyTick::DownloadUpdate = why {
531            if !progress.update_allowed() {
532                return Ok(());
533            }
534        }
535
536        let pending = self.pending.get();
537        let mut msg = if pending == 1 {
538            format!("{} crate", pending)
539        } else {
540            format!("{} crates", pending)
541        };
542        match why {
543            WhyTick::Extracting(krate) => {
544                msg.push_str(&format!(", extracting {} ...", krate));
545            }
546            _ => {
547                let remaining = self
548                    .set
549                    .gctx
550                    .http_async()
551                    .map(|c| c.bytes_pending())
552                    .unwrap_or_default();
553                if remaining > 0 {
554                    msg.push_str(&format!(
555                        ", remaining bytes: {:.1}",
556                        HumanBytes(remaining as u64)
557                    ));
558                }
559            }
560        }
561        progress.print_now(&msg)
562    }
563
564    fn print_summary(&self) -> CargoResult<()> {
565        // Don't print a download summary if we're not using a progress bar,
566        // we've already printed lots of `Downloading...` items.
567        if !self.progress.borrow().is_enabled() {
568            return Ok(());
569        }
570        let downloads_finished = self.downloads_finished.get();
571
572        // If we didn't download anything, no need for a summary.
573        if downloads_finished == 0 {
574            return Ok(());
575        }
576
577        // pick the correct plural of crate(s)
578        let crate_string = if downloads_finished == 1 {
579            "crate"
580        } else {
581            "crates"
582        };
583        let mut status = format!(
584            "{downloads_finished} {crate_string} ({:.1}) in {}",
585            HumanBytes(self.downloaded_bytes.get()),
586            util::elapsed(self.start.elapsed())
587        );
588        // print the size of largest crate if it was >1mb
589        // however don't print if only a single crate was downloaded
590        // because it is obvious that it will be the largest then
591        if let Some(largest) = self.largest.get() {
592            let mib_1 = 1024 * 1024;
593            if largest.0 > mib_1 && downloads_finished > 1 {
594                status.push_str(&format!(
595                    " (largest was `{}` at {:.1})",
596                    largest.1,
597                    HumanBytes(largest.0),
598                ));
599            }
600        }
601
602        // Clear progress before displaying final summary.
603        self.progress.borrow_mut().clear();
604        self.set.gctx.shell().status("Downloaded", status)?;
605        Ok(())
606    }
607}
608
609impl<'gctx> PackageSet<'gctx> {
610    pub fn new(
611        package_ids: &[PackageId],
612        sources: SourceMap<'gctx>,
613        gctx: &'gctx GlobalContext,
614    ) -> CargoResult<PackageSet<'gctx>> {
615        gctx.http_config()?;
616
617        Ok(PackageSet {
618            packages: package_ids
619                .iter()
620                .map(|&id| (id, OnceCell::new()))
621                .collect(),
622            sources: RefCell::new(sources),
623            gctx,
624        })
625    }
626
627    pub fn package_ids(&self) -> impl Iterator<Item = PackageId> + '_ {
628        self.packages.keys().cloned()
629    }
630
631    pub fn packages(&self) -> impl Iterator<Item = &Package> {
632        self.packages.values().filter_map(|p| p.get())
633    }
634
635    pub fn get_one(&self, id: PackageId) -> CargoResult<&Package> {
636        if let Some(pkg) = self.packages.get(&id).and_then(|slot| slot.get()) {
637            return Ok(pkg);
638        }
639        Ok(self.get_many(Some(id))?.remove(0))
640    }
641
642    pub fn get_many(&self, ids: impl IntoIterator<Item = PackageId>) -> CargoResult<Vec<&Package>> {
643        return crate::util::block_on(Downloads::download(self, ids));
644    }
645
646    /// Downloads any packages accessible from the give root ids.
647    #[tracing::instrument(skip_all)]
648    pub fn download_accessible(
649        &self,
650        resolve: &Resolve,
651        root_ids: &[PackageId],
652        has_dev_units: HasDevUnits,
653        requested_kinds: &[CompileKind],
654        target_data: &RustcTargetData<'gctx>,
655        force_all_targets: ForceAllTargets,
656    ) -> CargoResult<()> {
657        fn collect_used_deps(
658            used: &mut BTreeSet<(PackageId, CompileKind)>,
659            resolve: &Resolve,
660            pkg_id: PackageId,
661            has_dev_units: HasDevUnits,
662            requested_kind: CompileKind,
663            target_data: &RustcTargetData<'_>,
664            force_all_targets: ForceAllTargets,
665        ) -> CargoResult<()> {
666            if !used.insert((pkg_id, requested_kind)) {
667                return Ok(());
668            }
669            let requested_kinds = &[requested_kind];
670            let filtered_deps = PackageSet::filter_deps(
671                pkg_id,
672                resolve,
673                has_dev_units,
674                requested_kinds,
675                target_data,
676                force_all_targets,
677            );
678            for (pkg_id, deps) in filtered_deps {
679                collect_used_deps(
680                    used,
681                    resolve,
682                    pkg_id,
683                    has_dev_units,
684                    requested_kind,
685                    target_data,
686                    force_all_targets,
687                )?;
688                let artifact_kinds = deps.iter().filter_map(|dep| {
689                    Some(
690                        dep.artifact()?
691                            .target()?
692                            .to_resolved_compile_kind(*requested_kinds.iter().next().unwrap()),
693                    )
694                });
695                for artifact_kind in artifact_kinds {
696                    collect_used_deps(
697                        used,
698                        resolve,
699                        pkg_id,
700                        has_dev_units,
701                        artifact_kind,
702                        target_data,
703                        force_all_targets,
704                    )?;
705                }
706            }
707            Ok(())
708        }
709
710        // This is sorted by PackageId to get consistent behavior and error
711        // messages for Cargo's testsuite. Perhaps there is a better ordering
712        // that optimizes download time?
713        let mut to_download = BTreeSet::new();
714
715        for id in root_ids {
716            for requested_kind in requested_kinds {
717                collect_used_deps(
718                    &mut to_download,
719                    resolve,
720                    *id,
721                    has_dev_units,
722                    *requested_kind,
723                    target_data,
724                    force_all_targets,
725                )?;
726            }
727        }
728        let to_download = to_download
729            .into_iter()
730            .map(|(p, _)| p)
731            .collect::<BTreeSet<_>>();
732        self.get_many(to_download.into_iter())?;
733        Ok(())
734    }
735
736    /// Check if there are any dependency packages that violate artifact constraints
737    /// to instantly abort, or that do not have any libs which results in warnings.
738    pub(crate) fn warn_no_lib_packages_and_artifact_libs_overlapping_deps(
739        &self,
740        ws: &Workspace<'gctx>,
741        resolve: &Resolve,
742        root_ids: &[PackageId],
743        has_dev_units: HasDevUnits,
744        requested_kinds: &[CompileKind],
745        target_data: &RustcTargetData<'_>,
746        force_all_targets: ForceAllTargets,
747    ) -> CargoResult<()> {
748        let no_lib_pkgs: BTreeMap<PackageId, Vec<(&Package, &HashSet<Dependency>)>> = root_ids
749            .iter()
750            .map(|&root_id| {
751                let dep_pkgs_to_deps: Vec<_> = PackageSet::filter_deps(
752                    root_id,
753                    resolve,
754                    has_dev_units,
755                    requested_kinds,
756                    target_data,
757                    force_all_targets,
758                )
759                .collect();
760
761                let dep_pkgs_and_deps = dep_pkgs_to_deps
762                    .into_iter()
763                    .filter(|(_id, deps)| deps.iter().any(|dep| dep.maybe_lib()))
764                    .filter_map(|(dep_package_id, deps)| {
765                        self.get_one(dep_package_id).ok().and_then(|dep_pkg| {
766                            (!dep_pkg.targets().iter().any(|t| t.is_lib())).then(|| (dep_pkg, deps))
767                        })
768                    })
769                    .collect();
770                (root_id, dep_pkgs_and_deps)
771            })
772            .collect();
773
774        for (pkg_id, dep_pkgs) in no_lib_pkgs {
775            for (_dep_pkg_without_lib_target, deps) in dep_pkgs {
776                for dep in deps.iter().filter(|dep| {
777                    dep.artifact()
778                        .map(|artifact| artifact.is_lib())
779                        .unwrap_or(true)
780                }) {
781                    ws.gctx().shell().warn(&format!(
782                        "{} ignoring invalid dependency `{}` which is missing a lib target",
783                        pkg_id,
784                        dep.name_in_toml(),
785                    ))?;
786                }
787            }
788        }
789        Ok(())
790    }
791
792    pub fn filter_deps<'a>(
793        pkg_id: PackageId,
794        resolve: &'a Resolve,
795        has_dev_units: HasDevUnits,
796        requested_kinds: &'a [CompileKind],
797        target_data: &'a RustcTargetData<'_>,
798        force_all_targets: ForceAllTargets,
799    ) -> impl Iterator<Item = (PackageId, &'a HashSet<Dependency>)> + 'a {
800        resolve
801            .deps(pkg_id)
802            .filter(move |&(_id, deps)| {
803                deps.iter().any(|dep| {
804                    if dep.kind() == DepKind::Development && has_dev_units == HasDevUnits::No {
805                        return false;
806                    }
807                    if force_all_targets == ForceAllTargets::No {
808                        let activated = requested_kinds
809                            .iter()
810                            .chain(Some(&CompileKind::Host))
811                            .any(|kind| target_data.dep_platform_activated(dep, *kind));
812                        if !activated {
813                            return false;
814                        }
815                    }
816                    true
817                })
818            })
819            .into_iter()
820    }
821
822    pub fn sources(&self) -> Ref<'_, SourceMap<'gctx>> {
823        self.sources.borrow()
824    }
825
826    /// Merge the given set into self.
827    pub fn add_set(&mut self, set: PackageSet<'gctx>) {
828        for (pkg_id, p_cell) in set.packages {
829            self.packages.entry(pkg_id).or_insert(p_cell);
830        }
831        let mut sources = self.sources.borrow_mut();
832        let other_sources = set.sources.into_inner();
833        sources.add_source_map(other_sources);
834    }
835}
836
837#[derive(Copy, Clone)]
838enum WhyTick<'a> {
839    DownloadStarted,
840    DownloadUpdate,
841    DownloadFinished,
842    Extracting(&'a str),
843}