2025-06-25 22:37:24 +09:00

714 lines
15 KiB
Rust

//! Tests for cargo-sbom precursor files.
use std::path::PathBuf;
use crate::prelude::*;
use cargo_test_support::basic_bin_manifest;
use cargo_test_support::cargo_test;
use cargo_test_support::compare::assert_e2e;
use cargo_test_support::project;
use cargo_test_support::registry::Package;
use snapbox::IntoData;
const SBOM_FILE_EXTENSION: &str = ".cargo-sbom.json";
fn append_sbom_suffix(link: &PathBuf) -> PathBuf {
let mut link_buf = link.clone().into_os_string();
link_buf.push(SBOM_FILE_EXTENSION);
PathBuf::from(link_buf)
}
#[cargo_test]
fn warn_without_passing_unstable_flag() {
let p = project()
.file("Cargo.toml", &basic_bin_manifest("foo"))
.file("src/main.rs", r#"fn main() {}"#)
.build();
p.cargo("build")
.env("CARGO_BUILD_SBOM", "true")
.masquerade_as_nightly_cargo(&["sbom"])
.with_stderr_data(
"\
[WARNING] ignoring 'sbom' config, pass `-Zsbom` to enable it\n\
[COMPILING] foo v0.5.0 ([..])\n\
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [..]\n",
)
.run();
let file = append_sbom_suffix(&p.bin("foo"));
assert!(!file.exists());
}
#[cargo_test]
fn simple() {
let p = project()
.file("Cargo.toml", &basic_bin_manifest("foo"))
.file("src/main.rs", r#"fn main() {}"#)
.build();
p.cargo("build -Zsbom")
.env("CARGO_BUILD_SBOM", "true")
.masquerade_as_nightly_cargo(&["sbom"])
.run();
let file = append_sbom_suffix(&p.bin("foo"));
let output = std::fs::read_to_string(file).unwrap();
// The expected test does contain the "rustc" section
// but other tests omit them for brevity.
assert_e2e().eq(
output,
snapbox::str![[r#"
{
"crates": [
{
"dependencies": [],
"features": [],
"id": "path+[ROOTURL]/foo#0.5.0",
"kind": [
"bin"
]
}
],
"root": 0,
"rustc": {
"commit_hash": "{...}",
"host": "[HOST_TARGET]",
"verbose_version": "{...}",
"version": "{...}",
"workspace_wrapper": null,
"wrapper": null
},
"target": "[HOST_TARGET]",
"version": 1
}
"#]]
.is_json(),
);
}
#[cargo_test]
fn with_multiple_crate_types() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "1.2.3"
[lib]
crate-type = ["dylib", "rlib"]
"#,
)
.file("src/main.rs", r#"fn main() { let _i = foo::give_five(); }"#)
.file("src/lib.rs", r#"pub fn give_five() -> i32 { 5 }"#)
.build();
p.cargo("build -Zsbom")
.env("CARGO_BUILD_SBOM", "true")
.masquerade_as_nightly_cargo(&["sbom"])
.run();
assert_eq!(
3,
p.glob(p.target_debug_dir().join("*.cargo-sbom.json"))
.count()
);
let sbom_path = append_sbom_suffix(&p.dylib("foo"));
assert!(sbom_path.is_file());
let output = std::fs::read_to_string(sbom_path).unwrap();
assert_e2e().eq(
output,
snapbox::str![[r#"
{
"crates": [
{
"dependencies": [],
"features": [],
"id": "path+[ROOTURL]/foo#1.2.3",
"kind": [
"dylib",
"rlib"
]
}
],
"root": 0,
"rustc": "{...}",
"target": "[HOST_TARGET]",
"version": 1
}
"#]]
.is_json(),
);
}
#[cargo_test]
fn with_simple_build_script() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.1"
build = "build.rs"
"#,
)
.file("src/main.rs", "#[cfg(foo)] fn main() {}")
.file(
"build.rs",
r#"fn main() {
println!("cargo::rustc-check-cfg=cfg(foo)");
println!("cargo::rustc-cfg=foo");
}"#,
)
.build();
p.cargo("build -Zsbom")
.env("CARGO_BUILD_SBOM", "true")
.masquerade_as_nightly_cargo(&["sbom"])
.run();
let path = append_sbom_suffix(&p.bin("foo"));
assert!(path.is_file());
let output = std::fs::read_to_string(path).unwrap();
assert_e2e().eq(
output,
snapbox::str![[r#"
{
"crates": [
{
"dependencies": [
{
"index": 1,
"kind": "build"
}
],
"features": [],
"id": "path+[ROOTURL]/foo#0.0.1",
"kind": [
"bin"
]
},
{
"dependencies": [],
"features": [],
"id": "path+[ROOTURL]/foo#0.0.1",
"kind": [
"custom-build"
]
}
],
"root": 0,
"rustc": "{...}",
"target": "[HOST_TARGET]",
"version": 1
}
"#]]
.is_json(),
);
}
#[cargo_test]
fn with_build_dependencies() {
Package::new("baz", "0.1.0").publish();
Package::new("bar", "0.1.0")
.build_dep("baz", "0.1.0")
.file(
"Cargo.toml",
r#"
[package]
name = "bar"
version = "0.1.0"
build = "build.rs"
[build-dependencies]
baz = "0.1.0"
"#,
)
.file("src/lib.rs", "pub fn bar() -> i32 { 2 }")
.file(
"build.rs",
r#"fn main() {
println!("cargo::rustc-check-cfg=cfg(foo)");
println!("cargo::rustc-cfg=foo");
}"#,
)
.publish();
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.1"
[dependencies]
bar = "0.1.0"
"#,
)
.file("src/main.rs", "fn main() { let _i = bar::bar(); }")
.build();
p.cargo("build -Zsbom")
.env("CARGO_BUILD_SBOM", "true")
.masquerade_as_nightly_cargo(&["sbom"])
.run();
let path = append_sbom_suffix(&p.bin("foo"));
let output = std::fs::read_to_string(path).unwrap();
assert_e2e().eq(
output,
snapbox::str![[r#"
{
"crates": [
{
"dependencies": [
{
"index": 1,
"kind": "build"
}
],
"features": [],
"id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.1.0",
"kind": [
"lib"
]
},
{
"dependencies": [
{
"index": 2,
"kind": "normal"
}
],
"features": [],
"id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.1.0",
"kind": [
"custom-build"
]
},
{
"dependencies": [],
"features": [],
"id": "registry+https://github.com/rust-lang/crates.io-index#baz@0.1.0",
"kind": [
"lib"
]
},
{
"dependencies": [
{
"index": 0,
"kind": "normal"
}
],
"features": [],
"id": "path+[ROOTURL]/foo#0.0.1",
"kind": [
"bin"
]
}
],
"root": 3,
"rustc": "{...}",
"target": "[HOST_TARGET]",
"version": 1
}
"#]]
.is_json(),
);
}
#[cargo_test]
fn crate_uses_different_features_for_build_and_normal_dependencies() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "a"
version = "0.1.0"
edition = "2021"
[dependencies]
b = { path = "b/", features = ["f1"] }
[build-dependencies]
b = { path = "b/", features = ["f2"] }
"#,
)
.file(
"src/main.rs",
r#"
fn main() { b::f1(); }
"#,
)
.file(
"build.rs",
r#"
fn main() { b::f2(); }
"#,
)
.file(
"b/Cargo.toml",
r#"
[package]
name = "b"
version = "0.0.1"
edition = "2021"
[features]
f1 = []
f2 = []
"#,
)
.file(
"b/src/lib.rs",
r#"
#[cfg(feature = "f1")]
pub fn f1() {}
#[cfg(feature = "f2")]
pub fn f2() {}
"#,
)
.build();
p.cargo("build -Zsbom")
.env("CARGO_BUILD_SBOM", "true")
.masquerade_as_nightly_cargo(&["sbom"])
.run();
let path = append_sbom_suffix(&p.bin("a"));
assert!(path.is_file());
let output = std::fs::read_to_string(path).unwrap();
assert_e2e().eq(
output,
snapbox::str![[r#"
{
"crates": [
{
"dependencies": [
{
"index": 1,
"kind": "build"
},
{
"index": 3,
"kind": "normal"
}
],
"features": [],
"id": "path+[ROOTURL]/foo#a@0.1.0",
"kind": [
"bin"
]
},
{
"dependencies": [
{
"index": 2,
"kind": "normal"
}
],
"features": [],
"id": "path+[ROOTURL]/foo#a@0.1.0",
"kind": [
"custom-build"
]
},
{
"dependencies": [],
"features": [
"f2"
],
"id": "path+[ROOTURL]/foo/b#0.0.1",
"kind": [
"lib"
]
},
{
"dependencies": [],
"features": [
"f1"
],
"id": "path+[ROOTURL]/foo/b#0.0.1",
"kind": [
"lib"
]
}
],
"root": 0,
"rustc": "{...}",
"target": "[HOST_TARGET]",
"version": 1
}
"#]]
.is_json(),
);
}
#[cargo_test]
fn artifact_dep() {
Package::new("bar", "0.5.0")
.file("src/main.rs", "fn main() {}")
.file("Cargo.toml", &basic_bin_manifest("bar"))
.publish();
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.0"
edition = "2021"
[lib]
crate-type = ["dylib"]
[dependencies]
bar = { version = "0.5.0", artifact = "bin" }
[build-dependencies]
bar = { version = "0.5.0", artifact = "bin" }
"#,
)
.file("src/lib.rs", "")
.file("build.rs", r#"
fn main() {
let bar: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR").into();
assert!(&bar.is_file());
}"#)
.build();
p.cargo("build -Z bindeps -Z sbom")
.env("CARGO_BUILD_SBOM", "true")
.masquerade_as_nightly_cargo(&["bindeps", "sbom"])
.run();
let output = std::fs::read_to_string(append_sbom_suffix(&p.dylib("foo"))).unwrap();
assert_e2e().eq(
output,
snapbox::str![[r#"
{
"crates": [
{
"dependencies": [],
"features": [],
"id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.5.0",
"kind": [
"bin"
]
},
{
"dependencies": [
{
"index": 0,
"kind": "normal"
},
{
"index": 0,
"kind": "build"
},
{
"index": 2,
"kind": "build"
}
],
"features": [],
"id": "path+[ROOTURL]/foo#0.0.0",
"kind": [
"dylib"
]
},
{
"dependencies": [],
"features": [],
"id": "path+[ROOTURL]/foo#0.0.0",
"kind": [
"custom-build"
]
}
],
"root": 1,
"rustc": "{...}",
"target": "[HOST_TARGET]",
"version": 1
}
"#]]
.is_json(),
);
}
#[cargo_test]
fn proc_macro() {
Package::new("noop", "0.0.1")
.file(
"Cargo.toml",
r#"
[package]
name = "noop"
version = "0.0.1"
edition = "2021"
[lib]
proc-macro = true
"#,
)
.file(
"src/lib.rs",
r#"
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_derive(Noop)]
pub fn noop(_input: TokenStream) -> TokenStream {
"".parse().unwrap()
}
"#,
)
.publish();
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.1"
edition = "2021"
[dependencies]
noop = "0.0.1"
"#,
)
.file(
"src/main.rs",
r#"
#[macro_use]
extern crate noop;
#[derive(Noop)]
struct X;
fn main() {}
"#,
)
.build();
p.cargo("build -Z sbom")
.env("CARGO_BUILD_SBOM", "true")
.masquerade_as_nightly_cargo(&["sbom"])
.run();
let output = std::fs::read_to_string(append_sbom_suffix(&p.bin("foo"))).unwrap();
assert_e2e().eq(
output,
snapbox::str![[r#"
{
"crates": [
{
"dependencies": [
{
"index": 1,
"kind": "build"
}
],
"features": [],
"id": "path+[ROOTURL]/foo#0.0.1",
"kind": [
"bin"
]
},
{
"dependencies": [],
"features": [],
"id": "registry+https://github.com/rust-lang/crates.io-index#noop@0.0.1",
"kind": [
"proc-macro"
]
}
],
"root": 0,
"rustc": "{...}",
"target": "[HOST_TARGET]",
"version": 1
}
"#]]
.is_json(),
);
}
#[cargo_test]
fn workspace_wrapper() {
let wrapper = project()
.at("wrapper")
.file("Cargo.toml", &basic_bin_manifest("wrapper"))
.file(
"src/main.rs",
r#"
fn main() {
let mut args = std::env::args().skip(1);
if let Some(sbom) = std::env::var_os("CARGO_SBOM_PATH") {
for sbom in std::env::split_paths(&sbom) {
eprintln!("found sbom");
assert!(sbom.exists());
}
}
let status = std::process::Command::new(&args.next().unwrap())
.args(args).status().unwrap();
std::process::exit(status.code().unwrap_or(1));
}
"#,
)
.build();
wrapper.cargo("build").run();
let p = project()
.file("Cargo.toml", &basic_bin_manifest("foo"))
.file("src/main.rs", r#"fn main() {}"#)
.build();
p.cargo("build -Zsbom")
.env("CARGO_BUILD_SBOM", "true")
.env("RUSTC_WRAPPER", wrapper.bin("wrapper"))
.masquerade_as_nightly_cargo(&["sbom"])
.with_stderr_data(snapbox::str![[r#"
[COMPILING] foo v0.5.0 ([ROOT]/foo)
found sbom
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
"#]])
.run();
let file = append_sbom_suffix(&p.bin("foo"));
let output = std::fs::read_to_string(file).unwrap();
assert_e2e().eq(
output,
snapbox::str![[r#"
{
"crates": [
{
"dependencies": [],
"features": [],
"id": "path+[ROOTURL]/foo#0.5.0",
"kind": [
"bin"
]
}
],
"root": 0,
"rustc": "{...}",
"target": "[HOST_TARGET]",
"version": 1
}
"#]]
.is_json(),
);
}