Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/bootstrap/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ insta = "1.43"
# dependencies, only bootstrap itself.
[profile.dev]
debug = 0
debug-assertions = false

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really measurable? Debug asserts could in theory help find some overflows or similar things. I don't think that we should trade this off.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The asserts showed up in profiles but I haven't measured the change in isolation, will do.

The thing is that we run bootstrap in dev as if it were our release profile... would adding a test profile which reenables them be sufficient?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well we only do that because of compile times, nothing else, really. I guess that we could only use assertions on CI, but I'd like to see if the assertions really make that much of a difference.


[profile.dev.package]
# Only use debuginfo=1 to further reduce compile times.
Expand Down
8 changes: 7 additions & 1 deletion src/bootstrap/src/core/build_steps/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1720,7 +1720,10 @@ HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to
}

builder.info("tidy check");
cmd.delay_failure().run(builder);
let ctx = builder.as_ref();
let mut cmd = cmd.delay_failure();
// spawn child in background so we can do additional work in parallel
let tidy = cmd.start(ctx);

builder.info("x.py completions check");
let completion_paths = get_completion_paths(builder);
Expand All @@ -1736,6 +1739,9 @@ HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to
helpers::exit_process(1);
}

// now wait for the child to complete
tidy.wait_for_output(ctx);

builder.info("x.py help check");
if builder.config.cmd.bless() {
builder.ensure(crate::core::build_steps::run::GenerateHelp);
Expand Down
5 changes: 5 additions & 0 deletions src/bootstrap/src/utils/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,11 @@ impl<'a> BootstrapCommand {
self
}

#[track_caller]
pub fn start(&mut self, exec_ctx: impl AsRef<ExecutionContext>) -> DeferredCommand<'_> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is already the same function called start_capture. I think that you don't want to capture here, though? 🤔

exec_ctx.as_ref().start(self, OutputMode::Capture, OutputMode::Capture)
}

