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#[derive(Clone)]
42pub struct Package {
43 inner: Rc<PackageInner>,
44}
45
46#[derive(Clone)]
47struct PackageInner {
49 manifest: Manifest,
51 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#[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 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 pub fn dependencies(&self) -> &[Dependency] {
113 self.manifest().dependencies()
114 }
115 pub fn manifest(&self) -> &Manifest {
117 &self.inner.manifest
118 }
119 pub fn manifest_mut(&mut self) -> &mut Manifest {
121 &mut Rc::make_mut(&mut self.inner).manifest
122 }
123 pub fn manifest_path(&self) -> &Path {
125 &self.inner.manifest_path
126 }
127 pub fn name(&self) -> InternedString {
129 self.package_id().name()
130 }
131 pub fn package_id(&self) -> PackageId {
133 self.manifest().package_id()
134 }
135 pub fn root(&self) -> &Path {
137 self.manifest_path().parent().unwrap()
138 }
139 pub fn summary(&self) -> &Summary {
141 self.manifest().summary()
142 }
143 pub fn targets(&self) -> &[Target] {
145 self.manifest().targets()
146 }
147 pub fn library(&self) -> Option<&Target> {
149 self.targets().iter().find(|t| t.is_lib())
150 }
151 pub fn version(&self) -> &Version {
153 self.package_id().version()
154 }
155 pub fn authors(&self) -> &Vec<String> {
157 &self.manifest().metadata().authors
158 }
159
160 pub fn publish(&self) -> &Option<Vec<String>> {
164 self.manifest().publish()
165 }
166 pub fn proc_macro(&self) -> bool {
168 self.targets().iter().any(|target| target.proc_macro())
169 }
170 pub fn rust_version(&self) -> Option<&RustVersion> {
172 self.manifest().rust_version()
173 }
174
175 pub fn hints(&self) -> Option<&Hints> {
177 self.manifest().hints()
178 }
179
180 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 let targets: Vec<Target> = self
206 .manifest()
207 .targets()
208 .iter()
209 .filter(|t| t.src_path().is_path())
210 .cloned()
211 .collect();
212 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
283pub struct PackageSet<'gctx> {
288 packages: HashMap<PackageId, OnceCell<Package>>,
289 sources: RefCell<SourceMap<'gctx>>,
290 gctx: &'gctx GlobalContext,
291}
292
293pub struct Downloads<'a, 'gctx> {
295 set: &'a PackageSet<'gctx>,
296 progress: RefCell<Progress<'gctx>>,
298 first: Cell<bool>,
300 largest: Cell<Option<(u64, InternedString)>>,
302 downloads_finished: Cell<u64>,
304 downloaded_bytes: Cell<u64>,
306 pending: Cell<u64>,
308 start: Instant,
310 _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 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 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 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 async fn fetch(
444 &self,
445 url: &str,
446 authorization: Option<&str>,
447 descriptor: &str,
448 id: &PackageId,
449 ) -> CargoResult<Vec<u8>> {
450 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 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 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 if !self.progress.borrow().is_enabled() {
539 return Ok(());
540 }
541 let downloads_finished = self.downloads_finished.get();
542
543 if downloads_finished == 0 {
545 return Ok(());
546 }
547
548 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 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 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 #[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 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 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 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}