Skip to content

Commit 4e59631

Browse files
committed
Auto merge of #13842 - skogseth:build-only-specified-artifact-library, r=weihanglo
Fix: Build only the specified artifact library when multiple types are available ### What does this PR try to resolve? Fixes #12109. #### TL;DR: A crate `bar` exposes it's library as both a `staticlib` and a `cdylib`. A second crate `foo` specifies an artifact dependency of type `staticlib` on `bar`, meaning cargo should build `bar` as a `staticlib` and expose certain environment variables (e.g. `CARGO_STATICLIB_FILE_BAR`). However, due to a bug, cargo ends up building (and exposing the associated environment variables) for both the `staticlib` and `cdylib`. The same happens if `foo` specifies an artifact dependency of type `cdylib`; both artifact types are built. ### How should we test and review this PR? The first commit introduces a test which reproduces the issue, the second commit introduces the fix. This setup was recommended by `@ehuss.` ### Additional information Artifact dependencies: https://rust-lang.github.io/rfcs/3028-cargo-binary-dependencies.html #### TL;DR If a crate `foo` requests an artifact dependency of kind <artifact_kind> from a crate `bar` then the following happens: - `bar` is built with crate-type <artifact_kind> and a directory is created at `target/<profile>/deps/artifact/bar-<build_hash_or_something>/<artifact_kind>`. The binary artifact is placed in this directory. - Cargo exposes certain environment variables, the most important for this PR is `CARGO_<artifact_kind>_FILE_BAR`, which points to the binary artifact that was specified. This environment variable is available at compile time for normal dependencies and at runtime for build-dependencies. If multiple artifact-kinds are requested cargo will create a unit for each, and so they will all be built separately.
2 parents 9b4a501 + 9a5cfbc commit 4e59631

File tree

2 files changed

+82
-5
lines changed

2 files changed

+82
-5
lines changed

src/cargo/core/compiler/unit_dependencies.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use crate::core::compiler::unit_graph::{UnitDep, UnitGraph};
2424
use crate::core::compiler::{
2525
CompileKind, CompileMode, CrateType, RustcTargetData, Unit, UnitInterner,
2626
};
27-
use crate::core::dependency::{Artifact, ArtifactTarget, DepKind};
27+
use crate::core::dependency::{Artifact, ArtifactKind, ArtifactTarget, DepKind};
2828
use crate::core::profiles::{Profile, Profiles, UnitFor};
2929
use crate::core::resolver::features::{FeaturesFor, ResolvedFeatures};
3030
use crate::core::resolver::Resolve;
@@ -555,17 +555,20 @@ fn artifact_targets_to_unit_deps(
555555
let ret =
556556
match_artifacts_kind_with_targets(dep, artifact_pkg.targets(), parent.pkg.name().as_str())?
557557
.into_iter()
558-
.map(|(_artifact_kind, target)| target)
559-
.flat_map(|target| {
558+
.flat_map(|(artifact_kind, target)| {
560559
// We split target libraries into individual units, even though rustc is able
561-
// to produce multiple kinds in an single invocation for the sole reason that
560+
// to produce multiple kinds in a single invocation for the sole reason that
562561
// each artifact kind has its own output directory, something we can't easily
563562
// teach rustc for now.
564563
match target.kind() {
565564
TargetKind::Lib(kinds) => Box::new(
566565
kinds
567566
.iter()
568-
.filter(|tk| matches!(tk, CrateType::Cdylib | CrateType::Staticlib))
567+
.filter(move |tk| match (tk, artifact_kind) {
568+
(CrateType::Cdylib, ArtifactKind::Cdylib) => true,
569+
(CrateType::Staticlib, ArtifactKind::Staticlib) => true,
570+
_ => false,
571+
})
569572
.map(|target_kind| {
570573
new_unit_dep(
571574
state,

tests/testsuite/artifact_dep.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3189,3 +3189,77 @@ fn check_transitive_artifact_dependency_with_different_target() {
31893189
.with_status(101)
31903190
.run();
31913191
}
3192+
3193+
#[cargo_test]
3194+
fn build_only_specified_artifact_library() {
3195+
// Create a project with:
3196+
// - A crate `bar` with both `staticlib` and `cdylib` as crate-types.
3197+
// - A crate `foo` which depends on either the `staticlib` or `cdylib` artifact of bar,
3198+
// whose build-script simply checks which library artifacts are present.
3199+
let create_project = |artifact_lib| {
3200+
project()
3201+
.file(
3202+
"bar/Cargo.toml",
3203+
r#"
3204+
[package]
3205+
name = "bar"
3206+
version = "1.0.0"
3207+
3208+
[lib]
3209+
crate-type = ["staticlib", "cdylib"]
3210+
"#,
3211+
)
3212+
.file("bar/src/lib.rs", "")
3213+
.file(
3214+
"Cargo.toml",
3215+
&format!(
3216+
r#"
3217+
[package]
3218+
name = "foo"
3219+
version = "1.0.0"
3220+
3221+
[build-dependencies]
3222+
bar = {{ path = "bar", artifact = "{artifact_lib}" }}
3223+
"#),
3224+
)
3225+
.file("src/lib.rs", "")
3226+
.file(
3227+
"build.rs",
3228+
r#"
3229+
fn main() {
3230+
println!("cdylib present: {}", std::env::var_os("CARGO_CDYLIB_FILE_BAR").is_some());
3231+
println!("staticlib present: {}", std::env::var_os("CARGO_STATICLIB_FILE_BAR").is_some());
3232+
}
3233+
"#,
3234+
)
3235+
.build()
3236+
};
3237+
3238+
let cdylib = create_project("cdylib");
3239+
cdylib
3240+
.cargo("build -Z bindeps")
3241+
.masquerade_as_nightly_cargo(&["bindeps"])
3242+
.run();
3243+
match_exact(
3244+
"cdylib present: true\nstaticlib present: false",
3245+
&build_script_output_string(&cdylib, "foo"),
3246+
"build script output",
3247+
"",
3248+
None,
3249+
)
3250+
.unwrap();
3251+
3252+
let staticlib = create_project("staticlib");
3253+
staticlib
3254+
.cargo("build -Z bindeps")
3255+
.masquerade_as_nightly_cargo(&["bindeps"])
3256+
.run();
3257+
match_exact(
3258+
"cdylib present: false\nstaticlib present: true",
3259+
&build_script_output_string(&staticlib, "foo"),
3260+
"build script output",
3261+
"",
3262+
None,
3263+
)
3264+
.unwrap();
3265+
}

0 commit comments

Comments
 (0)