Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion auditable-cyclonedx/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ fn purl(pkg: &auditable_serde::Package) -> String {
Source::Local => "&download_url=redacted",
Source::Registry => "&repository_url=redacted",
Source::Other(_) => "&download_url=redacted",
unknown => panic!("Unknown source: {:?}", unknown),
unknown => panic!("Unknown source: {unknown:?}"),
});
purl
}
38 changes: 35 additions & 3 deletions cargo-auditable/src/auditable_from_metadata.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
//! Converts from `cargo_metadata` crate structs to `auditable-serde` structs,
//! which map to our own serialialized representation.

use std::{cmp::min, cmp::Ordering::*, collections::HashMap, error::Error, fmt::Display};
use std::{
cmp::{min, Ordering::*},
collections::{HashMap, HashSet},
error::Error,
fmt::Display,
};

use auditable_serde::{DependencyKind, Package, Source, VersionInfo};

Expand Down Expand Up @@ -84,6 +89,8 @@ pub fn encode_audit_data(
.repr
.as_str();

let proc_macros = proc_macro_packages(metadata);

// Walk the dependency tree and resolve dependency kinds for each package.
// We need this because there may be several different paths to the same package
// and we need to aggregate dependency types across all of them.
Expand All @@ -103,8 +110,14 @@ pub fn encode_audit_data(
let parent_dep_kind = id_to_dep_kind[parent.id.repr.as_str()];
for child in &parent.deps {
let child_id = child.pkg.repr.as_str();
let dep_kind = strongest_dep_kind(child.dep_kinds.as_slice());
let dep_kind = min(dep_kind, parent_dep_kind);
let mut dep_kind = strongest_dep_kind(child.dep_kinds.as_slice());
// If the parent is a build dependency that has a runtime dependency, overall dependency should be 'build'.
// This propagates the dependency kinds that way from parent to child.
dep_kind = min(dep_kind, parent_dep_kind);
// proc macros require special handling since cargo_metadata reports them as normal deps
if proc_macros.contains(child_id) {
dep_kind = min(dep_kind, PrivateDepKind::Build);
}
let dep_kind_on_previous_visit = id_to_dep_kind.get(child_id);
if dep_kind_on_previous_visit.is_none()
|| &dep_kind > dep_kind_on_previous_visit.unwrap()
Expand Down Expand Up @@ -215,6 +228,25 @@ fn strongest_dep_kind(deps: &[cargo_metadata::DepKindInfo]) -> PrivateDepKind {
.unwrap_or(PrivateDepKind::Runtime) // for compatibility with Rust earlier than 1.41
}

fn proc_macro_packages(metadata: &cargo_metadata::Metadata) -> HashSet<&str> {
metadata
.packages
.iter()
.filter_map(|pkg| {
// As of Rust 1.88 a single crate cannot be both a proc macro and something else.
// Checking that length is 1 is purely to hedge against support for it being added in the future.
if pkg.targets.len() == 1
&& pkg.targets[0].kind.len() == 1
&& pkg.targets[0].kind[0] == "proc-macro"
{
Some(pkg.id.repr.as_str())
} else {
None
}
})
.collect()
}

#[cfg(test)]
mod tests {
#![allow(unused_imports)] // otherwise conditional compilation emits warnings
Expand Down
5 changes: 2 additions & 3 deletions cargo-auditable/src/rustc_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,8 @@ pub fn main(rustc_path: &OsStr) {
command_with_args.extend(command.get_args());
eprintln!(
"Failed to invoke rustc! Make sure it's in your $PATH\n\
The error was: {}\n\
The attempted call was: {:?}",
err, command_with_args,
The error was: {err}\n\
The attempted call was: {command_with_args:?}",
);
std::process::exit(1);
});
Expand Down
65 changes: 65 additions & 0 deletions cargo-auditable/tests/fixtures/proc-macro-dependency/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "proc-macro-dependency"
version = "0.1.0"
edition = "2024"

[dependencies]
serde = { version = "1.0.219", features = ["derive"] }

[workspace]
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use serde::{Deserialize, Serialize};

fn main() {
println!("{:?}", Hello("Hello, world!"));
}

#[derive(Serialize, Deserialize, Debug)]
struct Hello (&'static str);
33 changes: 33 additions & 0 deletions cargo-auditable/tests/it.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,3 +552,36 @@ fn test_path_not_equal_name_inner(sbom: bool) {
.iter()
.any(|p| p.name == "baz" && p.kind == DependencyKind::Runtime));
}

#[test]
fn test_proc_macro() {
test_proc_macro_inner(false);
test_proc_macro_inner(true);
}
fn test_proc_macro_inner(sbom: bool) {
// Path to workspace fixture Cargo.toml. See that file for overview of workspace members and their dependencies.
let workspace_cargo_toml = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures/proc-macro-dependency/Cargo.toml");
// Run in workspace root with default features
let bins = run_cargo_auditable(workspace_cargo_toml, &[], &[], sbom);
eprintln!("Proc macro binary map: {bins:?}");

// proc-macro-dependency should depend on
let binary = &bins.get("proc-macro-dependency").unwrap()[0];
let dep_info = get_dependency_info(binary);
eprintln!("{binary} dependency info: {dep_info:?}");
// locate the serde_derive proc macro package
let serde_derive_info = dep_info
.packages
.iter()
.find(|p| p.name == "serde_derive")
.expect("Could not find 'serde_derive' in the embedded dependency list!");
assert_eq!(serde_derive_info.kind, DependencyKind::Build);
// locate the syn package which is norm a dependency of serde-derive
let syn_info = dep_info
.packages
.iter()
.find(|p| p.name == "syn")
.expect("Could not find 'syn' in the embedded dependency list!");
assert_eq!(syn_info.kind, DependencyKind::Build);
}
8 changes: 4 additions & 4 deletions resolverver/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ impl std::fmt::Display for UnrecognizedValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
UnrecognizedValue::UnknownResolver(resolver) => {
write!(f, "Unrecognized resolver version: {}", resolver)
write!(f, "Unrecognized resolver version: {resolver}")
}
UnrecognizedValue::UnknownEdition(edition) => {
write!(f, "Unrecognized Rust edition: {}", edition)
write!(f, "Unrecognized Rust edition: {edition}")
}
}
}
Expand All @@ -30,8 +30,8 @@ pub enum Error {
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::UnrecognizedValue(msg) => write!(f, "{}", msg),
Error::TomlParseError(err) => write!(f, "Failed to parse Cargo.toml: {}", err),
Error::UnrecognizedValue(msg) => write!(f, "{msg}"),
Error::TomlParseError(err) => write!(f, "Failed to parse Cargo.toml: {err}"),
}
}
}
Expand Down
10 changes: 5 additions & 5 deletions resolverver/src/raw_fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ package.edition = \"2021\"
}),
};

