]> git.proxmox.com Git - cargo.git/blame - src/cargo/ops/cargo_read_manifest.rs
Upgrade to Rust 2018
[cargo.git] / src / cargo / ops / cargo_read_manifest.rs
CommitLineData
06a0605f 1use std::collections::{HashMap, HashSet};
f28c7872 2use std::fs;
a6dad622
AC
3use std::io;
4use std::path::{Path, PathBuf};
a09ad635 5
04ddd4d0
DW
6use crate::core::{EitherManifest, Package, PackageId, SourceId};
7use crate::util::errors::{CargoError, CargoResult};
8use crate::util::important_paths::find_project_manifest_exact;
9use crate::util::toml::read_manifest;
10use crate::util::{self, Config};
b3c23503 11
1e682848
AC
12pub fn read_package(
13 path: &Path,
e5a11190 14 source_id: SourceId,
1e682848
AC
15 config: &Config,
16) -> CargoResult<(Package, Vec<PathBuf>)> {
17 trace!(
18 "read_package; path={}; source-id={}",
19 path.display(),
20 source_id
21 );
82655b46 22 let (manifest, nested) = read_manifest(path, source_id, config)?;
58ddb28a
AC
23 let manifest = match manifest {
24 EitherManifest::Real(manifest) => manifest,
1e682848
AC
25 EitherManifest::Virtual(..) => bail!(
26 "found a virtual manifest at `{}` instead of a package \
27 manifest",
28 path.display()
29 ),
58ddb28a 30 };
bbbf2dea 31
346df29a 32 Ok((Package::new(manifest, path), nested))
62bff631 33}
bcf90287 34
1e682848
AC
35pub fn read_packages(
36 path: &Path,
e5a11190 37 source_id: SourceId,
1e682848
AC
38 config: &Config,
39) -> CargoResult<Vec<Package>> {
06a0605f 40 let mut all_packages = HashMap::new();
a6dad622 41 let mut visited = HashSet::<PathBuf>::new();
4b827bf5 42 let mut errors = Vec::<CargoError>::new();
bcf90287 43
1e682848
AC
44 trace!(
45 "looking for root package: {}, source_id={}",
46 path.display(),
47 source_id
48 );
71e4252a 49
82655b46 50 walk(path, &mut |dir| {
98854f6f 51 trace!("looking for child package: {}", dir.display());
c84bc16b 52
4cd3c567
PG
53 // Don't recurse into hidden/dot directories unless we're at the toplevel
54 if dir != path {
55 let name = dir.file_name().and_then(|s| s.to_str());
23591fe5 56 if name.map(|s| s.starts_with('.')) == Some(true) {
1e682848 57 return Ok(false);
4cd3c567 58 }
c84bc16b 59
4cd3c567
PG
60 // Don't automatically discover packages across git submodules
61 if fs::metadata(&dir.join(".git")).is_ok() {
1e682848 62 return Ok(false);
4cd3c567 63 }
97a2f271 64 }
c84bc16b
AC
65
66 // Don't ever look at target directories
1e682848
AC
67 if dir.file_name().and_then(|s| s.to_str()) == Some("target")
68 && has_manifest(dir.parent().unwrap())
69 {
70 return Ok(false);
c84bc16b
AC
71 }
72
73 if has_manifest(dir) {
1e682848
AC
74 read_nested_packages(
75 dir,
76 &mut all_packages,
77 source_id,
78 config,
79 &mut visited,
80 &mut errors,
81 )?;
c84bc16b 82 }
ea3cb31c 83 Ok(true)
82655b46 84 })?;
bcf90287 85
ea3cb31c 86 if all_packages.is_empty() {
0275ca06 87 match errors.pop() {
88 Some(err) => Err(err),
1e682848
AC
89 None => Err(format_err!(
90 "Could not find Cargo.toml in `{}`",
91 path.display()
92 )),
4b827bf5 93 }
ea3cb31c 94 } else {
06a0605f 95 Ok(all_packages.into_iter().map(|(_, v)| v).collect())
ea3cb31c
YK
96 }
97}
98
1e682848 99fn walk(path: &Path, callback: &mut FnMut(&Path) -> CargoResult<bool>) -> CargoResult<()> {
82655b46 100 if !callback(path)? {
a6dad622 101 trace!("not processing {}", path.display());
1e682848 102 return Ok(());
a6dad622 103 }
71e4252a 104
a6dad622
AC
105 // Ignore any permission denied errors because temporary directories
106 // can often have some weird permissions on them.
107 let dirs = match fs::read_dir(path) {
108 Ok(dirs) => dirs,
1e682848 109 Err(ref e) if e.kind() == io::ErrorKind::PermissionDenied => return Ok(()),
d1e640d7 110 Err(e) => {
37cffbe0
AC
111 let cx = format!("failed to read directory `{}`", path.display());
112 let e = CargoError::from(e);
1e682848 113 return Err(e.context(cx).into());
d1e640d7 114 }
a6dad622
AC
115 };
116 for dir in dirs {
82655b46
SG
117 let dir = dir?;
118 if dir.file_type()?.is_dir() {
119 walk(&dir.path(), callback)?;
e595e877 120 }
ea3cb31c 121 }
ea3cb31c
YK
122 Ok(())
123}
124
ea3cb31c
YK
125fn has_manifest(path: &Path) -> bool {
126 find_project_manifest_exact(path, "Cargo.toml").is_ok()
127}
128
1e682848
AC
129fn read_nested_packages(
130 path: &Path,
131 all_packages: &mut HashMap<PackageId, Package>,
e5a11190 132 source_id: SourceId,
1e682848
AC
133 config: &Config,
134 visited: &mut HashSet<PathBuf>,
135 errors: &mut Vec<CargoError>,
136) -> CargoResult<()> {
137 if !visited.insert(path.to_path_buf()) {
138 return Ok(());
139 }
ea3cb31c 140
82655b46 141 let manifest_path = find_project_manifest_exact(path, "Cargo.toml")?;
9243f06d 142
2b416a0d 143 let (manifest, nested) = match read_manifest(&manifest_path, source_id, config) {
4b827bf5 144 Err(err) => {
2b416a0d
FA
145 // Ignore malformed manifests found on git repositories
146 //
147 // git source try to find and read all manifests from the repository
148 // but since it's not possible to exclude folders from this search
149 // it's safer to ignore malformed manifests to avoid
150 //
151 // TODO: Add a way to exclude folders?
1e682848
AC
152 info!(
153 "skipping malformed package found at `{}`",
154 path.to_string_lossy()
155 );
25b7ad26 156 errors.push(err.into());
2b416a0d
FA
157 return Ok(());
158 }
1e682848 159 Ok(tuple) => tuple,
2b416a0d 160 };
3d6de417 161
9243f06d
AC
162 let manifest = match manifest {
163 EitherManifest::Real(manifest) => manifest,
164 EitherManifest::Virtual(..) => return Ok(()),
165 };
166 let pkg = Package::new(manifest, &manifest_path);
ea3cb31c 167
dae87a26 168 let pkg_id = pkg.package_id();
1e682848 169 use std::collections::hash_map::Entry;
836fdacd 170 match all_packages.entry(pkg_id) {
1e682848
AC
171 Entry::Vacant(v) => {
172 v.insert(pkg);
173 }
836fdacd 174 Entry::Occupied(_) => {
1e682848
AC
175 info!(
176 "skipping nested package `{}` found at `{}`",
177 pkg.name(),
178 path.to_string_lossy()
179 );
836fdacd 180 }
cdbaa749 181 }
ea3cb31c 182
9fba127e
AC
183 // Registry sources are not allowed to have `path=` dependencies because
184 // they're all translated to actual registry dependencies.
a6dad622
AC
185 //
186 // We normalize the path here ensure that we don't infinitely walk around
187 // looking for crates. By normalizing we ensure that we visit this crate at
188 // most once.
189 //
190 // TODO: filesystem/symlink implications?
9fba127e
AC
191 if !source_id.is_registry() {
192 for p in nested.iter() {
a6dad622 193 let path = util::normalize_path(&path.join(p));
1e682848 194 read_nested_packages(&path, all_packages, source_id, config, visited, errors)?;
9fba127e 195 }
ea3cb31c
YK
196 }
197
c84bc16b 198 Ok(())
bcf90287 199}