From 8d5fa4dc0fe4640c71d8ec6199c535092a1338c4 Mon Sep 17 00:00:00 2001 From: Sebastian Ziebell Date: Mon, 13 May 2024 14:06:19 +0200 Subject: [PATCH] Integrate review feedback * use `PackageIdSpec` instead of only `PackageId` in SBOM output * change `version` of a dependency to `Option` * output `Vec` instead of only the first found crate type * output rustc workspace wrapper * update 'warning' string in test using `[WARNING]` * use `serde_json::to_writer` to serialize SBOM * set sbom suffix in tests explicitely, instead of using `with_extension` --- src/cargo/core/compiler/output_sbom.rs | 32 ++++++------ src/doc/src/reference/unstable.md | 3 ++ tests/testsuite/sbom.rs | 69 +++++++++++++++++--------- 3 files changed, 66 insertions(+), 38 deletions(-) diff --git a/src/cargo/core/compiler/output_sbom.rs b/src/cargo/core/compiler/output_sbom.rs index 8d6b2c4b279b..1c12821e6bc9 100644 --- a/src/cargo/core/compiler/output_sbom.rs +++ b/src/cargo/core/compiler/output_sbom.rs @@ -2,16 +2,17 @@ //! See [`output_sbom`] for more. use std::collections::BTreeSet; -use std::io::{BufWriter, Write}; +use std::io::BufWriter; use std::path::PathBuf; use cargo_util::paths::{self}; use cargo_util_schemas::core::PackageIdSpec; use itertools::Itertools; +use semver::Version; use serde::Serialize; use crate::{ - core::{profiles::Profile, PackageId, Target, TargetKind}, + core::{profiles::Profile, Target, TargetKind}, util::Rustc, CargoResult, }; @@ -43,16 +44,16 @@ impl Serialize for SbomFormatVersion { #[derive(Serialize, Clone, Debug)] struct SbomDependency { name: String, - package_id: PackageId, - version: String, + package_id: PackageIdSpec, + version: Option, features: Vec, } impl From<&UnitDep> for SbomDependency { fn from(dep: &UnitDep) -> Self { - let package_id = dep.unit.pkg.package_id(); + let package_id = dep.unit.pkg.package_id().to_spec(); let name = package_id.name().to_string(); - let version = package_id.version().to_string(); + let version = package_id.version(); let features = dep .unit .features @@ -71,9 +72,9 @@ impl From<&UnitDep> for SbomDependency { #[derive(Serialize, Clone, Debug)] struct SbomPackage { - package_id: PackageId, + package_id: PackageIdSpec, package: String, - version: String, + version: Option, features: Vec, build_type: SbomBuildType, extern_crate_name: String, @@ -86,9 +87,9 @@ impl SbomPackage { dependencies: Vec, build_type: SbomBuildType, ) -> Self { - let package_id = dep.unit.pkg.package_id(); + let package_id = dep.unit.pkg.package_id().to_spec(); let package = package_id.name().to_string(); - let version = package_id.version().to_string(); + let version = package_id.version(); let features = dep .unit .features @@ -111,7 +112,7 @@ impl SbomPackage { #[derive(Serialize)] struct SbomTarget { kind: TargetKind, - crate_type: Option, + crate_types: Vec, name: String, edition: String, } @@ -120,7 +121,7 @@ impl From<&Target> for SbomTarget { fn from(target: &Target) -> Self { SbomTarget { kind: target.kind().clone(), - crate_type: target.kind().rustc_crate_types().first().cloned(), + crate_types: target.kind().rustc_crate_types().clone(), name: target.name().to_string(), edition: target.edition().to_string(), } @@ -131,6 +132,7 @@ impl From<&Target> for SbomTarget { struct SbomRustc { version: String, wrapper: Option, + workspace_wrapper: Option, commit_hash: Option, host: String, } @@ -140,6 +142,7 @@ impl From<&Rustc> for SbomRustc { Self { version: rustc.version.to_string(), wrapper: rustc.wrapper.clone(), + workspace_wrapper: rustc.workspace_wrapper.clone(), commit_hash: rustc.commit_hash.clone(), host: rustc.host.to_string(), } @@ -196,9 +199,8 @@ pub fn output_sbom(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> Cargo for sbom_output_file in build_runner.sbom_output_files(unit)? { let sbom = Sbom::new(unit, packages.clone(), rustc.clone()); - let mut outfile = BufWriter::new(paths::create(sbom_output_file)?); - let output = serde_json::to_string(&sbom)?; - write!(outfile, "{}", output)?; + let outfile = BufWriter::new(paths::create(sbom_output_file)?); + serde_json::to_writer(outfile, &sbom)?; } Ok(()) diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index d2ee09d7165b..3781655a406b 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -390,6 +390,9 @@ build process that are difficult or impossible to obtain in another way. To enable this feature either set the `sbom` field in the `.cargo/config.toml` ```toml +[unstable] +sbom = true + [build] sbom = true ``` diff --git a/tests/testsuite/sbom.rs b/tests/testsuite/sbom.rs index afe170ab8d9c..ee1219027261 100644 --- a/tests/testsuite/sbom.rs +++ b/tests/testsuite/sbom.rs @@ -18,6 +18,14 @@ fn assert_json_output(actual_json_file: PathBuf, expected_json: &str) { } } +const SBOM_FILE_EXTENSION: &str = ".cargo-sbom.json"; + +fn with_sbom_suffix(link: &PathBuf) -> PathBuf { + let mut link_buf = link.clone().into_os_string(); + link_buf.push(SBOM_FILE_EXTENSION); + PathBuf::from(link_buf) +} + fn configured_project() -> ProjectBuilder { project().file( ".cargo/config.toml", @@ -39,13 +47,13 @@ fn build_sbom_without_passing_unstable_flag() { .masquerade_as_nightly_cargo(&["sbom"]) .with_stderr( "\ - warning: ignoring 'sbom' config, pass `-Zsbom` to enable it\n\ + [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 = p.bin("foo").with_extension("cargo-sbom.json"); + let file = with_sbom_suffix(&p.bin("foo")); assert!(!file.exists()); } @@ -60,7 +68,7 @@ fn build_sbom_using_cargo_config() { .masquerade_as_nightly_cargo(&["sbom"]) .run(); - let file = p.bin("foo").with_extension("cargo-sbom.json"); + let file = with_sbom_suffix(&p.bin("foo")); assert_json_output( file, r#" @@ -74,7 +82,9 @@ fn build_sbom_using_cargo_config() { "kind": [ "bin" ], - "crate_type": "bin", + "crate_types": [ + "bin" + ], "name": "foo", "edition": "2015" }, @@ -100,6 +110,7 @@ fn build_sbom_using_cargo_config() { "rustc": { "version": "[..]", "wrapper": null, + "workspace_wrapper": null, "commit_hash": "[..]", "host": "[..]" } @@ -120,7 +131,7 @@ fn build_sbom_using_env_var() { .masquerade_as_nightly_cargo(&["sbom"]) .run(); - let file = p.bin("foo").with_extension("cargo-sbom.json"); + let file = with_sbom_suffix(&p.bin("foo")); assert!(file.is_file()); } @@ -147,7 +158,7 @@ fn build_sbom_project_bin_and_lib() { .masquerade_as_nightly_cargo(&["sbom"]) .run(); - assert!(p.bin("foo").with_extension("cargo-sbom.json").is_file()); + assert!(with_sbom_suffix(&p.bin("foo")).is_file()); assert_eq!( 2, p.glob(p.target_debug_dir().join("*.cargo-sbom.json")) @@ -171,7 +182,10 @@ fn build_sbom_with_simple_build_script() { .file("src/main.rs", "#[cfg(foo)] fn main() {}") .file( "build.rs", - r#"fn main() { println!("cargo::rustc-cfg=foo"); }"#, + r#"fn main() { + println!("cargo::rustc-check-cfg=cfg(foo)"); + println!("cargo::rustc-cfg=foo"); + }"#, ) .build(); @@ -179,7 +193,7 @@ fn build_sbom_with_simple_build_script() { .masquerade_as_nightly_cargo(&["sbom"]) .run(); - let path = p.bin("foo").with_extension("cargo-sbom.json"); + let path = with_sbom_suffix(&p.bin("foo")); assert!(path.is_file()); assert_json_output( @@ -187,7 +201,7 @@ fn build_sbom_with_simple_build_script() { r#" { "format_version": 1, - "package_id": "path+file:///[..]/foo#0.0.1", + "package_id": "path+file://[ROOT]/foo#0.0.1", "name": "foo", "version": "0.0.1", "source": "[ROOT]/foo", @@ -195,7 +209,9 @@ fn build_sbom_with_simple_build_script() { "kind": [ "bin" ], - "crate_type": "bin", + "crate_types": [ + "bin" + ], "name": "foo", "edition": "2015" }, @@ -223,18 +239,18 @@ fn build_sbom_with_simple_build_script() { { "features": [], "name": "foo", - "package_id": "foo 0.0.1 (path+file:///[..]/foo)", + "package_id": "path+file://[ROOT]/foo#0.0.1", "version": "0.0.1" } ], "extern_crate_name": "build_script_build", "features": [], "package": "foo", - "package_id": "foo 0.0.1 (path+file:///[..]/foo)", + "package_id": "path+file://[ROOT]/foo#0.0.1", "version": "0.0.1" }, { - "package_id": "foo 0.0.1 (path+file:///[..]/foo)", + "package_id": "path+file://[ROOT]/foo#0.0.1", "package": "foo", "version": "0.0.1", "features": [], @@ -247,6 +263,7 @@ fn build_sbom_with_simple_build_script() { "rustc": { "version": "[..]", "wrapper": null, + "workspace_wrapper": null, "commit_hash": "[..]", "host": "[..]" } @@ -274,7 +291,10 @@ fn build_sbom_with_build_dependencies() { .file("src/lib.rs", "pub fn bar() -> i32 { 2 }") .file( "build.rs", - r#"fn main() { println!("cargo::rustc-cfg=foo"); }"#, + r#"fn main() { + println!("cargo::rustc-check-cfg=cfg(foo)"); + println!("cargo::rustc-cfg=foo"); + }"#, ) .publish(); @@ -298,7 +318,7 @@ fn build_sbom_with_build_dependencies() { .masquerade_as_nightly_cargo(&["sbom"]) .run(); - let path = p.bin("foo").with_extension("cargo-sbom.json"); + let path = with_sbom_suffix(&p.bin("foo")); assert_json_output( path, r#" @@ -312,7 +332,9 @@ fn build_sbom_with_build_dependencies() { "kind": [ "bin" ], - "crate_type": "bin", + "crate_types": [ + "bin" + ], "name": "foo", "edition": "2015" }, @@ -335,7 +357,7 @@ fn build_sbom_with_build_dependencies() { }, "packages": [ { - "package_id": "bar 0.1.0 (registry+[..])", + "package_id": "registry+[..]#bar@0.1.0", "package": "bar", "version": "0.1.0", "features": [], @@ -344,14 +366,14 @@ fn build_sbom_with_build_dependencies() { "dependencies": [ { "name": "bar", - "package_id": "bar 0.1.0 (registry+[..])", + "package_id": "registry+[..]#bar@0.1.0", "version": "0.1.0", "features": [] } ] }, { - "package_id": "bar 0.1.0 (registry+[..])", + "package_id": "registry+[..]#bar@0.1.0", "package": "bar", "version": "0.1.0", "features": [], @@ -360,14 +382,14 @@ fn build_sbom_with_build_dependencies() { "dependencies": [ { "name": "bar", - "package_id": "bar 0.1.0 (registry+[..])", + "package_id": "registry+[..]#bar@0.1.0", "version": "0.1.0", "features": [] } ] }, { - "package_id": "bar 0.1.0 (registry+[..])", + "package_id": "registry+[..]#bar@0.1.0", "package": "bar", "version": "0.1.0", "features": [], @@ -376,14 +398,14 @@ fn build_sbom_with_build_dependencies() { "dependencies": [ { "name": "baz", - "package_id": "baz 0.1.0 (registry+[..])", + "package_id": "registry+[..]#baz@0.1.0", "version": "0.1.0", "features": [] } ] }, { - "package_id": "baz 0.1.0 (registry+[..])", + "package_id": "registry+[..]#baz@0.1.0", "package": "baz", "version": "0.1.0", "features": [], @@ -396,6 +418,7 @@ fn build_sbom_with_build_dependencies() { "rustc": { "version": "[..]", "wrapper": null, + "workspace_wrapper": null, "commit_hash": "[..]", "host": "[..]" }