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#[derive(Clone)]
43pub struct Package {
44 inner: Rc<PackageInner>,
45}
46
47#[derive(Clone)]
48struct PackageInner {
50 manifest: Manifest,
52 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#[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 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 pub fn dependencies(&self) -> &[Dependency] {
114 self.manifest().dependencies()
115 }
116 pub fn manifest(&self) -> &Manifest {
118 &self.inner.manifest
119 }
120 pub fn manifest_mut(&mut self) -> &mut Manifest {
122 &mut Rc::make_mut(&mut self.inner).manifest
123 }
124 pub fn manifest_path(&self) -> &Path {
126 &self.inner.manifest_path
127 }
128 pub fn name(&self) -> InternedString {
130 self.package_id().name()
131 }
132 pub fn package_id(&self) -> PackageId {
134 self.manifest().package_id()
135 }
136 pub fn root(&self) -> &Path {
138 self.manifest_path().parent().unwrap()
139 }
140 pub fn summary(&self) -> &Summary {
142 self.manifest().summary()
143 }
144 pub fn targets(&self) -> &[Target] {
146 self.manifest().targets()
147 }
148 pub fn library(&self) -> Option<&Target> {
150 self.targets().iter().find(|t| t.is_lib())
151 }
152 pub fn version(&self) -> &Version {
154 self.package_id().version()
155 }
156 pub fn authors(&self) -> &Vec<String> {
158 &self.manifest().metadata().authors
159 }
160
161 pub fn publish(&self) -> &Option<Vec<String>> {
165 self.manifest().publish()
166 }
167 pub fn proc_macro(&self) -> bool {
169 self.targets().iter().any(|target| target.proc_macro())
170 }
171 pub fn rust_version(&self) -> Option<&RustVersion> {
173 self.manifest().rust_version()
174 }
175
176 pub fn hints(&self) -> Option<&Hints> {
178 self.manifest().hints()
179 }
180
181 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 let targets: Vec<Target> = self
207 .manifest()
208 .targets()
209 .iter()
210 .filter(|t| t.src_path().is_path())
211 .cloned()
212 .collect();
213 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
284pub struct PackageSet<'gctx> {
289 packages: HashMap<PackageId, OnceCell<Package>>,
290 sources: RefCell<SourceMap<'gctx>>,
291 gctx: &'gctx GlobalContext,
292}
293
294pub struct Downloads<'a, 'gctx> {
296 set: &'a PackageSet<'gctx>,
297 progress: RefCell<Progress<'gctx>>,
299 first: Cell<bool>,
301 largest: Cell<Option<(u64, InternedString)>>,
303 downloads_finished: Cell<u64>,
305 downloaded_bytes: Cell<u64>,
307 pending: Cell<u64>,
309 start: Instant,
311 _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 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 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 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 async fn fetch(
445 &self,
446 url: &str,
447 authorization: Option<&str>,
448 descriptor: &str,
449 id: &PackageId,
450 ) -> CargoResult<Vec<u8>> {
451 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 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 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 if !self.progress.borrow().is_enabled() {
540 return Ok(());
541 }
542 let downloads_finished = self.downloads_finished.get();
543
544 if downloads_finished == 0 {
546 return Ok(());
547 }
548
549 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 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 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 #[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 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 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 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}