| //! 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() { |
| eprintln!("{:?}", option_env!("CARGO_SBOM_PATH")); |
| }"#, |
| ) |
| .build(); |
| |
| p.cargo("run") |
| .env("CARGO_BUILD_SBOM", "true") |
| .masquerade_as_nightly_cargo(&["sbom"]) |
| .with_stderr_data(snapbox::str![[r#" |
| [WARNING] ignoring 'sbom' config, pass `-Zsbom` to enable it |
| [COMPILING] foo v0.5.0 ([ROOT]/foo) |
| [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s |
| [RUNNING] `target/debug/foo[EXE]` |
| None |
| |
| "#]]) |
| .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(), |
| ); |
| } |