diff --git a/src/bin/cargo/commands/build.rs b/src/bin/cargo/commands/build.rs index adfc4844f62..51b019d4382 100644 --- a/src/bin/cargo/commands/build.rs +++ b/src/bin/cargo/commands/build.rs @@ -38,6 +38,7 @@ pub fn cli() -> Command { .arg_build_plan() .arg_unit_graph() .arg_timings() + .arg_compile_time_deps() .arg_manifest_path() .arg_lockfile_path() .arg_ignore_rust_version() diff --git a/src/bin/cargo/commands/check.rs b/src/bin/cargo/commands/check.rs index c97767e844c..957195498ab 100644 --- a/src/bin/cargo/commands/check.rs +++ b/src/bin/cargo/commands/check.rs @@ -35,6 +35,7 @@ pub fn cli() -> Command { .arg_target_dir() .arg_unit_graph() .arg_timings() + .arg_compile_time_deps() .arg_manifest_path() .arg_lockfile_path() .arg_ignore_rust_version() diff --git a/src/cargo/core/compiler/build_config.rs b/src/cargo/core/compiler/build_config.rs index 387098a2e3a..e042f21e21f 100644 --- a/src/cargo/core/compiler/build_config.rs +++ b/src/cargo/core/compiler/build_config.rs @@ -50,6 +50,8 @@ pub struct BuildConfig { pub timing_outputs: Vec, /// Output SBOM precursor files. pub sbom: bool, + /// Build compile time dependencies only, e.g., build scripts and proc macros + pub compile_time_deps_only: bool, } fn default_parallelism() -> CargoResult { @@ -129,6 +131,7 @@ impl BuildConfig { future_incompat_report: false, timing_outputs: Vec::new(), sbom, + compile_time_deps_only: false, }) } diff --git a/src/cargo/core/compiler/build_runner/compilation_files.rs b/src/cargo/core/compiler/build_runner/compilation_files.rs index 1892e8775bb..f70a870cf46 100644 --- a/src/cargo/core/compiler/build_runner/compilation_files.rs +++ b/src/cargo/core/compiler/build_runner/compilation_files.rs @@ -448,6 +448,10 @@ impl<'a, 'gctx: 'a> CompilationFiles<'a, 'gctx> { bcx: &BuildContext<'a, 'gctx>, ) -> CargoResult>> { let ret = match unit.mode { + _ if unit.skip_non_compile_time_dep => { + // This skips compilations so no outputs + vec![] + } CompileMode::Doc => { let path = if bcx.build_config.intent.wants_doc_json_output() { self.out_dir(unit) diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 63857fea800..9bd925a207e 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -180,50 +180,55 @@ fn compile<'gctx>( return Ok(()); } - // Build up the work to be done to compile this unit, enqueuing it once - // we've got everything constructed. - fingerprint::prepare_init(build_runner, unit)?; - - let job = if unit.mode.is_run_custom_build() { - custom_build::prepare(build_runner, unit)? - } else if unit.mode.is_doc_test() { - // We run these targets later, so this is just a no-op for now. - Job::new_fresh() - } else if build_plan { - Job::new_dirty( - rustc(build_runner, unit, &exec.clone())?, - DirtyReason::FreshBuild, - ) - } else { - let force = exec.force_rebuild(unit) || force_rebuild; - let mut job = fingerprint::prepare_target(build_runner, unit, force)?; - job.before(if job.freshness().is_dirty() { - let work = if unit.mode.is_doc() || unit.mode.is_doc_scrape() { - rustdoc(build_runner, unit)? - } else { - rustc(build_runner, unit, exec)? - }; - work.then(link_targets(build_runner, unit, false)?) + // If we are in `--compile-time-deps` and the given unit is not a compile time + // dependency, skip compling the unit and jumps to dependencies, which still + // have chances to be compile time dependencies + if !unit.skip_non_compile_time_dep { + // Build up the work to be done to compile this unit, enqueuing it once + // we've got everything constructed. + fingerprint::prepare_init(build_runner, unit)?; + + let job = if unit.mode.is_run_custom_build() { + custom_build::prepare(build_runner, unit)? + } else if unit.mode.is_doc_test() { + // We run these targets later, so this is just a no-op for now. + Job::new_fresh() + } else if build_plan { + Job::new_dirty( + rustc(build_runner, unit, &exec.clone())?, + DirtyReason::FreshBuild, + ) } else { - // We always replay the output cache, - // since it might contain future-incompat-report messages - let show_diagnostics = unit.show_warnings(bcx.gctx) - && build_runner.bcx.gctx.warning_handling()? != WarningHandling::Allow; - let work = replay_output_cache( - unit.pkg.package_id(), - PathBuf::from(unit.pkg.manifest_path()), - &unit.target, - build_runner.files().message_cache_path(unit), - build_runner.bcx.build_config.message_format, - show_diagnostics, - ); - // Need to link targets on both the dirty and fresh. - work.then(link_targets(build_runner, unit, true)?) - }); - - job - }; - jobs.enqueue(build_runner, unit, job)?; + let force = exec.force_rebuild(unit) || force_rebuild; + let mut job = fingerprint::prepare_target(build_runner, unit, force)?; + job.before(if job.freshness().is_dirty() { + let work = if unit.mode.is_doc() || unit.mode.is_doc_scrape() { + rustdoc(build_runner, unit)? + } else { + rustc(build_runner, unit, exec)? + }; + work.then(link_targets(build_runner, unit, false)?) + } else { + // We always replay the output cache, + // since it might contain future-incompat-report messages + let show_diagnostics = unit.show_warnings(bcx.gctx) + && build_runner.bcx.gctx.warning_handling()? != WarningHandling::Allow; + let work = replay_output_cache( + unit.pkg.package_id(), + PathBuf::from(unit.pkg.manifest_path()), + &unit.target, + build_runner.files().message_cache_path(unit), + build_runner.bcx.build_config.message_format, + show_diagnostics, + ); + // Need to link targets on both the dirty and fresh. + work.then(link_targets(build_runner, unit, true)?) + }); + + job + }; + jobs.enqueue(build_runner, unit, job)?; + } // Be sure to compile all dependencies of this target as well. let deps = Vec::from(build_runner.unit_deps(unit)); // Create vec due to mutable borrow. diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs index 18253b5d465..30f7c1815fd 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -210,6 +210,7 @@ fn generate_roots( /*dep_hash*/ 0, IsArtifact::No, None, + false, )); } } diff --git a/src/cargo/core/compiler/unit.rs b/src/cargo/core/compiler/unit.rs index 631353aaeff..43068b7f5ac 100644 --- a/src/cargo/core/compiler/unit.rs +++ b/src/cargo/core/compiler/unit.rs @@ -112,6 +112,13 @@ pub struct UnitInner { /// /// [`FeaturesFor::ArtifactDep`]: crate::core::resolver::features::FeaturesFor::ArtifactDep pub artifact_target_for_features: Option, + + /// Skip compiling this unit because `--compile-time-deps` flag is set and + /// this is not a compile time dependency. + /// + /// Since dependencies of this unit might be compile time dependencies, we + /// set this field instead of completely dropping out this unit from unit graph. + pub skip_non_compile_time_dep: bool, } impl UnitInner { @@ -245,6 +252,7 @@ impl UnitInterner { dep_hash: u64, artifact: IsArtifact, artifact_target_for_features: Option, + skip_non_compile_time_dep: bool, ) -> Unit { let target = match (is_std, target.kind()) { // This is a horrible hack to support build-std. `libstd` declares @@ -281,6 +289,7 @@ impl UnitInterner { dep_hash, artifact, artifact_target_for_features, + skip_non_compile_time_dep, }); Unit { inner } } diff --git a/src/cargo/core/compiler/unit_dependencies.rs b/src/cargo/core/compiler/unit_dependencies.rs index 3255c820861..8f71156104e 100644 --- a/src/cargo/core/compiler/unit_dependencies.rs +++ b/src/cargo/core/compiler/unit_dependencies.rs @@ -866,6 +866,7 @@ fn new_unit_dep_with_profile( /*dep_hash*/ 0, artifact.map_or(IsArtifact::No, |_| IsArtifact::Yes), artifact_target, + false, ); Ok(UnitDep { unit, diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index f85f0ff736e..18bd6210215 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -1079,6 +1079,11 @@ impl Target { *self.kind() == TargetKind::CustomBuild } + /// Returns `true` if it is a compile time depencencies, e.g., build script or proc macro + pub fn is_compile_time_dependency(&self) -> bool { + self.is_custom_build() || self.proc_macro() + } + /// Returns the arguments suitable for `--crate-type` to pass to rustc. pub fn rustc_crate_types(&self) -> Vec { self.kind().rustc_crate_types() diff --git a/src/cargo/ops/cargo_compile/mod.rs b/src/cargo/ops/cargo_compile/mod.rs index 6a406cc9227..366056ef288 100644 --- a/src/cargo/ops/cargo_compile/mod.rs +++ b/src/cargo/ops/cargo_compile/mod.rs @@ -449,6 +449,7 @@ pub fn create_bcx<'a, 'gctx>( &units, &scrape_units, host_kind_requested.then_some(explicit_host_kind), + build_config.compile_time_deps_only, ); let mut extra_compiler_args = HashMap::new(); @@ -582,12 +583,16 @@ where `` is the latest version supporting rustc {rustc_version}" /// This is also responsible for adjusting the `debug` setting for host /// dependencies, turning off debug if the user has not explicitly enabled it, /// and the unit is not shared with a target unit. +/// +/// This is also responsible for adjusting whether each unit should be compiled +/// or not regarding `--compile-time-deps` flag. fn rebuild_unit_graph_shared( interner: &UnitInterner, unit_graph: UnitGraph, roots: &[Unit], scrape_units: &[Unit], to_host: Option, + compile_time_deps_only: bool, ) -> (Vec, Vec, UnitGraph) { let mut result = UnitGraph::new(); // Map of the old unit to the new unit, used to avoid recursing into units @@ -602,8 +607,10 @@ fn rebuild_unit_graph_shared( &mut result, &unit_graph, root, + true, false, to_host, + compile_time_deps_only, ) }) .collect(); @@ -628,14 +635,21 @@ fn traverse_and_share( new_graph: &mut UnitGraph, unit_graph: &UnitGraph, unit: &Unit, + unit_is_root: bool, unit_is_for_host: bool, to_host: Option, + compile_time_deps_only: bool, ) -> Unit { if let Some(new_unit) = memo.get(unit) { // Already computed, no need to recompute. return new_unit.clone(); } let mut dep_hash = StableHasher::new(); + let skip_non_compile_time_deps = compile_time_deps_only + && (!unit.target.is_compile_time_dependency() || + // Root unit is not a dependency unless other units are dependant + // to it. + unit_is_root); let new_deps: Vec<_> = unit_graph[unit] .iter() .map(|dep| { @@ -645,8 +659,13 @@ fn traverse_and_share( new_graph, unit_graph, &dep.unit, + false, dep.unit_for.is_for_host(), to_host, + // If we should compile the current unit, we should also compile + // its dependencies. And if not, we should compile compile time + // dependencies only. + skip_non_compile_time_deps, ); new_dep_unit.hash(&mut dep_hash); UnitDep { @@ -712,6 +731,7 @@ fn traverse_and_share( unit.dep_hash, unit.artifact, unit.artifact_target_for_features, + unit.skip_non_compile_time_dep, ); // We can now turn the deferred value into its actual final value. @@ -742,8 +762,11 @@ fn traverse_and_share( // Since `dep_hash` is now filled in, there's no need to specify the artifact target // for target-dependent feature resolution None, + skip_non_compile_time_deps, ); - assert!(memo.insert(unit.clone(), new_unit.clone()).is_none()); + if !unit_is_root || !compile_time_deps_only { + assert!(memo.insert(unit.clone(), new_unit.clone()).is_none()); + } new_graph.entry(new_unit.clone()).or_insert(new_deps); new_unit } @@ -904,6 +927,7 @@ fn override_rustc_crate_types( unit.dep_hash, unit.artifact, unit.artifact_target_for_features, + unit.skip_non_compile_time_dep, ) }; units[0] = match unit.target.kind() { diff --git a/src/cargo/ops/cargo_compile/unit_generator.rs b/src/cargo/ops/cargo_compile/unit_generator.rs index 29762c8f8a4..2e239889718 100644 --- a/src/cargo/ops/cargo_compile/unit_generator.rs +++ b/src/cargo/ops/cargo_compile/unit_generator.rs @@ -167,6 +167,7 @@ impl<'a> UnitGenerator<'a, '_> { /*dep_hash*/ 0, IsArtifact::No, None, + false, ) }) .collect() diff --git a/src/cargo/util/command_prelude.rs b/src/cargo/util/command_prelude.rs index 11ca68a57f8..2946021df00 100644 --- a/src/cargo/util/command_prelude.rs +++ b/src/cargo/util/command_prelude.rs @@ -521,6 +521,10 @@ pub trait CommandExt: Sized { .hide(true), ) } + + fn arg_compile_time_deps(self) -> Self { + self._arg(flag("compile-time-deps", "").hide(true)) + } } impl CommandExt for Command { @@ -806,6 +810,7 @@ Run `{cmd}` to see possible targets." build_config.build_plan = self.flag("build-plan"); build_config.unit_graph = self.flag("unit-graph"); build_config.future_incompat_report = self.flag("future-incompat-report"); + build_config.compile_time_deps_only = self.flag("compile-time-deps"); if self._contains("timings") { for timing_output in self._values_of("timings") { @@ -840,6 +845,10 @@ Run `{cmd}` to see possible targets." gctx.cli_unstable() .fail_if_stable_opt("--unit-graph", 8002)?; } + if build_config.compile_time_deps_only { + gctx.cli_unstable() + .fail_if_stable_opt("--compile-time-deps", 14434)?; + } let opts = CompileOptions { build_config, diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index 76ecb860f49..e2b321665c8 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -2190,3 +2190,17 @@ More information can be found in the [config chapter](config.md#cache). ## doctest-xcompile Doctest cross-compiling is now unconditionally enabled starting in Rust 1.89. Running doctests with `cargo test` will now honor the `--target` flag. + +## compile-time-deps + +This permanently-unstable flag to only build proc-macros and build scripts (and their required dependencies), +as well as run the build scripts. + +It is intended for use by tools like rust-analyzer and will never be stabilized. + +Example: + +```console +cargo +nightly build --compile-time-deps -Z unstable-options +cargo +nightly check --compile-time-deps --all-targets -Z unstable-options +``` diff --git a/tests/testsuite/compile_time_deps.rs b/tests/testsuite/compile_time_deps.rs new file mode 100644 index 00000000000..9adc3067cea --- /dev/null +++ b/tests/testsuite/compile_time_deps.rs @@ -0,0 +1,399 @@ +use cargo_test_support::prelude::*; +use cargo_test_support::{project, str}; + +#[cargo_test] +fn gated_by_unstable_opts() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) + .build(); + + p.cargo("check --compile-time-deps") + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] the `--compile-time-deps` flag is unstable, and only available on the nightly channel of Cargo, but this is the `stable` channel +See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information about Rust release channels. +See https://github.com/rust-lang/cargo/issues/14434 for more information about the `--compile-time-deps` flag. + +"#]]) + .run(); +} + +#[cargo_test] +fn non_comp_time_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2021" + + [dependencies] + bar.path = "bar" + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + bar::bar(); + } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + edition = "2021" + "#, + ) + .file("bar/src/lib.rs", r#"pub fn bar() {}"#) + .build(); + + p.cargo("-Zunstable-options check --compile-time-deps") + .masquerade_as_nightly_cargo(&["compile-time-deps"]) + .with_stderr_data(str![[r#" +[LOCKING] 1 package to latest compatible version +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); +} + +#[cargo_test] +fn proc_macro_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + resolver = "2" + members = ["foo", "bar", "baz"] + + [workspace.dependencies] + bar.path = "bar" + baz.path = "baz" + "#, + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2021" + + [dependencies] + bar.workspace = true + "#, + ) + .file( + "foo/src/main.rs", + r#" + fn main() { + bar::bar!(); + } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + edition = "2021" + + [lib] + proc-macro = true + + [dependencies] + baz.workspace = true + "#, + ) + .file( + "bar/src/lib.rs", + r#" + extern crate proc_macro; + + use proc_macro::TokenStream; + + #[proc_macro] + pub fn bar(input: TokenStream) -> TokenStream { + baz::baz(); + input + } + "#, + ) + .file( + "bar/tests/simple.rs", + r#" + #[test] + fn test_bar() { + let _x: bool = bar::bar!(true); + } + "#, + ) + .file( + "baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.0.1" + edition = "2021" + "#, + ) + .file("baz/src/lib.rs", r#"pub fn baz() {}"#) + .build(); + + p.cargo("-Zunstable-options check --package foo --compile-time-deps") + .masquerade_as_nightly_cargo(&["compile-time-deps"]) + .with_stderr_data(str![[r#" +[COMPILING] baz v0.0.1 ([ROOT]/foo/baz) +[COMPILING] bar v0.0.1 ([ROOT]/foo/bar) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + p.cargo("clean").run(); + + p.cargo("-Zunstable-options check --package bar --compile-time-deps") + .masquerade_as_nightly_cargo(&["compile-time-deps"]) + .with_stderr_data(str![[r#" +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + p.cargo("clean").run(); + + p.cargo("-Zunstable-options check --package bar --all-targets --compile-time-deps") + .masquerade_as_nightly_cargo(&["compile-time-deps"]) + .with_stderr_data(str![[r#" +[COMPILING] baz v0.0.1 ([ROOT]/foo/baz) +[COMPILING] bar v0.0.1 ([ROOT]/foo/bar) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); +} + +#[cargo_test] +fn build_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2021" + + [build-dependencies] + bar.path = "bar" + "#, + ) + .file("src/main.rs", r#"fn main() {}"#) + .file( + "build.rs", + r#" + fn main() { + bar::bar(); + std::fs::write("check-script-output", "build script run").unwrap(); + } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + edition = "2021" + + [dependencies] + baz.path = "baz" + "#, + ) + .file( + "bar/src/lib.rs", + r#" + pub fn bar() { + baz::baz(); + } + "#, + ) + .file( + "bar/baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.0.1" + edition = "2021" + "#, + ) + .file("bar/baz/src/lib.rs", r#"pub fn baz() {}"#) + .build(); + + p.cargo("-Zunstable-options check --compile-time-deps") + .masquerade_as_nightly_cargo(&["compile-time-deps"]) + .with_stderr_data(str![[r#" +[LOCKING] 2 packages to latest compatible versions +[COMPILING] baz v0.0.1 ([ROOT]/foo/bar/baz) +[COMPILING] bar v0.0.1 ([ROOT]/foo/bar) +[COMPILING] foo v0.0.1 ([ROOT]/foo) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + assert_eq!(p.read_file("check-script-output"), "build script run"); +} + +#[cargo_test] +fn indirect_comp_time_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2021" + + [dependencies] + bar.path = "bar" + "#, + ) + .file("src/main.rs", r#"fn main() {}"#) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + edition = "2021" + + [build-dependencies] + baz.path = "baz" + "#, + ) + .file("bar/src/lib.rs", r#"pub fn bar() {}"#) + .file( + "bar/build.rs", + r#" + fn main() { + baz::baz(); + } + "#, + ) + .file( + "bar/baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.0.1" + edition = "2021" + "#, + ) + .file("bar/src/lib.rs", r#"pub fn baz() {}"#) + .file( + "bar/baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.0.1" + edition = "2021" + "#, + ) + .file("bar/baz/src/lib.rs", r#"pub fn baz() {}"#) + .build(); + + p.cargo("-Zunstable-options check --compile-time-deps") + .masquerade_as_nightly_cargo(&["compile-time-deps"]) + .with_stderr_data(str![[r#" +[LOCKING] 2 packages to latest compatible versions +[COMPILING] baz v0.0.1 ([ROOT]/foo/bar/baz) +[COMPILING] bar v0.0.1 ([ROOT]/foo/bar) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); +} + +#[cargo_test] +fn tests_target() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2021" + + [dev-dependencies] + bar.path = "bar" + "#, + ) + .file( + "src/main.rs", + r#" + fn main() {} + + #[test] + fn foo() { + bar::bar!(); + } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + edition = "2021" + + [lib] + proc-macro = true + "#, + ) + .file( + "bar/src/lib.rs", + r#" + extern crate proc_macro; + + use proc_macro::TokenStream; + + #[proc_macro] + pub fn bar(input: TokenStream) -> TokenStream { + input + } + "#, + ) + .build(); + + p.cargo("-Zunstable-options check --tests --compile-time-deps") + .with_stderr_data(str![[r#" +[LOCKING] 1 package to latest compatible version +[COMPILING] bar v0.0.1 ([ROOT]/foo/bar) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .masquerade_as_nightly_cargo(&["compile-time-deps"]) + .run(); + + p.cargo("clean").run(); + + p.cargo("-Zunstable-options check --compile-time-deps") + .masquerade_as_nightly_cargo(&["compile-time-deps"]) + .with_stderr_data(str![[r#" +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); +} diff --git a/tests/testsuite/main.rs b/tests/testsuite/main.rs index b264d5fa5fb..038953dc425 100644 --- a/tests/testsuite/main.rs +++ b/tests/testsuite/main.rs @@ -68,6 +68,7 @@ mod check; mod check_cfg; mod clean; mod collisions; +mod compile_time_deps; mod concurrent; mod config; mod config_cli;