Skip to content

Commit d0f204e

Browse files
committed
Auto merge of rust-lang#109133 - weihanglo:make-cargo-a-workspace, r=ehuss
Make cargo a workspace 8 commits in 7bf43f028ba5eb1f4d70d271c2546c38512c9875..39116ccc9b420a883a98a960f0597f9cf87414b8 2023-04-10 16:01:41 +0000 to 2023-04-15 20:24:15 +0000 - Make cargo a workspace (rust-lang/cargo#11851) - Fix flaky not_found_permutations test. (rust-lang/cargo#11976) - Use restricted Damerau-Levenshtein algorithm (rust-lang/cargo#11963) - Correct the bug report for `cargo clippy --fix` (rust-lang/cargo#11882) - Stabilize `cargo logout` (rust-lang/cargo#11950) - Add more information to HTTP errors to help with debugging. (rust-lang/cargo#11878) - Use registry.default for login/logout (rust-lang/cargo#11949) - Change -C to be unstable (rust-lang/cargo#11960) --- ### What does this PR try to resolve? Making cargo a workspace. Why doing this? * `rustc-workspace-hack` is primarily for sharing dependencies between rls and cargo, as rls previously depends on cargo. After rls retired, it is no longer the case sharing dependencies. * It's q bit painful that cargo needs to deal with some dependency and licensing complexities. For example, rust-lang#108665 failed because of the interaction bewteen `windows-sys` and `raw-dylib`. It currenctly blocks cargo's feature `-Zgitxodie` from moving forward. * See rust-lang/cargo#11851 ### Benchmark result I've done a simple benchmark on both keeping or removing entire `rustc-workspace-hack`. It had no significant difference. Both took ~2m30s to finish `./x.py build -j8 src/tools/cargo src/tools/rls src/tools/clippy src/tools/miri src/tools/rustfmt`. Environment info: ``` host: aarch64-apple-darwin os: Mac OS 13.2.1 [64-bit] ``` A sophisticated benchmark may be needed. ### Additional information This depends on prior works from `@Muscraft` and `@ehuss.` Credits to them!
2 parents 8a778ca + 103ed0e commit d0f204e

File tree

13 files changed

+136
-2141
lines changed

13 files changed

+136
-2141
lines changed

Cargo.lock

+38-1,779
Large diffs are not rendered by default.

Cargo.toml

-10
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@ members = [
2222
"src/tools/remote-test-server",
2323
"src/tools/rust-installer",
2424
"src/tools/rust-demangler",
25-
"src/tools/cargo",
26-
"src/tools/cargo/crates/credential/cargo-credential-1password",
27-
"src/tools/cargo/crates/credential/cargo-credential-macos-keychain",
28-
"src/tools/cargo/crates/credential/cargo-credential-wincred",
29-
"src/tools/cargo/crates/mdman",
30-
# "src/tools/cargo/crates/resolver-tests",
3125
"src/tools/rustdoc",
3226
"src/tools/rls",
3327
"src/tools/rustfmt",
@@ -106,10 +100,6 @@ miniz_oxide.debug = 0
106100
object.debug = 0
107101

108102
[patch.crates-io]
109-
# See comments in `src/tools/rustc-workspace-hack/README.md` for what's going on
110-
# here
111-
rustc-workspace-hack = { path = 'src/tools/rustc-workspace-hack' }
112-
113103
# See comments in `library/rustc-std-workspace-core/README.md` for what's going on
114104
# here
115105
rustc-std-workspace-core = { path = 'library/rustc-std-workspace-core' }

src/bootstrap/bootstrap.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -822,7 +822,8 @@ def check_vendored_status(self):
822822
if self.use_vendored_sources:
823823
vendor_dir = os.path.join(self.rust_root, 'vendor')
824824
if not os.path.exists(vendor_dir):
825-
sync_dirs = "--sync ./src/tools/rust-analyzer/Cargo.toml " \
825+
sync_dirs = "--sync ./src/tools/cargo/Cargo.toml " \
826+
"--sync ./src/tools/rust-analyzer/Cargo.toml " \
826827
"--sync ./compiler/rustc_codegen_cranelift/Cargo.toml " \
827828
"--sync ./src/bootstrap/Cargo.toml "
828829
print('error: vendoring required, but vendor directory does not exist.')

src/bootstrap/dist.rs

+3
Original file line numberDiff line numberDiff line change
@@ -996,11 +996,14 @@ impl Step for PlainSourceTarball {
996996
// If we're building from git sources, we need to vendor a complete distribution.
997997
if builder.rust_info().is_managed_git_subrepository() {
998998
// Ensure we have the submodules checked out.
999+
builder.update_submodule(Path::new("src/tools/cargo"));
9991000
builder.update_submodule(Path::new("src/tools/rust-analyzer"));
10001001

10011002
// Vendor all Cargo dependencies
10021003
let mut cmd = Command::new(&builder.initial_cargo);
10031004
cmd.arg("vendor")
1005+
.arg("--sync")
1006+
.arg(builder.src.join("./src/tools/cargo/Cargo.toml"))
10041007
.arg("--sync")
10051008
.arg(builder.src.join("./src/tools/rust-analyzer/Cargo.toml"))
10061009
.arg("--sync")

src/bootstrap/lib.rs

-3
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,6 @@ pub struct Build {
238238
ci_env: CiEnv,
239239
delayed_failures: RefCell<Vec<String>>,
240240
prerelease_version: Cell<Option<u32>>,
241-
tool_artifacts:
242-
RefCell<HashMap<TargetSelection, HashMap<String, (&'static str, PathBuf, Vec<String>)>>>,
243241

244242
#[cfg(feature = "build-metrics")]
245243
metrics: metrics::BuildMetrics,
@@ -458,7 +456,6 @@ impl Build {
458456
ci_env: CiEnv::current(),
459457
delayed_failures: RefCell::new(Vec::new()),
460458
prerelease_version: Cell::new(None),
461-
tool_artifacts: Default::default(),
462459

463460
#[cfg(feature = "build-metrics")]
464461
metrics: metrics::BuildMetrics::init(),

src/bootstrap/metadata.rs

+44-15
Original file line numberDiff line numberDiff line change
@@ -7,38 +7,35 @@ use crate::cache::INTERNER;
77
use crate::util::output;
88
use crate::{Build, Crate};
99

10-
#[derive(Deserialize)]
10+
/// For more information, see the output of
11+
/// <https://doc.rust-lang.org/nightly/cargo/commands/cargo-metadata.html>
12+
#[derive(Debug, Deserialize)]
1113
struct Output {
1214
packages: Vec<Package>,
1315
}
1416

15-
#[derive(Deserialize)]
17+
/// For more information, see the output of
18+
/// <https://doc.rust-lang.org/nightly/cargo/commands/cargo-metadata.html>
19+
#[derive(Debug, Deserialize)]
1620
struct Package {
1721
name: String,
1822
source: Option<String>,
1923
manifest_path: String,
2024
dependencies: Vec<Dependency>,
2125
}
2226

23-
#[derive(Deserialize)]
27+
/// For more information, see the output of
28+
/// <https://doc.rust-lang.org/nightly/cargo/commands/cargo-metadata.html>
29+
#[derive(Debug, Deserialize)]
2430
struct Dependency {
2531
name: String,
2632
source: Option<String>,
2733
}
2834

35+
/// Collects and stores package metadata of each workspace members into `build`,
36+
/// by executing `cargo metadata` commands.
2937
pub fn build(build: &mut Build) {
30-
// Run `cargo metadata` to figure out what crates we're testing.
31-
let mut cargo = Command::new(&build.initial_cargo);
32-
cargo
33-
.arg("metadata")
34-
.arg("--format-version")
35-
.arg("1")
36-
.arg("--no-deps")
37-
.arg("--manifest-path")
38-
.arg(build.src.join("Cargo.toml"));
39-
let output = output(&mut cargo);
40-
let output: Output = serde_json::from_str(&output).unwrap();
41-
for package in output.packages {
38+
for package in workspace_members(build) {
4239
if package.source.is_none() {
4340
let name = INTERNER.intern_string(package.name);
4441
let mut path = PathBuf::from(package.manifest_path);
@@ -57,3 +54,35 @@ pub fn build(build: &mut Build) {
5754
}
5855
}
5956
}
57+
58+
/// Invokes `cargo metadata` to get package metadata of each workspace member.
59+
///
60+
/// Note that `src/tools/cargo` is no longer a workspace member but we still
61+
/// treat it as one here, by invoking an additional `cargo metadata` command.
62+
fn workspace_members(build: &Build) -> impl Iterator<Item = Package> {
63+
let cmd_metadata = |manifest_path| {
64+
let mut cargo = Command::new(&build.initial_cargo);
65+
cargo
66+
.arg("metadata")
67+
.arg("--format-version")
68+
.arg("1")
69+
.arg("--no-deps")
70+
.arg("--manifest-path")
71+
.arg(manifest_path);
72+
cargo
73+
};
74+
75+
// Collects `metadata.packages` from the root workspace.
76+
let root_manifest_path = build.src.join("Cargo.toml");
77+
let root_output = output(&mut cmd_metadata(&root_manifest_path));
78+
let Output { packages, .. } = serde_json::from_str(&root_output).unwrap();
79+
80+
// Collects `metadata.packages` from src/tools/cargo separately.
81+
let cargo_manifest_path = build.src.join("src/tools/cargo/Cargo.toml");
82+
let cargo_output = output(&mut cmd_metadata(&cargo_manifest_path));
83+
let Output { packages: cargo_packages, .. } = serde_json::from_str(&cargo_output).unwrap();
84+
85+
// We only care about the root package from `src/tool/cargo` workspace.
86+
let cargo_package = cargo_packages.into_iter().find(|pkg| pkg.name == "cargo").into_iter();
87+
packages.into_iter().chain(cargo_package)
88+
}

src/bootstrap/tool.rs

+5-130
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::collections::HashSet;
21
use std::env;
32
use std::fs;
43
use std::path::PathBuf;
@@ -120,135 +119,9 @@ impl Step for ToolBuild {
120119
&self.target,
121120
);
122121
builder.info(&msg);
123-
let mut duplicates = Vec::new();
124-
let is_expected = compile::stream_cargo(builder, cargo, vec![], &mut |msg| {
125-
// Only care about big things like the RLS/Cargo for now
126-
match tool {
127-
"rls" | "cargo" | "clippy-driver" | "miri" | "rustfmt" => {}
128122

129-
_ => return,
130-
}
131-
let (id, features, filenames) = match msg {
132-
compile::CargoMessage::CompilerArtifact {
133-
package_id,
134-
features,
135-
filenames,
136-
target: _,
137-
} => (package_id, features, filenames),
138-
_ => return,
139-
};
140-
let features = features.iter().map(|s| s.to_string()).collect::<Vec<_>>();
141-
142-
for path in filenames {
143-
let val = (tool, PathBuf::from(&*path), features.clone());
144-
// we're only interested in deduplicating rlibs for now
145-
if val.1.extension().and_then(|s| s.to_str()) != Some("rlib") {
146-
continue;
147-
}
148-
149-
// Don't worry about compiles that turn out to be host
150-
// dependencies or build scripts. To skip these we look for
151-
// anything that goes in `.../release/deps` but *doesn't* go in
152-
// `$target/release/deps`. This ensure that outputs in
153-
// `$target/release` are still considered candidates for
154-
// deduplication.
155-
if let Some(parent) = val.1.parent() {
156-
if parent.ends_with("release/deps") {
157-
let maybe_target = parent
158-
.parent()
159-
.and_then(|p| p.parent())
160-
.and_then(|p| p.file_name())
161-
.and_then(|p| p.to_str())
162-
.unwrap();
163-
if maybe_target != &*target.triple {
164-
continue;
165-
}
166-
}
167-
}
168-
169-
// Record that we've built an artifact for `id`, and if one was
170-
// already listed then we need to see if we reused the same
171-
// artifact or produced a duplicate.
172-
let mut artifacts = builder.tool_artifacts.borrow_mut();
173-
let prev_artifacts = artifacts.entry(target).or_default();
174-
let prev = match prev_artifacts.get(&*id) {
175-
Some(prev) => prev,
176-
None => {
177-
prev_artifacts.insert(id.to_string(), val);
178-
continue;
179-
}
180-
};
181-
if prev.1 == val.1 {
182-
return; // same path, same artifact
183-
}
184-
185-
// If the paths are different and one of them *isn't* inside of
186-
// `release/deps`, then it means it's probably in
187-
// `$target/release`, or it's some final artifact like
188-
// `libcargo.rlib`. In these situations Cargo probably just
189-
// copied it up from `$target/release/deps/libcargo-xxxx.rlib`,
190-
// so if the features are equal we can just skip it.
191-
let prev_no_hash = prev.1.parent().unwrap().ends_with("release/deps");
192-
let val_no_hash = val.1.parent().unwrap().ends_with("release/deps");
193-
if prev.2 == val.2 || !prev_no_hash || !val_no_hash {
194-
return;
195-
}
196-
197-
// ... and otherwise this looks like we duplicated some sort of
198-
// compilation, so record it to generate an error later.
199-
duplicates.push((id.to_string(), val, prev.clone()));
200-
}
201-
});
202-
203-
if is_expected && !duplicates.is_empty() {
204-
eprintln!(
205-
"duplicate artifacts found when compiling a tool, this \
206-
typically means that something was recompiled because \
207-
a transitive dependency has different features activated \
208-
than in a previous build:\n"
209-
);
210-
let (same, different): (Vec<_>, Vec<_>) =
211-
duplicates.into_iter().partition(|(_, cur, prev)| cur.2 == prev.2);
212-
if !same.is_empty() {
213-
eprintln!(
214-
"the following dependencies are duplicated although they \
215-
have the same features enabled:"
216-
);
217-
for (id, cur, prev) in same {
218-
eprintln!(" {}", id);
219-
// same features
220-
eprintln!(" `{}` ({:?})\n `{}` ({:?})", cur.0, cur.1, prev.0, prev.1);
221-
}
222-
}
223-
if !different.is_empty() {
224-
eprintln!("the following dependencies have different features:");
225-
for (id, cur, prev) in different {
226-
eprintln!(" {}", id);
227-
let cur_features: HashSet<_> = cur.2.into_iter().collect();
228-
let prev_features: HashSet<_> = prev.2.into_iter().collect();
229-
eprintln!(
230-
" `{}` additionally enabled features {:?} at {:?}",
231-
cur.0,
232-
&cur_features - &prev_features,
233-
cur.1
234-
);
235-
eprintln!(
236-
" `{}` additionally enabled features {:?} at {:?}",
237-
prev.0,
238-
&prev_features - &cur_features,
239-
prev.1
240-
);
241-
}
242-
}
243-
eprintln!();
244-
eprintln!(
245-
"to fix this you will probably want to edit the local \
246-
src/tools/rustc-workspace-hack/Cargo.toml crate, as \
247-
that will update the dependency graph to ensure that \
248-
these crates all share the same feature set"
249-
);
250-
panic!("tools should not compile multiple copies of the same crate");
251-
}
123+
let mut cargo = Command::from(cargo);
124+
let is_expected = builder.try_run_quiet(&mut cargo);
252125

253126
builder.save_toolstate(
254127
tool,
@@ -299,7 +172,9 @@ pub fn prepare_tool_cargo(
299172
|| path.ends_with("rustfmt")
300173
{
301174
cargo.env("LIBZ_SYS_STATIC", "1");
302-
features.push("rustc-workspace-hack/all-static".to_string());
175+
}
176+
if path.ends_with("cargo") {
177+
features.push("all-static".to_string());
303178
}
304179
}
305180

src/tools/rls/Cargo.toml

-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,3 @@ license = "Apache-2.0/MIT"
77
[dependencies]
88
serde = { version = "1.0.143", features = ["derive"] }
99
serde_json = "1.0.83"
10-
# A noop dependency that changes in the Rust repository, it's a bit of a hack.
11-
# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
12-
# for more information.
13-
rustc-workspace-hack = "1.0.0"

0 commit comments

Comments
 (0)