Skip to content

Commit b5354b5

Browse files
authored
Do not lock the artifact-dir for check builds (#16230)
### What does this PR try to resolve? This PR modifies the locking logic to avoid locking `artifact-dir` for check builds. Part of #4282 Note: This change is **not** behind `-Zbuild-dir-new-layout` or `-Zfine-grain-locking` unstable flags. ### How to test and review this PR? See the tests included in the PR. You can run `cargo check` to verify. r? @epage
2 parents 745aae6 + 9f5393a commit b5354b5

File tree

12 files changed

+163
-67
lines changed

12 files changed

+163
-67
lines changed

src/cargo/core/compiler/build_context/target_info.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1181,7 +1181,14 @@ impl RustDocFingerprint {
11811181
.bcx
11821182
.all_kinds
11831183
.iter()
1184-
.map(|kind| build_runner.files().layout(*kind).artifact_dir().doc())
1184+
.map(|kind| {
1185+
build_runner
1186+
.files()
1187+
.layout(*kind)
1188+
.artifact_dir()
1189+
.expect("artifact-dir was not locked")
1190+
.doc()
1191+
})
11851192
.filter(|path| path.exists())
11861193
.try_for_each(|path| clean_doc(path))?;
11871194
write_fingerprint()?;

src/cargo/core/compiler/build_runner/compilation_files.rs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,11 @@ impl<'a, 'gctx: 'a> CompilationFiles<'a, 'gctx> {
211211
// Docscrape units need to have doc/ set as the out_dir so sources for reverse-dependencies
212212
// will be put into doc/ and not into deps/ where the *.examples files are stored.
213213
if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
214-
self.layout(unit.kind).artifact_dir().doc().to_path_buf()
214+
self.layout(unit.kind)
215+
.artifact_dir()
216+
.expect("artifact-dir was not locked")
217+
.doc()
218+
.to_path_buf()
215219
} else if unit.mode.is_doc_test() {
216220
panic!("doc tests do not have an out dir");
217221
} else if unit.target.is_custom_build() {
@@ -249,8 +253,8 @@ impl<'a, 'gctx: 'a> CompilationFiles<'a, 'gctx> {
249253
}
250254

251255
/// Returns the final artifact path for the host (`/…/target/debug`)
252-
pub fn host_dest(&self) -> &Path {
253-
self.host.artifact_dir().dest()
256+
pub fn host_dest(&self) -> Option<&Path> {
257+
self.host.artifact_dir().map(|v| v.dest())
254258
}
255259

256260
/// Returns the root of the build output tree for the host (`/…/build-dir`)
@@ -283,8 +287,8 @@ impl<'a, 'gctx: 'a> CompilationFiles<'a, 'gctx> {
283287
}
284288

285289
/// Directory where timing output should go.
286-
pub fn timings_dir(&self) -> &Path {
287-
self.host.artifact_dir().timings()
290+
pub fn timings_dir(&self) -> Option<&Path> {
291+
self.host.artifact_dir().map(|v| v.timings())
288292
}
289293

290294
/// Returns the path for a file in the fingerprint directory.
@@ -378,9 +382,11 @@ impl<'a, 'gctx: 'a> CompilationFiles<'a, 'gctx> {
378382
target: &Target,
379383
kind: CompileKind,
380384
bcx: &BuildContext<'_, '_>,
381-
) -> CargoResult<PathBuf> {
385+
) -> CargoResult<Option<PathBuf>> {
382386
assert!(target.is_bin());
383-
let dest = self.layout(kind).artifact_dir().dest();
387+
let Some(dest) = self.layout(kind).artifact_dir().map(|v| v.dest()) else {
388+
return Ok(None);
389+
};
384390
let info = bcx.target_data.info(kind);
385391
let (file_types, _) = info
386392
.rustc_outputs(
@@ -396,7 +402,7 @@ impl<'a, 'gctx: 'a> CompilationFiles<'a, 'gctx> {
396402
.find(|file_type| file_type.flavor == FileFlavor::Normal)
397403
.expect("target must support `bin`");
398404

399-
Ok(dest.join(file_type.uplift_filename(target)))
405+
Ok(Some(dest.join(file_type.uplift_filename(target))))
400406
}
401407

402408
/// Returns the filenames that the given unit will generate.
@@ -450,12 +456,17 @@ impl<'a, 'gctx: 'a> CompilationFiles<'a, 'gctx> {
450456
// Examples live in their own little world.
451457
self.layout(unit.kind)
452458
.artifact_dir()
459+
.expect("artifact-dir was not locked")
453460
.examples()
454461
.join(filename)
455462
} else if unit.target.is_custom_build() {
456463
self.build_script_dir(unit).join(filename)
457464
} else {
458-
self.layout(unit.kind).artifact_dir().dest().join(filename)
465+
self.layout(unit.kind)
466+
.artifact_dir()
467+
.expect("artifact-dir was not locked")
468+
.dest()
469+
.join(filename)
459470
};
460471
if from_path == uplift_path {
461472
// This can happen with things like examples that reside in the

src/cargo/core/compiler/build_runner/mod.rs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::sync::{Arc, Mutex};
66

77
use crate::core::PackageId;
88
use crate::core::compiler::compilation::{self, UnitOutput};
9-
use crate::core::compiler::{self, Unit, artifact};
9+
use crate::core::compiler::{self, Unit, UserIntent, artifact};
1010
use crate::util::cache_lock::CacheLockMode;
1111
use crate::util::errors::CargoResult;
1212
use annotate_snippets::{Level, Message};
@@ -352,11 +352,32 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> {
352352
#[tracing::instrument(skip_all)]
353353
pub fn prepare_units(&mut self) -> CargoResult<()> {
354354
let dest = self.bcx.profiles.get_dir_name();
355-
let host_layout = Layout::new(self.bcx.ws, None, &dest)?;
355+
// We try to only lock the artifact-dir if we need to.
356+
// For example, `cargo check` does not write any files to the artifact-dir so we don't need
357+
// to lock it.
358+
let must_take_artifact_dir_lock = match self.bcx.build_config.intent {
359+
UserIntent::Check { .. } => {
360+
// Generally cargo check does not need to take the artifact-dir lock but there is
361+
// one exception: If check has `--timings` we still need to lock artifact-dir since
362+
// we will output the report files.
363+
!self.bcx.build_config.timing_outputs.is_empty()
364+
}
365+
UserIntent::Build
366+
| UserIntent::Test
367+
| UserIntent::Doc { .. }
368+
| UserIntent::Doctest
369+
| UserIntent::Bench => true,
370+
};
371+
let host_layout = Layout::new(self.bcx.ws, None, &dest, must_take_artifact_dir_lock)?;
356372
let mut targets = HashMap::new();
357373
for kind in self.bcx.all_kinds.iter() {
358374
if let CompileKind::Target(target) = *kind {
359-
let layout = Layout::new(self.bcx.ws, Some(target), &dest)?;
375+
let layout = Layout::new(
376+
self.bcx.ws,
377+
Some(target),
378+
&dest,
379+
must_take_artifact_dir_lock,
380+
)?;
360381
targets.insert(target, layout);
361382
}
362383
}
@@ -392,9 +413,11 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> {
392413
let files = self.files.as_ref().unwrap();
393414
for &kind in self.bcx.all_kinds.iter() {
394415
let layout = files.layout(kind);
395-
self.compilation
396-
.root_output
397-
.insert(kind, layout.artifact_dir().dest().to_path_buf());
416+
if let Some(artifact_dir) = layout.artifact_dir() {
417+
self.compilation
418+
.root_output
419+
.insert(kind, artifact_dir.dest().to_path_buf());
420+
}
398421
if self.bcx.gctx.cli_unstable().build_dir_new_layout {
399422
for (unit, _) in self.bcx.unit_graph.iter() {
400423
let dep_dir = self.files().deps_dir(unit);

src/cargo/core/compiler/compilation.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -305,12 +305,14 @@ impl<'gctx> Compilation<'gctx> {
305305
}
306306
search_path.push(self.deps_output[&CompileKind::Host].clone());
307307
} else {
308-
search_path.extend(super::filter_dynamic_search_path(
309-
self.native_dirs.iter(),
310-
&self.root_output[&kind],
311-
));
308+
if let Some(path) = self.root_output.get(&kind) {
309+
search_path.extend(super::filter_dynamic_search_path(
310+
self.native_dirs.iter(),
311+
path,
312+
));
313+
search_path.push(path.clone());
314+
}
312315
search_path.push(self.deps_output[&kind].clone());
313-
search_path.push(self.root_output[&kind].clone());
314316
// For build-std, we don't want to accidentally pull in any shared
315317
// libs from the sysroot that ships with rustc. This may not be
316318
// required (at least I cannot craft a situation where it

src/cargo/core/compiler/custom_build.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResul
473473
let output_file = script_run_dir.join("output");
474474
let err_file = script_run_dir.join("stderr");
475475
let root_output_file = script_run_dir.join("root-output");
476-
let host_target_root = build_runner.files().host_dest().to_path_buf();
476+
let host_target_root = build_runner.files().host_dest().map(|v| v.to_path_buf());
477477
let all = (
478478
id,
479479
library_name.clone(),
@@ -541,12 +541,14 @@ fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResul
541541
);
542542
}
543543
}
544-
if let Some(build_scripts) = build_scripts {
544+
if let Some(build_scripts) = build_scripts
545+
&& let Some(ref host_target_root) = host_target_root
546+
{
545547
super::add_plugin_deps(
546548
&mut cmd,
547549
&build_script_outputs,
548550
&build_scripts,
549-
&host_target_root,
551+
host_target_root,
550552
)?;
551553
}
552554
}

src/cargo/core/compiler/layout.rs

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ use std::path::{Path, PathBuf};
112112
///
113113
/// See module docs for more information.
114114
pub struct Layout {
115-
artifact_dir: ArtifactDirLayout,
115+
artifact_dir: Option<ArtifactDirLayout>,
116116
build_dir: BuildDirLayout,
117117
}
118118

@@ -127,6 +127,7 @@ impl Layout {
127127
ws: &Workspace<'_>,
128128
target: Option<CompileTarget>,
129129
dest: &str,
130+
must_take_artifact_dir_lock: bool,
130131
) -> CargoResult<Layout> {
131132
let is_new_layout = ws.gctx().cli_unstable().build_dir_new_layout;
132133
let mut root = ws.target_dir();
@@ -150,15 +151,6 @@ impl Layout {
150151
// actual destination (sub)subdirectory.
151152
paths::create_dir_all(dest.as_path_unlocked())?;
152153

153-
// For now we don't do any more finer-grained locking on the artifact
154-
// directory, so just lock the entire thing for the duration of this
155-
// compile.
156-
let artifact_dir_lock = if is_on_nfs_mount(root.as_path_unlocked()) {
157-
None
158-
} else {
159-
Some(dest.open_rw_exclusive_create(".cargo-lock", ws.gctx(), "artifact directory")?)
160-
};
161-
162154
let build_dir_lock = if root == build_root || is_on_nfs_mount(build_root.as_path_unlocked())
163155
{
164156
None
@@ -169,21 +161,38 @@ impl Layout {
169161
"build directory",
170162
)?)
171163
};
172-
let root = root.into_path_unlocked();
173164
let build_root = build_root.into_path_unlocked();
174-
let dest = dest.into_path_unlocked();
175165
let build_dest = build_dest.as_path_unlocked();
176166
let deps = build_dest.join("deps");
177167
let artifact = deps.join("artifact");
178168

179-
Ok(Layout {
180-
artifact_dir: ArtifactDirLayout {
169+
let artifact_dir = if must_take_artifact_dir_lock {
170+
// For now we don't do any more finer-grained locking on the artifact
171+
// directory, so just lock the entire thing for the duration of this
172+
// compile.
173+
let artifact_dir_lock = if is_on_nfs_mount(root.as_path_unlocked()) {
174+
None
175+
} else {
176+
Some(dest.open_rw_exclusive_create(
177+
".cargo-lock",
178+
ws.gctx(),
179+
"artifact directory",
180+
)?)
181+
};
182+
let root = root.into_path_unlocked();
183+
let dest = dest.into_path_unlocked();
184+
Some(ArtifactDirLayout {
181185
dest: dest.clone(),
182186
examples: dest.join("examples"),
183187
doc: root.join("doc"),
184188
timings: root.join("cargo-timings"),
185189
_lock: artifact_dir_lock,
186-
},
190+
})
191+
} else {
192+
None
193+
};
194+
Ok(Layout {
195+
artifact_dir,
187196
build_dir: BuildDirLayout {
188197
root: build_root.clone(),
189198
deps,
@@ -201,14 +210,16 @@ impl Layout {
201210

202211
/// Makes sure all directories stored in the Layout exist on the filesystem.
203212
pub fn prepare(&mut self) -> CargoResult<()> {
204-
self.artifact_dir.prepare()?;
213+
if let Some(ref mut artifact_dir) = self.artifact_dir {
214+
artifact_dir.prepare()?;
215+
}
205216
self.build_dir.prepare()?;
206217

207218
Ok(())
208219
}
209220

210-
pub fn artifact_dir(&self) -> &ArtifactDirLayout {
211-
&self.artifact_dir
221+
pub fn artifact_dir(&self) -> Option<&ArtifactDirLayout> {
222+
self.artifact_dir.as_ref()
212223
}
213224

214225
pub fn build_dir(&self) -> &BuildDirLayout {

src/cargo/core/compiler/mod.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ fn rustc(
298298
exec.init(build_runner, unit);
299299
let exec = exec.clone();
300300

301-
let root_output = build_runner.files().host_dest().to_path_buf();
301+
let root_output = build_runner.files().host_dest().map(|v| v.to_path_buf());
302302
let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
303303
let pkg_root = unit.pkg.root().to_path_buf();
304304
let cwd = rustc
@@ -361,7 +361,9 @@ fn rustc(
361361
current_id,
362362
mode,
363363
)?;
364-
add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?;
364+
if let Some(ref root_output) = root_output {
365+
add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, root_output)?;
366+
}
365367
add_custom_flags(&mut rustc, &script_outputs, script_metadatas)?;
366368
}
367369

@@ -1421,11 +1423,15 @@ fn build_base_args(
14211423
.iter()
14221424
.filter(|target| target.is_bin())
14231425
{
1424-
let exe_path = build_runner.files().bin_link_for_target(
1425-
bin_target,
1426-
unit.kind,
1427-
build_runner.bcx,
1428-
)?;
1426+
// For `cargo check` builds we do not uplift the CARGO_BIN_EXE_ artifacts to the
1427+
// artifact-dir. We do not want to provide a path to a non-existent binary but we still
1428+
// need to provide *something* so `env!("CARGO_BIN_EXE_...")` macros will compile.
1429+
let exe_path = build_runner
1430+
.files()
1431+
.bin_link_for_target(bin_target, unit.kind, build_runner.bcx)?
1432+
.map(|path| path.as_os_str().to_os_string())
1433+
.unwrap_or_else(|| OsString::from(format!("placeholder:{}", bin_target.name())));
1434+
14291435
let name = bin_target
14301436
.binary_filename()
14311437
.unwrap_or(bin_target.name().to_string());

src/cargo/core/compiler/timings.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,10 @@ impl<'gctx> Timings<'gctx> {
457457
) -> CargoResult<()> {
458458
let duration = self.start.elapsed().as_secs_f64();
459459
let timestamp = self.start_str.replace(&['-', ':'][..], "");
460-
let timings_path = build_runner.files().timings_dir();
460+
let timings_path = build_runner
461+
.files()
462+
.timings_dir()
463+
.expect("artifact-dir was not locked");
461464
paths::create_dir_all(&timings_path)?;
462465
let filename = timings_path.join(format!("cargo-timing-{}.html", timestamp));
463466
let mut f = BufWriter::new(paths::create(&filename)?);

src/cargo/ops/cargo_clean.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,17 @@ fn clean_specs(
116116
let target_data = RustcTargetData::new(ws, &requested_kinds)?;
117117
let (pkg_set, resolve) = ops::resolve_ws(ws, dry_run)?;
118118
let prof_dir_name = profiles.get_dir_name();
119-
let host_layout = Layout::new(ws, None, &prof_dir_name)?;
119+
let host_layout = Layout::new(ws, None, &prof_dir_name, true)?;
120120
// Convert requested kinds to a Vec of layouts.
121121
let target_layouts: Vec<(CompileKind, Layout)> = requested_kinds
122122
.into_iter()
123123
.filter_map(|kind| match kind {
124-
CompileKind::Target(target) => match Layout::new(ws, Some(target), &prof_dir_name) {
125-
Ok(layout) => Some(Ok((kind, layout))),
126-
Err(e) => Some(Err(e)),
127-
},
124+
CompileKind::Target(target) => {
125+
match Layout::new(ws, Some(target), &prof_dir_name, true) {
126+
Ok(layout) => Some(Ok((kind, layout))),
127+
Err(e) => Some(Err(e)),
128+
}
129+
}
128130
CompileKind::Host => None,
129131
})
130132
.collect::<CargoResult<_>>()?;
@@ -236,19 +238,18 @@ fn clean_specs(
236238
let (file_types, _unsupported) = target_data
237239
.info(*compile_kind)
238240
.rustc_outputs(mode, target.kind(), triple, clean_ctx.gctx)?;
241+
let artifact_dir = layout
242+
.artifact_dir()
243+
.expect("artifact-dir was not locked during clean");
239244
let (dir, uplift_dir) = match target.kind() {
240-
TargetKind::ExampleBin | TargetKind::ExampleLib(..) => (
241-
layout.build_dir().examples(),
242-
Some(layout.artifact_dir().examples()),
243-
),
245+
TargetKind::ExampleBin | TargetKind::ExampleLib(..) => {
246+
(layout.build_dir().examples(), Some(artifact_dir.examples()))
247+
}
244248
// Tests/benchmarks are never uplifted.
245249
TargetKind::Test | TargetKind::Bench => {
246250
(layout.build_dir().legacy_deps(), None)
247251
}
248-
_ => (
249-
layout.build_dir().legacy_deps(),
250-
Some(layout.artifact_dir().dest()),
251-
),
252+
_ => (layout.build_dir().legacy_deps(), Some(artifact_dir.dest())),
252253
};
253254
let mut dir_glob_str = escape_glob_path(dir)?;
254255
let dir_glob = Path::new(&dir_glob_str);

0 commit comments

Comments
 (0)