let parsed: RawTomlFields = toml::from_str(&toml).unwrap();
let parsed: RawTomlFields = toml::from_str(toml).unwrap();
assert_eq!(parsed, expected);

let resolved_expected = TomlFields {
Expand Down Expand Up @@ -132,7 +132,7 @@ resolver = \"2\"
}),
};

let parsed: RawTomlFields = toml::from_str(&toml).unwrap();
let parsed: RawTomlFields = toml::from_str(toml).unwrap();
assert_eq!(parsed, expected);

let resolved_expected = TomlFields {
Expand Down Expand Up @@ -160,7 +160,7 @@ edition = \"2021\"
workspace: None,
};

let parsed: RawTomlFields = toml::from_str(&toml).unwrap();
let parsed: RawTomlFields = toml::from_str(toml).unwrap();
assert_eq!(parsed, expected);
}

Expand All @@ -180,7 +180,7 @@ version = \"0.1.0\"
workspace: None,
};

let parsed: RawTomlFields = toml::from_str(&toml).unwrap();
let parsed: RawTomlFields = toml::from_str(toml).unwrap();
assert_eq!(parsed, expected);

let resolved_expected = TomlFields {
Expand All @@ -206,7 +206,7 @@ members = [\"some-package\"]
}),
};

let parsed: RawTomlFields = toml::from_str(&toml).unwrap();
let parsed: RawTomlFields = toml::from_str(toml).unwrap();
assert_eq!(parsed, expected);

let resolved_expected = TomlFields {
Expand Down
Loading