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