]>
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 | |
58ddb28a | 6 | use core::{Package, SourceId, PackageId, EitherManifest}; |
f28c7872 | 7 | use util::{self, paths, CargoResult, human, Config, ChainError}; |
ea3cb31c | 8 | use util::important_paths::find_project_manifest_exact; |
1e01f489 | 9 | use util::toml::Layout; |
62bff631 | 10 | |
58ddb28a AC |
11 | pub fn read_manifest(path: &Path, source_id: &SourceId, config: &Config) |
12 | -> CargoResult<(EitherManifest, Vec<PathBuf>)> { | |
13 | trace!("read_package; path={}; source-id={}", path.display(), source_id); | |
82655b46 | 14 | let contents = paths::read(path)?; |
58ddb28a AC |
15 | |
16 | let layout = Layout::from_project_path(path.parent().unwrap()); | |
ed58e0be | 17 | let root = layout.root.clone(); |
58ddb28a | 18 | util::toml::to_manifest(&contents, source_id, layout, config).chain_error(|| { |
d73a110d AC |
19 | human(format!("failed to parse manifest at `{}`", |
20 | root.join("Cargo.toml").display())) | |
ed58e0be | 21 | }) |
b3c23503 CL |
22 | } |
23 | ||
5d0cb3f2 | 24 | pub fn read_package(path: &Path, source_id: &SourceId, config: &Config) |
a6dad622 | 25 | -> CargoResult<(Package, Vec<PathBuf>)> { |
98854f6f | 26 | trace!("read_package; path={}; source-id={}", path.display(), source_id); |
82655b46 | 27 | let (manifest, nested) = read_manifest(path, source_id, config)?; |
58ddb28a AC |
28 | let manifest = match manifest { |
29 | EitherManifest::Real(manifest) => manifest, | |
30 | EitherManifest::Virtual(..) => { | |
31 | bail!("found a virtual manifest at `{}` instead of a package \ | |
32 | manifest", path.display()) | |
33 | } | |
34 | }; | |
bbbf2dea | 35 | |
346df29a | 36 | Ok((Package::new(manifest, path), nested)) |
62bff631 | 37 | } |
bcf90287 | 38 | |
5d0cb3f2 AC |
39 | pub fn read_packages(path: &Path, source_id: &SourceId, config: &Config) |
40 | -> CargoResult<Vec<Package>> { | |
06a0605f | 41 | let mut all_packages = HashMap::new(); |
a6dad622 | 42 | let mut visited = HashSet::<PathBuf>::new(); |
bcf90287 | 43 | |
98854f6f | 44 | trace!("looking for root package: {}, source_id={}", path.display(), source_id); |
71e4252a | 45 | |
82655b46 | 46 | walk(path, &mut |dir| { |
98854f6f | 47 | trace!("looking for child package: {}", dir.display()); |
c84bc16b | 48 | |
4cd3c567 PG |
49 | // Don't recurse into hidden/dot directories unless we're at the toplevel |
50 | if dir != path { | |
51 | let name = dir.file_name().and_then(|s| s.to_str()); | |
52 | if name.map(|s| s.starts_with(".")) == Some(true) { | |
53 | return Ok(false) | |
54 | } | |
c84bc16b | 55 | |
4cd3c567 PG |
56 | // Don't automatically discover packages across git submodules |
57 | if fs::metadata(&dir.join(".git")).is_ok() { | |
58 | return Ok(false) | |
59 | } | |
97a2f271 | 60 | } |
c84bc16b AC |
61 | |
62 | // Don't ever look at target directories | |
a6dad622 AC |
63 | if dir.file_name().and_then(|s| s.to_str()) == Some("target") && |
64 | has_manifest(dir.parent().unwrap()) { | |
c84bc16b AC |
65 | return Ok(false) |
66 | } | |
67 | ||
68 | if has_manifest(dir) { | |
82655b46 SG |
69 | read_nested_packages(dir, &mut all_packages, source_id, config, |
70 | &mut visited)?; | |
c84bc16b | 71 | } |
ea3cb31c | 72 | Ok(true) |
82655b46 | 73 | })?; |
bcf90287 | 74 | |
ea3cb31c YK |
75 | if all_packages.is_empty() { |
76 | Err(human(format!("Could not find Cargo.toml in `{}`", path.display()))) | |
77 | } else { | |
06a0605f | 78 | Ok(all_packages.into_iter().map(|(_, v)| v).collect()) |
ea3cb31c YK |
79 | } |
80 | } | |
81 | ||
e595e877 AC |
82 | fn walk(path: &Path, callback: &mut FnMut(&Path) -> CargoResult<bool>) |
83 | -> CargoResult<()> { | |
82655b46 | 84 | if !callback(path)? { |
a6dad622 | 85 | trace!("not processing {}", path.display()); |
97a2f271 | 86 | return Ok(()) |
a6dad622 | 87 | } |
71e4252a | 88 | |
a6dad622 AC |
89 | // Ignore any permission denied errors because temporary directories |
90 | // can often have some weird permissions on them. | |
91 | let dirs = match fs::read_dir(path) { | |
92 | Ok(dirs) => dirs, | |
93 | Err(ref e) if e.kind() == io::ErrorKind::PermissionDenied => { | |
94 | return Ok(()) | |
ea3cb31c | 95 | } |
d1e640d7 AC |
96 | Err(e) => { |
97 | return Err(human(e)).chain_error(|| { | |
98 | human(format!("failed to read directory `{}`", path.display())) | |
99 | }) | |
100 | } | |
a6dad622 AC |
101 | }; |
102 | for dir in dirs { | |
82655b46 SG |
103 | let dir = dir?; |
104 | if dir.file_type()?.is_dir() { | |
105 | walk(&dir.path(), callback)?; | |
e595e877 | 106 | } |
ea3cb31c | 107 | } |
ea3cb31c YK |
108 | Ok(()) |
109 | } | |
110 | ||
ea3cb31c YK |
111 | fn has_manifest(path: &Path) -> bool { |
112 | find_project_manifest_exact(path, "Cargo.toml").is_ok() | |
113 | } | |
114 | ||
c84bc16b | 115 | fn read_nested_packages(path: &Path, |
06a0605f | 116 | all_packages: &mut HashMap<PackageId, Package>, |
c84bc16b | 117 | source_id: &SourceId, |
5d0cb3f2 | 118 | config: &Config, |
a6dad622 AC |
119 | visited: &mut HashSet<PathBuf>) -> CargoResult<()> { |
120 | if !visited.insert(path.to_path_buf()) { return Ok(()) } | |
ea3cb31c | 121 | |
82655b46 | 122 | let manifest_path = find_project_manifest_exact(path, "Cargo.toml")?; |
9243f06d | 123 | |
3d6de417 FA |
124 | let result = read_manifest(&manifest_path, source_id, config); |
125 | ||
126 | // Ignore malformed manifests | |
127 | if result.is_err() { | |
128 | info!("skipping malformed package found at `{}`", | |
129 | path.to_string_lossy()); | |
130 | return Ok(()); | |
131 | } | |
132 | ||
133 | let (manifest, nested) = result.unwrap(); | |
9243f06d AC |
134 | let manifest = match manifest { |
135 | EitherManifest::Real(manifest) => manifest, | |
136 | EitherManifest::Virtual(..) => return Ok(()), | |
137 | }; | |
138 | let pkg = Package::new(manifest, &manifest_path); | |
ea3cb31c | 139 | |
cdbaa749 SF |
140 | let pkg_id = pkg.package_id().clone(); |
141 | if !all_packages.contains_key(&pkg_id) { | |
142 | all_packages.insert(pkg_id, pkg); | |
143 | } else { | |
8790c820 SF |
144 | info!("skipping nested package `{}` found at `{}`", |
145 | pkg.name(), path.to_string_lossy()); | |
cdbaa749 | 146 | } |
ea3cb31c | 147 | |
9fba127e AC |
148 | // Registry sources are not allowed to have `path=` dependencies because |
149 | // they're all translated to actual registry dependencies. | |
a6dad622 AC |
150 | // |
151 | // We normalize the path here ensure that we don't infinitely walk around | |
152 | // looking for crates. By normalizing we ensure that we visit this crate at | |
153 | // most once. | |
154 | // | |
155 | // TODO: filesystem/symlink implications? | |
9fba127e AC |
156 | if !source_id.is_registry() { |
157 | for p in nested.iter() { | |
a6dad622 | 158 | let path = util::normalize_path(&path.join(p)); |
82655b46 SG |
159 | read_nested_packages(&path, all_packages, source_id, |
160 | config, visited)?; | |
9fba127e | 161 | } |
ea3cb31c YK |
162 | } |
163 | ||
c84bc16b | 164 | Ok(()) |
bcf90287 | 165 | } |