/// Run the command, while printing stdout and stderr.
/// Returns true if the command has succeeded.
#[track_caller]
Expand Down
2 changes: 1 addition & 1 deletion src/tools/tidy/src/bins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ mod os_impl {

#[cfg(unix)]
pub fn check(path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check("bins");
let check = tidy_ctx.start_check("bins");

use std::ffi::OsStr;

Expand Down
2 changes: 1 addition & 1 deletion src/tools/tidy/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn has_supported_extension(path: &Path) -> bool {
}

pub fn check(path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("codegen").path(path));
let check = tidy_ctx.start_check(CheckId::new("codegen").path(path));

fn skip(path: &Path, is_dir: bool) -> bool {
if path.file_name().is_some_and(|name| name.to_string_lossy().starts_with(".#")) {
Expand Down
2 changes: 1 addition & 1 deletion src/tools/tidy/src/debug_artifacts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::walk::{filter_dirs, filter_not_rust, walk};
const GRAPHVIZ_POSTFLOW_MSG: &str = "`borrowck_graphviz_postflow` attribute in test";

pub fn check(test_dir: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("debug_artifacts").path(test_dir));
let check = tidy_ctx.start_check(CheckId::new("debug_artifacts").path(test_dir));

walk(
test_dir,
Expand Down
120 changes: 70 additions & 50 deletions src/tools/tidy/src/deps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
use std::collections::{BTreeSet, HashMap, HashSet};
use std::fmt::{Display, Formatter};
use std::fs::{self, read_dir};
use std::io;
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::Scope;
use std::{io, thread};

use cargo_metadata::semver::Version;
use cargo_metadata::{Metadata, Package, PackageId};
Expand Down Expand Up @@ -630,62 +632,80 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
///
/// `root` is path to the directory with the root `Cargo.toml` (for the workspace). `cargo` is path
/// to the cargo executable.
pub fn check(root: &Path, cargo: &Path, tidy_ctx: TidyCtx) {
pub fn check(
root: &Path,
cargo: &Path,
_scope: &'_ Scope<'_, '_>,
sem: &'_ crate::Semaphore,
tidy_ctx: TidyCtx,
) {
let mut check = tidy_ctx.start_check("deps");
let bless = tidy_ctx.is_bless_enabled();
let is_ci = tidy_ctx.is_running_on_ci();

let mut checked_runtime_licenses = false;
let checked_runtime_licenses = AtomicBool::new(false);

check_proc_macro_dep_list(root, cargo, bless, &mut check);

for &WorkspaceInfo { path, exceptions, crates_and_deps, submodules } in WORKSPACES {
if has_missing_submodule(root, submodules, tidy_ctx.is_running_on_ci()) {
continue;
}
thread::scope(|s| {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this create a new scope when one scope is already passed in?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some leftover from trying different approaches. I'll remove either the local scope or the passed argument.

for (i, WorkspaceInfo { path, exceptions, crates_and_deps, submodules }) in
WORKSPACES.iter().enumerate()
{
let check = &check;
let checked_runtime_licenses = &checked_runtime_licenses;
let guard = if i > 0 { Some(sem.acquire()) } else { None };

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the semaphore not acquired for the first workspace?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If concurrency==1 then that would hang because the deps module itself already takes up one task slot.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, then please add this as a comment on top of this line.

s.spawn(move || {
if has_missing_submodule(root, submodules, is_ci) {
return;
}

if !root.join(path).join("Cargo.lock").exists() {
check.error(format!("the `{path}` workspace doesn't have a Cargo.lock"));
continue;
}
if !root.join(path).join("Cargo.lock").exists() {
check.error(format!("the `{path}` workspace doesn't have a Cargo.lock"));
return;
}

let mut cmd = cargo_metadata::MetadataCommand::new();
cmd.cargo_path(cargo)
.manifest_path(root.join(path).join("Cargo.toml"))
.features(cargo_metadata::CargoOpt::AllFeatures)
.other_options(vec!["--locked".to_owned()]);
let metadata = t!(cmd.exec());

// Check for packages which have been moved into a different workspace and not updated
let absolute_root =
if path == "." { root.to_path_buf() } else { t!(std::path::absolute(root.join(path))) };
let absolute_root_real = t!(std::path::absolute(&metadata.workspace_root));
if absolute_root_real != absolute_root {
check.error(format!("{path} is part of another workspace ({} != {}), remove from `WORKSPACES` ({WORKSPACE_LOCATION})", absolute_root.display(), absolute_root_real.display()));
}
check_license_exceptions(&metadata, path, exceptions, &mut check);
if let Some((crates, permitted_deps, location)) = crates_and_deps {
let descr = crates.get(0).unwrap_or(&path);
check_permitted_dependencies(
&metadata,
descr,
permitted_deps,
crates,
location,
&mut check,
);
}
let mut cmd = cargo_metadata::MetadataCommand::new();
cmd.cargo_path(cargo)
.manifest_path(root.join(path).join("Cargo.toml"))
.features(cargo_metadata::CargoOpt::AllFeatures)
.other_options(vec!["--locked".to_owned()]);
let metadata = t!(cmd.exec());

// Check for packages which have been moved into a different workspace and not updated
let absolute_root =
if *path == "." { root.to_path_buf() } else { t!(std::path::absolute(root.join(path))) };
let absolute_root_real = t!(std::path::absolute(&metadata.workspace_root));
if absolute_root_real != absolute_root {
check.error(format!("{path} is part of another workspace ({} != {}), remove from `WORKSPACES` ({WORKSPACE_LOCATION})", absolute_root.display(), absolute_root_real.display()));
}
check_license_exceptions(&metadata, path, exceptions, &check);
if let Some((crates, permitted_deps, location)) = &crates_and_deps {
let descr = crates.get(0).unwrap_or(&path);
check_permitted_dependencies(
&metadata,
descr,
permitted_deps,
crates,
location,
&check,
);
}

if path == "library" {
check_runtime_license_exceptions(&metadata, &mut check);
check_runtime_no_duplicate_dependencies(&metadata, &mut check);
check_runtime_no_proc_macros(&metadata, &mut check);
checked_runtime_licenses = true;
if *path == "library" {
check_runtime_license_exceptions(&metadata, &check);
check_runtime_no_duplicate_dependencies(&metadata, &check);
check_runtime_no_proc_macros(&metadata, &check);
checked_runtime_licenses.store(true, Ordering::Relaxed);
}

let _guard = guard;
});
}
}
});

// Sanity check to ensure we don't accidentally remove the workspace containing the runtime
// crates.
assert!(checked_runtime_licenses);
assert!(checked_runtime_licenses.load(Ordering::Relaxed));
}

/// Ensure the list of proc-macro crate transitive dependencies is up to date
Expand Down Expand Up @@ -788,7 +808,7 @@ pub fn has_missing_submodule(root: &Path, submodules: &[&str], is_ci: bool) -> b
///
/// Unlike for tools we don't allow exceptions to the `LICENSES` list for the runtime with the sole
/// exception of `fortanix-sgx-abi` which is only used on x86_64-fortanix-unknown-sgx.
fn check_runtime_license_exceptions(metadata: &Metadata, check: &mut RunningCheck) {
fn check_runtime_license_exceptions(metadata: &Metadata, check: &RunningCheck) {
for pkg in &metadata.packages {
if pkg.source.is_none() {
// No need to check local packages.
Expand Down Expand Up @@ -823,7 +843,7 @@ fn check_license_exceptions(
metadata: &Metadata,
workspace: &str,
exceptions: &[(&str, &str)],
check: &mut RunningCheck,
check: &RunningCheck,
) {
// Validate the EXCEPTIONS list hasn't changed.
for (name, license) in exceptions {
Expand Down Expand Up @@ -890,7 +910,7 @@ fn check_license_exceptions(
}
}

fn check_runtime_no_duplicate_dependencies(metadata: &Metadata, check: &mut RunningCheck) {
fn check_runtime_no_duplicate_dependencies(metadata: &Metadata, check: &RunningCheck) {
let mut seen_pkgs = HashSet::new();
for pkg in &metadata.packages {
if pkg.source.is_none() {
Expand All @@ -906,7 +926,7 @@ fn check_runtime_no_duplicate_dependencies(metadata: &Metadata, check: &mut Runn
}
}

fn check_runtime_no_proc_macros(metadata: &Metadata, check: &mut RunningCheck) {
fn check_runtime_no_proc_macros(metadata: &Metadata, check: &RunningCheck) {
for pkg in &metadata.packages {
if pkg.targets.iter().any(|target| target.is_proc_macro()) {
check.error(format!(
Expand All @@ -928,8 +948,8 @@ fn check_permitted_dependencies(
descr: &str,
permitted_dependencies: &[&'static str],
restricted_dependency_crates: &[&'static str],
permitted_location: ListLocation,
check: &mut RunningCheck,
permitted_location: &ListLocation,
check: &RunningCheck,
) {
let mut has_permitted_dep_error = false;
let mut deps = HashSet::new();
Expand Down
32 changes: 18 additions & 14 deletions src/tools/tidy/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::collections::HashSet;
use std::fmt::{Display, Formatter};
use std::io;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};

use build_helper::ci::CiEnv;
Expand Down Expand Up @@ -86,10 +87,10 @@ impl TidyCtx {
ctx.start_check(id.clone());
RunningCheck {
id,
bad: false,
bad: AtomicBool::new(false),
ctx: self.diag_ctx.clone(),
#[cfg(test)]
errors: vec![],
errors: Mutex::new(vec![]),
}
}

Expand Down Expand Up @@ -228,10 +229,10 @@ impl FinishedCheck {
/// Represents a single tidy check, identified by its `name`, running.
pub struct RunningCheck {
id: CheckId,
bad: bool,
bad: AtomicBool,
ctx: Arc<Mutex<DiagCtxInner>>,
#[cfg(test)]
errors: Vec<String>,
errors: Mutex<Vec<String>>,
}

impl RunningCheck {
Expand All @@ -245,34 +246,34 @@ impl RunningCheck {
}

/// Immediately output an error and mark the check as failed.
pub fn error<T: Display>(&mut self, msg: T) {
pub fn error<T: Display>(&self, msg: T) {
self.mark_as_bad();
let msg = msg.to_string();
output_message(&msg, Some(&self.id), Some(COLOR_ERROR));
#[cfg(test)]
self.errors.push(msg);
self.errors.lock().unwrap().push(msg);
}

/// Immediately output a warning.
pub fn warning<T: Display>(&mut self, msg: T) {
pub fn warning<T: Display>(&self, msg: T) {
output_message(&msg.to_string(), Some(&self.id), Some(COLOR_WARNING));
}

/// Output an informational message
pub fn message<T: Display>(&mut self, msg: T) {
pub fn message<T: Display>(&self, msg: T) {
output_message(&msg.to_string(), Some(&self.id), None);
}

/// Output a message only if verbose output is enabled.
pub fn verbose_msg<T: Display>(&mut self, msg: T) {
pub fn verbose_msg<T: Display>(&self, msg: T) {
if self.is_verbose_enabled() {
self.message(msg);
}
}

/// Has an error already occurred for this check?
pub fn is_bad(&self) -> bool {
self.bad
self.bad.load(Ordering::Relaxed)
}

/// Is verbose output enabled?
Expand All @@ -282,17 +283,20 @@ impl RunningCheck {

#[cfg(test)]
pub fn get_errors(&self) -> Vec<String> {
self.errors.clone()
self.errors.lock().unwrap().clone()
}

fn mark_as_bad(&mut self) {
self.bad = true;
fn mark_as_bad(&self) {
self.bad.store(true, Ordering::Relaxed);
}
}

impl Drop for RunningCheck {
fn drop(&mut self) {
self.ctx.lock().unwrap().finish_check(FinishedCheck { id: self.id.clone(), bad: self.bad })
self.ctx.lock().unwrap().finish_check(FinishedCheck {
id: self.id.clone(),
bad: self.bad.load(Ordering::Relaxed),
})
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/tools/tidy/src/edition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::diagnostics::{CheckId, TidyCtx};
use crate::walk::{filter_dirs, walk};

pub fn check(path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("edition").path(path));
let check = tidy_ctx.start_check(CheckId::new("edition").path(path));
walk(path, |path, _is_dir| filter_dirs(path), &mut |entry, contents| {
let file = entry.path();
let filename = file.file_name().unwrap();
Expand Down
2 changes: 1 addition & 1 deletion src/tools/tidy/src/extdeps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const ALLOWED_SOURCES: &[&str] = &[
/// Checks for external package sources. `root` is the path to the directory that contains the
/// workspace `Cargo.toml`.
pub fn check(root: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check("extdeps");
let check = tidy_ctx.start_check("extdeps");

for &WorkspaceInfo { path, submodules, .. } in crate::deps::WORKSPACES {
if crate::deps::has_missing_submodule(root, submodules, tidy_ctx.is_running_on_ci()) {
Expand Down
Loading
Loading