Skip to main content

cargo/core/
package.rs

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