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