]> git.proxmox.com Git - cargo.git/blob - src/cargo/core/package.rs
Stabilize namespaced and weak dependency features.
[cargo.git] / src / cargo / core / package.rs
1 use std::cell::{Cell, Ref, RefCell, RefMut};
2 use std::cmp::Ordering;
3 use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
4 use std::fmt;
5 use std::hash;
6 use std::mem;
7 use std::path::{Path, PathBuf};
8 use std::rc::Rc;
9 use std::time::{Duration, Instant};
10
11 use anyhow::Context;
12 use bytesize::ByteSize;
13 use curl::easy::{Easy, HttpVersion};
14 use curl::multi::{EasyHandle, Multi};
15 use lazycell::LazyCell;
16 use log::{debug, warn};
17 use semver::Version;
18 use serde::Serialize;
19
20 use crate::core::compiler::{CompileKind, RustcTargetData};
21 use crate::core::dependency::DepKind;
22 use crate::core::resolver::features::ForceAllTargets;
23 use crate::core::resolver::{HasDevUnits, Resolve};
24 use crate::core::source::MaybePackage;
25 use crate::core::{Dependency, Manifest, PackageId, SourceId, Target};
26 use crate::core::{SourceMap, Summary, Workspace};
27 use crate::ops;
28 use crate::util::config::PackageCacheLock;
29 use crate::util::errors::{CargoResult, HttpNot200};
30 use crate::util::interning::InternedString;
31 use crate::util::network::Retry;
32 use crate::util::{self, internal, Config, Progress, ProgressStyle};
33
34 pub const MANIFEST_PREAMBLE: &str = "\
35 # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
36 #
37 # When uploading crates to the registry Cargo will automatically
38 # \"normalize\" Cargo.toml files for maximal compatibility
39 # with all versions of Cargo and also rewrite `path` dependencies
40 # to registry (e.g., crates.io) dependencies.
41 #
42 # If you are reading this file be aware that the original Cargo.toml
43 # will likely look very different (and much more reasonable).
44 # See Cargo.toml.orig for the original contents.
45 ";
46
47 /// Information about a package that is available somewhere in the file system.
48 ///
49 /// A package is a `Cargo.toml` file plus all the files that are part of it.
50 //
51 // TODO: is `manifest_path` a relic?
52 #[derive(Clone)]
53 pub struct Package {
54 inner: Rc<PackageInner>,
55 }
56
57 #[derive(Clone)]
58 struct PackageInner {
59 /// The package's manifest.
60 manifest: Manifest,
61 /// The root of the package.
62 manifest_path: PathBuf,
63 }
64
65 impl Ord for Package {
66 fn cmp(&self, other: &Package) -> Ordering {
67 self.package_id().cmp(&other.package_id())
68 }
69 }
70
71 impl PartialOrd for Package {
72 fn partial_cmp(&self, other: &Package) -> Option<Ordering> {
73 Some(self.cmp(other))
74 }
75 }
76
77 /// A Package in a form where `Serialize` can be derived.
78 #[derive(Serialize)]
79 pub struct SerializedPackage {
80 name: InternedString,
81 version: Version,
82 id: PackageId,
83 license: Option<String>,
84 license_file: Option<String>,
85 description: Option<String>,
86 source: SourceId,
87 dependencies: Vec<Dependency>,
88 targets: Vec<Target>,
89 features: BTreeMap<InternedString, Vec<InternedString>>,
90 manifest_path: PathBuf,
91 metadata: Option<toml::Value>,
92 publish: Option<Vec<String>>,
93 authors: Vec<String>,
94 categories: Vec<String>,
95 keywords: Vec<String>,
96 readme: Option<String>,
97 repository: Option<String>,
98 homepage: Option<String>,
99 documentation: Option<String>,
100 edition: String,
101 links: Option<String>,
102 #[serde(skip_serializing_if = "Option::is_none")]
103 metabuild: Option<Vec<String>>,
104 default_run: Option<String>,
105 rust_version: Option<String>,
106 }
107
108 impl Package {
109 /// Creates a package from a manifest and its location.
110 pub fn new(manifest: Manifest, manifest_path: &Path) -> Package {
111 Package {
112 inner: Rc::new(PackageInner {
113 manifest,
114 manifest_path: manifest_path.to_path_buf(),
115 }),
116 }
117 }
118
119 /// Gets the manifest dependencies.
120 pub fn dependencies(&self) -> &[Dependency] {
121 self.manifest().dependencies()
122 }
123 /// Gets the manifest.
124 pub fn manifest(&self) -> &Manifest {
125 &self.inner.manifest
126 }
127 /// Gets the manifest.
128 pub fn manifest_mut(&mut self) -> &mut Manifest {
129 &mut Rc::make_mut(&mut self.inner).manifest
130 }
131 /// Gets the path to the manifest.
132 pub fn manifest_path(&self) -> &Path {
133 &self.inner.manifest_path
134 }
135 /// Gets the name of the package.
136 pub fn name(&self) -> InternedString {
137 self.package_id().name()
138 }
139 /// Gets the `PackageId` object for the package (fully defines a package).
140 pub fn package_id(&self) -> PackageId {
141 self.manifest().package_id()
142 }
143 /// Gets the root folder of the package.
144 pub fn root(&self) -> &Path {
145 self.manifest_path().parent().unwrap()
146 }
147 /// Gets the summary for the package.
148 pub fn summary(&self) -> &Summary {
149 self.manifest().summary()
150 }
151 /// Gets the targets specified in the manifest.
152 pub fn targets(&self) -> &[Target] {
153 self.manifest().targets()
154 }
155 /// Gets the library crate for this package, if it exists.
156 pub fn library(&self) -> Option<&Target> {
157 self.targets().iter().find(|t| t.is_lib())
158 }
159 /// Gets the current package version.
160 pub fn version(&self) -> &Version {
161 self.package_id().version()
162 }
163 /// Gets the package authors.
164 pub fn authors(&self) -> &Vec<String> {
165 &self.manifest().metadata().authors
166 }
167
168 /// Returns `None` if the package is set to publish.
169 /// Returns `Some(allowed_registries)` if publishing is limited to specified
170 /// registries or if package is set to not publish.
171 pub fn publish(&self) -> &Option<Vec<String>> {
172 self.manifest().publish()
173 }
174 /// Returns `true` if this package is a proc-macro.
175 pub fn proc_macro(&self) -> bool {
176 self.targets().iter().any(|target| target.proc_macro())
177 }
178 /// Gets the package's minimum Rust version.
179 pub fn rust_version(&self) -> Option<&str> {
180 self.manifest().rust_version()
181 }
182
183 /// Returns `true` if the package uses a custom build script for any target.
184 pub fn has_custom_build(&self) -> bool {
185 self.targets().iter().any(|t| t.is_custom_build())
186 }
187
188 pub fn map_source(self, to_replace: SourceId, replace_with: SourceId) -> Package {
189 Package {
190 inner: Rc::new(PackageInner {
191 manifest: self.manifest().clone().map_source(to_replace, replace_with),
192 manifest_path: self.manifest_path().to_owned(),
193 }),
194 }
195 }
196
197 pub fn to_registry_toml(&self, ws: &Workspace<'_>) -> CargoResult<String> {
198 let manifest = self
199 .manifest()
200 .original()
201 .prepare_for_publish(ws, self.root())?;
202 let toml = toml::to_string(&manifest)?;
203 Ok(format!("{}\n{}", MANIFEST_PREAMBLE, toml))
204 }
205
206 /// Returns if package should include `Cargo.lock`.
207 pub fn include_lockfile(&self) -> bool {
208 self.targets().iter().any(|t| t.is_example() || t.is_bin())
209 }
210
211 pub fn serialized(&self) -> SerializedPackage {
212 let summary = self.manifest().summary();
213 let package_id = summary.package_id();
214 let manmeta = self.manifest().metadata();
215 // Filter out metabuild targets. They are an internal implementation
216 // detail that is probably not relevant externally. There's also not a
217 // real path to show in `src_path`, and this avoids changing the format.
218 let targets: Vec<Target> = self
219 .manifest()
220 .targets()
221 .iter()
222 .filter(|t| t.src_path().is_path())
223 .cloned()
224 .collect();
225 // Convert Vec<FeatureValue> to Vec<InternedString>
226 let features = summary
227 .features()
228 .iter()
229 .map(|(k, v)| {
230 (
231 *k,
232 v.iter()
233 .map(|fv| InternedString::new(&fv.to_string()))
234 .collect(),
235 )
236 })
237 .collect();
238
239 SerializedPackage {
240 name: package_id.name(),
241 version: package_id.version().clone(),
242 id: package_id,
243 license: manmeta.license.clone(),
244 license_file: manmeta.license_file.clone(),
245 description: manmeta.description.clone(),
246 source: summary.source_id(),
247 dependencies: summary.dependencies().to_vec(),
248 targets,
249 features,
250 manifest_path: self.manifest_path().to_path_buf(),
251 metadata: self.manifest().custom_metadata().cloned(),
252 authors: manmeta.authors.clone(),
253 categories: manmeta.categories.clone(),
254 keywords: manmeta.keywords.clone(),
255 readme: manmeta.readme.clone(),
256 repository: manmeta.repository.clone(),
257 homepage: manmeta.homepage.clone(),
258 documentation: manmeta.documentation.clone(),
259 edition: self.manifest().edition().to_string(),
260 links: self.manifest().links().map(|s| s.to_owned()),
261 metabuild: self.manifest().metabuild().cloned(),
262 publish: self.publish().as_ref().cloned(),
263 default_run: self.manifest().default_run().map(|s| s.to_owned()),
264 rust_version: self.rust_version().map(|s| s.to_owned()),
265 }
266 }
267 }
268
269 impl fmt::Display for Package {
270 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
271 write!(f, "{}", self.summary().package_id())
272 }
273 }
274
275 impl fmt::Debug for Package {
276 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277 f.debug_struct("Package")
278 .field("id", &self.summary().package_id())
279 .field("..", &"..")
280 .finish()
281 }
282 }
283
284 impl PartialEq for Package {
285 fn eq(&self, other: &Package) -> bool {
286 self.package_id() == other.package_id()
287 }
288 }
289
290 impl Eq for Package {}
291
292 impl hash::Hash for Package {
293 fn hash<H: hash::Hasher>(&self, into: &mut H) {
294 self.package_id().hash(into)
295 }
296 }
297
298 /// A set of packages, with the intent to download.
299 ///
300 /// This is primarily used to convert a set of `PackageId`s to `Package`s. It
301 /// will download as needed, or used the cached download if available.
302 pub struct PackageSet<'cfg> {
303 packages: HashMap<PackageId, LazyCell<Package>>,
304 sources: RefCell<SourceMap<'cfg>>,
305 config: &'cfg Config,
306 multi: Multi,
307 /// Used to prevent reusing the PackageSet to download twice.
308 downloading: Cell<bool>,
309 /// Whether or not to use curl HTTP/2 multiplexing.
310 multiplexing: bool,
311 }
312
313 /// Helper for downloading crates.
314 pub struct Downloads<'a, 'cfg> {
315 set: &'a PackageSet<'cfg>,
316 /// When a download is started, it is added to this map. The key is a
317 /// "token" (see `Download::token`). It is removed once the download is
318 /// finished.
319 pending: HashMap<usize, (Download<'cfg>, EasyHandle)>,
320 /// Set of packages currently being downloaded. This should stay in sync
321 /// with `pending`.
322 pending_ids: HashSet<PackageId>,
323 /// The final result of each download. A pair `(token, result)`. This is a
324 /// temporary holding area, needed because curl can report multiple
325 /// downloads at once, but the main loop (`wait`) is written to only
326 /// handle one at a time.
327 results: Vec<(usize, Result<(), curl::Error>)>,
328 /// The next ID to use for creating a token (see `Download::token`).
329 next: usize,
330 /// Progress bar.
331 progress: RefCell<Option<Progress<'cfg>>>,
332 /// Number of downloads that have successfully finished.
333 downloads_finished: usize,
334 /// Total bytes for all successfully downloaded packages.
335 downloaded_bytes: u64,
336 /// Size (in bytes) and package name of the largest downloaded package.
337 largest: (u64, String),
338 /// Time when downloading started.
339 start: Instant,
340 /// Indicates *all* downloads were successful.
341 success: bool,
342
343 /// Timeout management, both of timeout thresholds as well as whether or not
344 /// our connection has timed out (and accompanying message if it has).
345 ///
346 /// Note that timeout management is done manually here instead of in libcurl
347 /// because we want to apply timeouts to an entire batch of operations, not
348 /// any one particular single operation.
349 timeout: ops::HttpTimeout,
350 /// Last time bytes were received.
351 updated_at: Cell<Instant>,
352 /// This is a slow-speed check. It is reset to `now + timeout_duration`
353 /// every time at least `threshold` bytes are received. If the current
354 /// time ever exceeds `next_speed_check`, then give up and report a
355 /// timeout error.
356 next_speed_check: Cell<Instant>,
357 /// This is the slow-speed threshold byte count. It starts at the
358 /// configured threshold value (default 10), and is decremented by the
359 /// number of bytes received in each chunk. If it is <= zero, the
360 /// threshold has been met and data is being received fast enough not to
361 /// trigger a timeout; reset `next_speed_check` and set this back to the
362 /// configured threshold.
363 next_speed_check_bytes_threshold: Cell<u64>,
364 /// Global filesystem lock to ensure only one Cargo is downloading at a
365 /// time.
366 _lock: PackageCacheLock<'cfg>,
367 }
368
369 struct Download<'cfg> {
370 /// The token for this download, used as the key of the `Downloads::pending` map
371 /// and stored in `EasyHandle` as well.
372 token: usize,
373
374 /// The package that we're downloading.
375 id: PackageId,
376
377 /// Actual downloaded data, updated throughout the lifetime of this download.
378 data: RefCell<Vec<u8>>,
379
380 /// The URL that we're downloading from, cached here for error messages and
381 /// reenqueuing.
382 url: String,
383
384 /// A descriptive string to print when we've finished downloading this crate.
385 descriptor: String,
386
387 /// Statistics updated from the progress callback in libcurl.
388 total: Cell<u64>,
389 current: Cell<u64>,
390
391 /// The moment we started this transfer at.
392 start: Instant,
393 timed_out: Cell<Option<String>>,
394
395 /// Logic used to track retrying this download if it's a spurious failure.
396 retry: Retry<'cfg>,
397 }
398
399 impl<'cfg> PackageSet<'cfg> {
400 pub fn new(
401 package_ids: &[PackageId],
402 sources: SourceMap<'cfg>,
403 config: &'cfg Config,
404 ) -> CargoResult<PackageSet<'cfg>> {
405 // We've enabled the `http2` feature of `curl` in Cargo, so treat
406 // failures here as fatal as it would indicate a build-time problem.
407 //
408 // Note that the multiplexing support is pretty new so we're having it
409 // off-by-default temporarily.
410 //
411 // Also note that pipelining is disabled as curl authors have indicated
412 // that it's buggy, and we've empirically seen that it's buggy with HTTP
413 // proxies.
414 let mut multi = Multi::new();
415 let multiplexing = config.http_config()?.multiplexing.unwrap_or(true);
416 multi
417 .pipelining(false, multiplexing)
418 .with_context(|| "failed to enable multiplexing/pipelining in curl")?;
419
420 // let's not flood crates.io with connections
421 multi.set_max_host_connections(2)?;
422
423 Ok(PackageSet {
424 packages: package_ids
425 .iter()
426 .map(|&id| (id, LazyCell::new()))
427 .collect(),
428 sources: RefCell::new(sources),
429 config,
430 multi,
431 downloading: Cell::new(false),
432 multiplexing,
433 })
434 }
435
436 pub fn package_ids(&self) -> impl Iterator<Item = PackageId> + '_ {
437 self.packages.keys().cloned()
438 }
439
440 pub fn packages(&self) -> impl Iterator<Item = &Package> {
441 self.packages.values().filter_map(|p| p.borrow())
442 }
443
444 pub fn enable_download<'a>(&'a self) -> CargoResult<Downloads<'a, 'cfg>> {
445 assert!(!self.downloading.replace(true));
446 let timeout = ops::HttpTimeout::new(self.config)?;
447 Ok(Downloads {
448 start: Instant::now(),
449 set: self,
450 next: 0,
451 pending: HashMap::new(),
452 pending_ids: HashSet::new(),
453 results: Vec::new(),
454 progress: RefCell::new(Some(Progress::with_style(
455 "Downloading",
456 ProgressStyle::Ratio,
457 self.config,
458 ))),
459 downloads_finished: 0,
460 downloaded_bytes: 0,
461 largest: (0, String::new()),
462 success: false,
463 updated_at: Cell::new(Instant::now()),
464 timeout,
465 next_speed_check: Cell::new(Instant::now()),
466 next_speed_check_bytes_threshold: Cell::new(0),
467 _lock: self.config.acquire_package_cache_lock()?,
468 })
469 }
470
471 pub fn get_one(&self, id: PackageId) -> CargoResult<&Package> {
472 if let Some(pkg) = self.packages.get(&id).and_then(|slot| slot.borrow()) {
473 return Ok(pkg);
474 }
475 Ok(self.get_many(Some(id))?.remove(0))
476 }
477
478 pub fn get_many(&self, ids: impl IntoIterator<Item = PackageId>) -> CargoResult<Vec<&Package>> {
479 let mut pkgs = Vec::new();
480 let mut downloads = self.enable_download()?;
481 for id in ids {
482 pkgs.extend(downloads.start(id)?);
483 }
484 while downloads.remaining() > 0 {
485 pkgs.push(downloads.wait()?);
486 }
487 downloads.success = true;
488 Ok(pkgs)
489 }
490
491 /// Downloads any packages accessible from the give root ids.
492 pub fn download_accessible(
493 &self,
494 resolve: &Resolve,
495 root_ids: &[PackageId],
496 has_dev_units: HasDevUnits,
497 requested_kinds: &[CompileKind],
498 target_data: &RustcTargetData<'cfg>,
499 force_all_targets: ForceAllTargets,
500 ) -> CargoResult<()> {
501 fn collect_used_deps(
502 used: &mut BTreeSet<PackageId>,
503 resolve: &Resolve,
504 pkg_id: PackageId,
505 has_dev_units: HasDevUnits,
506 requested_kinds: &[CompileKind],
507 target_data: &RustcTargetData<'_>,
508 force_all_targets: ForceAllTargets,
509 ) -> CargoResult<()> {
510 if !used.insert(pkg_id) {
511 return Ok(());
512 }
513 let filtered_deps = PackageSet::filter_deps(
514 pkg_id,
515 resolve,
516 has_dev_units,
517 requested_kinds,
518 target_data,
519 force_all_targets,
520 );
521 for pkg_id in filtered_deps {
522 collect_used_deps(
523 used,
524 resolve,
525 pkg_id,
526 has_dev_units,
527 requested_kinds,
528 target_data,
529 force_all_targets,
530 )?;
531 }
532 Ok(())
533 }
534
535 // This is sorted by PackageId to get consistent behavior and error
536 // messages for Cargo's testsuite. Perhaps there is a better ordering
537 // that optimizes download time?
538 let mut to_download = BTreeSet::new();
539
540 for id in root_ids {
541 collect_used_deps(
542 &mut to_download,
543 resolve,
544 *id,
545 has_dev_units,
546 requested_kinds,
547 target_data,
548 force_all_targets,
549 )?;
550 }
551 self.get_many(to_download.into_iter())?;
552 Ok(())
553 }
554
555 /// Check if there are any dependency packages that do not have any libs.
556 pub(crate) fn no_lib_pkgs(
557 &self,
558 resolve: &Resolve,
559 root_ids: &[PackageId],
560 has_dev_units: HasDevUnits,
561 requested_kinds: &[CompileKind],
562 target_data: &RustcTargetData<'_>,
563 force_all_targets: ForceAllTargets,
564 ) -> BTreeMap<PackageId, Vec<&Package>> {
565 root_ids
566 .iter()
567 .map(|&root_id| {
568 let pkgs = PackageSet::filter_deps(
569 root_id,
570 resolve,
571 has_dev_units,
572 requested_kinds,
573 target_data,
574 force_all_targets,
575 )
576 .filter_map(|package_id| {
577 if let Ok(dep_pkg) = self.get_one(package_id) {
578 if !dep_pkg.targets().iter().any(|t| t.is_lib()) {
579 Some(dep_pkg)
580 } else {
581 None
582 }
583 } else {
584 None
585 }
586 })
587 .collect();
588 (root_id, pkgs)
589 })
590 .collect()
591 }
592
593 fn filter_deps<'a>(
594 pkg_id: PackageId,
595 resolve: &'a Resolve,
596 has_dev_units: HasDevUnits,
597 requested_kinds: &'a [CompileKind],
598 target_data: &'a RustcTargetData<'_>,
599 force_all_targets: ForceAllTargets,
600 ) -> impl Iterator<Item = PackageId> + 'a {
601 resolve
602 .deps(pkg_id)
603 .filter(move |&(_id, deps)| {
604 deps.iter().any(|dep| {
605 if dep.kind() == DepKind::Development && has_dev_units == HasDevUnits::No {
606 return false;
607 }
608 if force_all_targets == ForceAllTargets::No {
609 let activated = requested_kinds
610 .iter()
611 .chain(Some(&CompileKind::Host))
612 .any(|kind| target_data.dep_platform_activated(dep, *kind));
613 if !activated {
614 return false;
615 }
616 }
617 true
618 })
619 })
620 .map(|(pkg_id, _)| pkg_id)
621 .into_iter()
622 }
623
624 pub fn sources(&self) -> Ref<'_, SourceMap<'cfg>> {
625 self.sources.borrow()
626 }
627
628 pub fn sources_mut(&self) -> RefMut<'_, SourceMap<'cfg>> {
629 self.sources.borrow_mut()
630 }
631
632 /// Merge the given set into self.
633 pub fn add_set(&mut self, set: PackageSet<'cfg>) {
634 assert!(!self.downloading.get());
635 assert!(!set.downloading.get());
636 for (pkg_id, p_cell) in set.packages {
637 self.packages.entry(pkg_id).or_insert(p_cell);
638 }
639 let mut sources = self.sources.borrow_mut();
640 let other_sources = set.sources.into_inner();
641 sources.add_source_map(other_sources);
642 }
643 }
644
645 // When dynamically linked against libcurl, we want to ignore some failures
646 // when using old versions that don't support certain features.
647 macro_rules! try_old_curl {
648 ($e:expr, $msg:expr) => {
649 let result = $e;
650 if cfg!(target_os = "macos") {
651 if let Err(e) = result {
652 warn!("ignoring libcurl {} error: {}", $msg, e);
653 }
654 } else {
655 result.with_context(|| {
656 anyhow::format_err!("failed to enable {}, is curl not built right?", $msg)
657 })?;
658 }
659 };
660 }
661
662 impl<'a, 'cfg> Downloads<'a, 'cfg> {
663 /// Starts to download the package for the `id` specified.
664 ///
665 /// Returns `None` if the package is queued up for download and will
666 /// eventually be returned from `wait_for_download`. Returns `Some(pkg)` if
667 /// the package is ready and doesn't need to be downloaded.
668 pub fn start(&mut self, id: PackageId) -> CargoResult<Option<&'a Package>> {
669 self.start_inner(id)
670 .with_context(|| format!("failed to download `{}`", id))
671 }
672
673 fn start_inner(&mut self, id: PackageId) -> CargoResult<Option<&'a Package>> {
674 // First up see if we've already cached this package, in which case
675 // there's nothing to do.
676 let slot = self
677 .set
678 .packages
679 .get(&id)
680 .ok_or_else(|| internal(format!("couldn't find `{}` in package set", id)))?;
681 if let Some(pkg) = slot.borrow() {
682 return Ok(Some(pkg));
683 }
684
685 // Ask the original source fo this `PackageId` for the corresponding
686 // package. That may immediately come back and tell us that the package
687 // is ready, or it could tell us that it needs to be downloaded.
688 let mut sources = self.set.sources.borrow_mut();
689 let source = sources
690 .get_mut(id.source_id())
691 .ok_or_else(|| internal(format!("couldn't find source for `{}`", id)))?;
692 let pkg = source
693 .download(id)
694 .with_context(|| "unable to get packages from source")?;
695 let (url, descriptor) = match pkg {
696 MaybePackage::Ready(pkg) => {
697 debug!("{} doesn't need a download", id);
698 assert!(slot.fill(pkg).is_ok());
699 return Ok(Some(slot.borrow().unwrap()));
700 }
701 MaybePackage::Download { url, descriptor } => (url, descriptor),
702 };
703
704 // Ok we're going to download this crate, so let's set up all our
705 // internal state and hand off an `Easy` handle to our libcurl `Multi`
706 // handle. This won't actually start the transfer, but later it'll
707 // happen during `wait_for_download`
708 let token = self.next;
709 self.next += 1;
710 debug!("downloading {} as {}", id, token);
711 assert!(self.pending_ids.insert(id));
712
713 let (mut handle, _timeout) = ops::http_handle_and_timeout(self.set.config)?;
714 handle.get(true)?;
715 handle.url(&url)?;
716 handle.follow_location(true)?; // follow redirects
717
718 // Enable HTTP/2 to be used as it'll allow true multiplexing which makes
719 // downloads much faster.
720 //
721 // Currently Cargo requests the `http2` feature of the `curl` crate
722 // which means it should always be built in. On OSX, however, we ship
723 // cargo still linked against the system libcurl. Building curl with
724 // ALPN support for HTTP/2 requires newer versions of OSX (the
725 // SecureTransport API) than we want to ship Cargo for. By linking Cargo
726 // against the system libcurl then older curl installations won't use
727 // HTTP/2 but newer ones will. All that to basically say we ignore
728 // errors here on OSX, but consider this a fatal error to not activate
729 // HTTP/2 on all other platforms.
730 if self.set.multiplexing {
731 try_old_curl!(handle.http_version(HttpVersion::V2), "HTTP2");
732 } else {
733 handle.http_version(HttpVersion::V11)?;
734 }
735
736 // This is an option to `libcurl` which indicates that if there's a
737 // bunch of parallel requests to the same host they all wait until the
738 // pipelining status of the host is known. This means that we won't
739 // initiate dozens of connections to crates.io, but rather only one.
740 // Once the main one is opened we realized that pipelining is possible
741 // and multiplexing is possible with static.crates.io. All in all this
742 // reduces the number of connections done to a more manageable state.
743 try_old_curl!(handle.pipewait(true), "pipewait");
744
745 handle.write_function(move |buf| {
746 debug!("{} - {} bytes of data", token, buf.len());
747 tls::with(|downloads| {
748 if let Some(downloads) = downloads {
749 downloads.pending[&token]
750 .0
751 .data
752 .borrow_mut()
753 .extend_from_slice(buf);
754 }
755 });
756 Ok(buf.len())
757 })?;
758
759 handle.progress(true)?;
760 handle.progress_function(move |dl_total, dl_cur, _, _| {
761 tls::with(|downloads| match downloads {
762 Some(d) => d.progress(token, dl_total as u64, dl_cur as u64),
763 None => false,
764 })
765 })?;
766
767 // If the progress bar isn't enabled then it may be awhile before the
768 // first crate finishes downloading so we inform immediately that we're
769 // downloading crates here.
770 if self.downloads_finished == 0
771 && self.pending.is_empty()
772 && !self.progress.borrow().as_ref().unwrap().is_enabled()
773 {
774 self.set
775 .config
776 .shell()
777 .status("Downloading", "crates ...")?;
778 }
779
780 let dl = Download {
781 token,
782 data: RefCell::new(Vec::new()),
783 id,
784 url,
785 descriptor,
786 total: Cell::new(0),
787 current: Cell::new(0),
788 start: Instant::now(),
789 timed_out: Cell::new(None),
790 retry: Retry::new(self.set.config)?,
791 };
792 self.enqueue(dl, handle)?;
793 self.tick(WhyTick::DownloadStarted)?;
794
795 Ok(None)
796 }
797
798 /// Returns the number of crates that are still downloading.
799 pub fn remaining(&self) -> usize {
800 self.pending.len()
801 }
802
803 /// Blocks the current thread waiting for a package to finish downloading.
804 ///
805 /// This method will wait for a previously enqueued package to finish
806 /// downloading and return a reference to it after it's done downloading.
807 ///
808 /// # Panics
809 ///
810 /// This function will panic if there are no remaining downloads.
811 pub fn wait(&mut self) -> CargoResult<&'a Package> {
812 let (dl, data) = loop {
813 assert_eq!(self.pending.len(), self.pending_ids.len());
814 let (token, result) = self.wait_for_curl()?;
815 debug!("{} finished with {:?}", token, result);
816
817 let (mut dl, handle) = self
818 .pending
819 .remove(&token)
820 .expect("got a token for a non-in-progress transfer");
821 let data = mem::take(&mut *dl.data.borrow_mut());
822 let mut handle = self.set.multi.remove(handle)?;
823 self.pending_ids.remove(&dl.id);
824
825 // Check if this was a spurious error. If it was a spurious error
826 // then we want to re-enqueue our request for another attempt and
827 // then we wait for another request to finish.
828 let ret = {
829 let timed_out = &dl.timed_out;
830 let url = &dl.url;
831 dl.retry
832 .r#try(|| {
833 if let Err(e) = result {
834 // If this error is "aborted by callback" then that's
835 // probably because our progress callback aborted due to
836 // a timeout. We'll find out by looking at the
837 // `timed_out` field, looking for a descriptive message.
838 // If one is found we switch the error code (to ensure
839 // it's flagged as spurious) and then attach our extra
840 // information to the error.
841 if !e.is_aborted_by_callback() {
842 return Err(e.into());
843 }
844
845 return Err(match timed_out.replace(None) {
846 Some(msg) => {
847 let code = curl_sys::CURLE_OPERATION_TIMEDOUT;
848 let mut err = curl::Error::new(code);
849 err.set_extra(msg);
850 err
851 }
852 None => e,
853 }
854 .into());
855 }
856
857 let code = handle.response_code()?;
858 if code != 200 && code != 0 {
859 let url = handle.effective_url()?.unwrap_or(url);
860 return Err(HttpNot200 {
861 code,
862 url: url.to_string(),
863 }
864 .into());
865 }
866 Ok(())
867 })
868 .with_context(|| format!("failed to download from `{}`", dl.url))?
869 };
870 match ret {
871 Some(()) => break (dl, data),
872 None => {
873 self.pending_ids.insert(dl.id);
874 self.enqueue(dl, handle)?
875 }
876 }
877 };
878
879 // If the progress bar isn't enabled then we still want to provide some
880 // semblance of progress of how we're downloading crates, and if the
881 // progress bar is enabled this provides a good log of what's happening.
882 self.progress.borrow_mut().as_mut().unwrap().clear();
883 self.set
884 .config
885 .shell()
886 .status("Downloaded", &dl.descriptor)?;
887
888 self.downloads_finished += 1;
889 self.downloaded_bytes += dl.total.get();
890 if dl.total.get() > self.largest.0 {
891 self.largest = (dl.total.get(), dl.id.name().to_string());
892 }
893
894 // We're about to synchronously extract the crate below. While we're
895 // doing that our download progress won't actually be updated, nor do we
896 // have a great view into the progress of the extraction. Let's prepare
897 // the user for this CPU-heavy step if it looks like it'll take some
898 // time to do so.
899 if dl.total.get() < ByteSize::kb(400).0 {
900 self.tick(WhyTick::DownloadFinished)?;
901 } else {
902 self.tick(WhyTick::Extracting(&dl.id.name()))?;
903 }
904
905 // Inform the original source that the download is finished which
906 // should allow us to actually get the package and fill it in now.
907 let mut sources = self.set.sources.borrow_mut();
908 let source = sources
909 .get_mut(dl.id.source_id())
910 .ok_or_else(|| internal(format!("couldn't find source for `{}`", dl.id)))?;
911 let start = Instant::now();
912 let pkg = source.finish_download(dl.id, data)?;
913
914 // Assume that no time has passed while we were calling
915 // `finish_download`, update all speed checks and timeout limits of all
916 // active downloads to make sure they don't fire because of a slowly
917 // extracted tarball.
918 let finish_dur = start.elapsed();
919 self.updated_at.set(self.updated_at.get() + finish_dur);
920 self.next_speed_check
921 .set(self.next_speed_check.get() + finish_dur);
922
923 let slot = &self.set.packages[&dl.id];
924 assert!(slot.fill(pkg).is_ok());
925 Ok(slot.borrow().unwrap())
926 }
927
928 fn enqueue(&mut self, dl: Download<'cfg>, handle: Easy) -> CargoResult<()> {
929 let mut handle = self.set.multi.add(handle)?;
930 let now = Instant::now();
931 handle.set_token(dl.token)?;
932 self.updated_at.set(now);
933 self.next_speed_check.set(now + self.timeout.dur);
934 self.next_speed_check_bytes_threshold
935 .set(u64::from(self.timeout.low_speed_limit));
936 dl.timed_out.set(None);
937 dl.current.set(0);
938 dl.total.set(0);
939 self.pending.insert(dl.token, (dl, handle));
940 Ok(())
941 }
942
943 /// Block, waiting for curl. Returns a token and a `Result` for that token
944 /// (`Ok` means the download successfully finished).
945 fn wait_for_curl(&mut self) -> CargoResult<(usize, Result<(), curl::Error>)> {
946 // This is the main workhorse loop. We use libcurl's portable `wait`
947 // method to actually perform blocking. This isn't necessarily too
948 // efficient in terms of fd management, but we should only be juggling
949 // a few anyway.
950 //
951 // Here we start off by asking the `multi` handle to do some work via
952 // the `perform` method. This will actually do I/O work (non-blocking)
953 // and attempt to make progress. Afterwards we ask about the `messages`
954 // contained in the handle which will inform us if anything has finished
955 // transferring.
956 //
957 // If we've got a finished transfer after all that work we break out
958 // and process the finished transfer at the end. Otherwise we need to
959 // actually block waiting for I/O to happen, which we achieve with the
960 // `wait` method on `multi`.
961 loop {
962 let n = tls::set(self, || {
963 self.set
964 .multi
965 .perform()
966 .with_context(|| "failed to perform http requests")
967 })?;
968 debug!("handles remaining: {}", n);
969 let results = &mut self.results;
970 let pending = &self.pending;
971 self.set.multi.messages(|msg| {
972 let token = msg.token().expect("failed to read token");
973 let handle = &pending[&token].1;
974 if let Some(result) = msg.result_for(handle) {
975 results.push((token, result));
976 } else {
977 debug!("message without a result (?)");
978 }
979 });
980
981 if let Some(pair) = results.pop() {
982 break Ok(pair);
983 }
984 assert!(!self.pending.is_empty());
985 let timeout = self
986 .set
987 .multi
988 .get_timeout()?
989 .unwrap_or_else(|| Duration::new(5, 0));
990 self.set
991 .multi
992 .wait(&mut [], timeout)
993 .with_context(|| "failed to wait on curl `Multi`")?;
994 }
995 }
996
997 fn progress(&self, token: usize, total: u64, cur: u64) -> bool {
998 let dl = &self.pending[&token].0;
999 dl.total.set(total);
1000 let now = Instant::now();
1001 if cur > dl.current.get() {
1002 let delta = cur - dl.current.get();
1003 let threshold = self.next_speed_check_bytes_threshold.get();
1004
1005 dl.current.set(cur);
1006 self.updated_at.set(now);
1007
1008 if delta >= threshold {
1009 self.next_speed_check.set(now + self.timeout.dur);
1010 self.next_speed_check_bytes_threshold
1011 .set(u64::from(self.timeout.low_speed_limit));
1012 } else {
1013 self.next_speed_check_bytes_threshold.set(threshold - delta);
1014 }
1015 }
1016 if self.tick(WhyTick::DownloadUpdate).is_err() {
1017 return false;
1018 }
1019
1020 // If we've spent too long not actually receiving any data we time out.
1021 if now > self.updated_at.get() + self.timeout.dur {
1022 self.updated_at.set(now);
1023 let msg = format!(
1024 "failed to download any data for `{}` within {}s",
1025 dl.id,
1026 self.timeout.dur.as_secs()
1027 );
1028 dl.timed_out.set(Some(msg));
1029 return false;
1030 }
1031
1032 // If we reached the point in time that we need to check our speed
1033 // limit, see if we've transferred enough data during this threshold. If
1034 // it fails this check then we fail because the download is going too
1035 // slowly.
1036 if now >= self.next_speed_check.get() {
1037 self.next_speed_check.set(now + self.timeout.dur);
1038 assert!(self.next_speed_check_bytes_threshold.get() > 0);
1039 let msg = format!(
1040 "download of `{}` failed to transfer more \
1041 than {} bytes in {}s",
1042 dl.id,
1043 self.timeout.low_speed_limit,
1044 self.timeout.dur.as_secs()
1045 );
1046 dl.timed_out.set(Some(msg));
1047 return false;
1048 }
1049
1050 true
1051 }
1052
1053 fn tick(&self, why: WhyTick<'_>) -> CargoResult<()> {
1054 let mut progress = self.progress.borrow_mut();
1055 let progress = progress.as_mut().unwrap();
1056
1057 if let WhyTick::DownloadUpdate = why {
1058 if !progress.update_allowed() {
1059 return Ok(());
1060 }
1061 }
1062 let pending = self.pending.len();
1063 let mut msg = if pending == 1 {
1064 format!("{} crate", pending)
1065 } else {
1066 format!("{} crates", pending)
1067 };
1068 match why {
1069 WhyTick::Extracting(krate) => {
1070 msg.push_str(&format!(", extracting {} ...", krate));
1071 }
1072 _ => {
1073 let mut dur = Duration::new(0, 0);
1074 let mut remaining = 0;
1075 for (dl, _) in self.pending.values() {
1076 dur += dl.start.elapsed();
1077 // If the total/current look weird just throw out the data
1078 // point, sounds like curl has more to learn before we have
1079 // the true information.
1080 if dl.total.get() >= dl.current.get() {
1081 remaining += dl.total.get() - dl.current.get();
1082 }
1083 }
1084 if remaining > 0 && dur > Duration::from_millis(500) {
1085 msg.push_str(&format!(", remaining bytes: {}", ByteSize(remaining)));
1086 }
1087 }
1088 }
1089 progress.print_now(&msg)
1090 }
1091 }
1092
1093 #[derive(Copy, Clone)]
1094 enum WhyTick<'a> {
1095 DownloadStarted,
1096 DownloadUpdate,
1097 DownloadFinished,
1098 Extracting(&'a str),
1099 }
1100
1101 impl<'a, 'cfg> Drop for Downloads<'a, 'cfg> {
1102 fn drop(&mut self) {
1103 self.set.downloading.set(false);
1104 let progress = self.progress.get_mut().take().unwrap();
1105 // Don't print a download summary if we're not using a progress bar,
1106 // we've already printed lots of `Downloading...` items.
1107 if !progress.is_enabled() {
1108 return;
1109 }
1110 // If we didn't download anything, no need for a summary.
1111 if self.downloads_finished == 0 {
1112 return;
1113 }
1114 // If an error happened, let's not clutter up the output.
1115 if !self.success {
1116 return;
1117 }
1118 // pick the correct plural of crate(s)
1119 let crate_string = if self.downloads_finished == 1 {
1120 "crate"
1121 } else {
1122 "crates"
1123 };
1124 let mut status = format!(
1125 "{} {} ({}) in {}",
1126 self.downloads_finished,
1127 crate_string,
1128 ByteSize(self.downloaded_bytes),
1129 util::elapsed(self.start.elapsed())
1130 );
1131 // print the size of largest crate if it was >1mb
1132 // however don't print if only a single crate was downloaded
1133 // because it is obvious that it will be the largest then
1134 if self.largest.0 > ByteSize::mb(1).0 && self.downloads_finished > 1 {
1135 status.push_str(&format!(
1136 " (largest was `{}` at {})",
1137 self.largest.1,
1138 ByteSize(self.largest.0),
1139 ));
1140 }
1141 // Clear progress before displaying final summary.
1142 drop(progress);
1143 drop(self.set.config.shell().status("Downloaded", status));
1144 }
1145 }
1146
1147 mod tls {
1148 use std::cell::Cell;
1149
1150 use super::Downloads;
1151
1152 thread_local!(static PTR: Cell<usize> = Cell::new(0));
1153
1154 pub(crate) fn with<R>(f: impl FnOnce(Option<&Downloads<'_, '_>>) -> R) -> R {
1155 let ptr = PTR.with(|p| p.get());
1156 if ptr == 0 {
1157 f(None)
1158 } else {
1159 unsafe { f(Some(&*(ptr as *const Downloads<'_, '_>))) }
1160 }
1161 }
1162
1163 pub(crate) fn set<R>(dl: &Downloads<'_, '_>, f: impl FnOnce() -> R) -> R {
1164 struct Reset<'a, T: Copy>(&'a Cell<T>, T);
1165
1166 impl<'a, T: Copy> Drop for Reset<'a, T> {
1167 fn drop(&mut self) {
1168 self.0.set(self.1);
1169 }
1170 }
1171
1172 PTR.with(|p| {
1173 let _reset = Reset(p, p.get());
1174 p.set(dl as *const Downloads<'_, '_> as usize);
1175 f()
1176 })
1177 }
1178 }