From c9b060551b321ca481766f077320ce0f648d2e5e Mon Sep 17 00:00:00 2001 From: Manuel Ceron Date: Tue, 9 Jun 2026 19:16:26 +0200 Subject: [PATCH] feat: add `harness` field to cargo metadata output (#11846) Adds the `harness` field to each target in `cargo metadata` JSON output, exposing the Cargo.toml `[[test]]`/`[[bench]]` `harness` setting so that external tools can tell whether a target uses the libtest harness or provides its own `main()`. --- src/cargo/core/manifest.rs | 4 + src/doc/man/cargo-metadata.md | 8 +- src/doc/man/generated_txt/cargo-metadata.txt | 8 +- src/doc/src/commands/cargo-metadata.md | 8 +- src/doc/src/reference/external-tools.md | 10 +- src/doc/src/reference/unstable.md | 3 +- src/etc/man/cargo-metadata.1 | 8 +- tests/testsuite/metadata.rs | 123 +++++++++++++++++++ 8 files changed, 165 insertions(+), 7 deletions(-) diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index 81084bcc8ce..ea697e5eb11 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -396,6 +396,9 @@ struct SerializedTarget<'a> { doctest: bool, /// Whether tests should be run for the target (`test` field in `Cargo.toml`) test: bool, + /// Whether the target uses the libtest harness (`harness` field in `Cargo.toml`). + #[serde(skip_serializing_if = "bool::clone")] // skip if true + harness: bool, } impl ser::Serialize for Target { @@ -418,6 +421,7 @@ impl ser::Serialize for Target { doc: self.documented(), doctest: self.doctested() && self.doctestable(), test: self.tested(), + harness: self.harness(), } .serialize(s) } diff --git a/src/doc/man/cargo-metadata.md b/src/doc/man/cargo-metadata.md index 21bda47752c..4024e9eb26e 100644 --- a/src/doc/man/cargo-metadata.md +++ b/src/doc/man/cargo-metadata.md @@ -178,7 +178,13 @@ The JSON output has the following format: "doctest": false, /* Whether or not this target should be built and run with `--test` */ - "test": true + "test": true, + /* Whether or not the target uses the libtest harness. + Corresponds to the `harness` field in `Cargo.toml`. + This property is not included if `harness` is true + (the default). + */ + "harness": false } ], /* Set of features defined for the package. diff --git a/src/doc/man/generated_txt/cargo-metadata.txt b/src/doc/man/generated_txt/cargo-metadata.txt index 82eb6e2aaef..654b086ec54 100644 --- a/src/doc/man/generated_txt/cargo-metadata.txt +++ b/src/doc/man/generated_txt/cargo-metadata.txt @@ -174,7 +174,13 @@ OUTPUT FORMAT "doctest": false, /* Whether or not this target should be built and run with `--test` */ - "test": true + "test": true, + /* Whether or not the target uses the libtest harness. + Corresponds to the `harness` field in `Cargo.toml`. + This property is not included if `harness` is true + (the default). + */ + "harness": false } ], /* Set of features defined for the package. diff --git a/src/doc/src/commands/cargo-metadata.md b/src/doc/src/commands/cargo-metadata.md index bb1c4da0b4c..98af826d083 100644 --- a/src/doc/src/commands/cargo-metadata.md +++ b/src/doc/src/commands/cargo-metadata.md @@ -178,7 +178,13 @@ The JSON output has the following format: "doctest": false, /* Whether or not this target should be built and run with `--test` */ - "test": true + "test": true, + /* Whether or not the target uses the libtest harness. + Corresponds to the `harness` field in `Cargo.toml`. + This property is not included if `harness` is true + (the default). + */ + "harness": false } ], /* Set of features defined for the package. diff --git a/src/doc/src/reference/external-tools.md b/src/doc/src/reference/external-tools.md index 1b76876d4ef..859b93fb012 100644 --- a/src/doc/src/reference/external-tools.md +++ b/src/doc/src/reference/external-tools.md @@ -127,7 +127,12 @@ structure: "doctest": true /* Whether or not this target should be built and run with `--test` */ - "test": true + "test": true, + /* Whether or not the target uses the libtest harness. + Corresponds to the `harness` field in `Cargo.toml`. + This property is not included if `harness` is true (the default). + */ + "harness": false }, /* The message emitted by the compiler. @@ -167,7 +172,8 @@ following structure: "edition": "2018", "doc": true, "doctest": true, - "test": true + "test": true, + "harness": false }, /* The profile indicates which compiler settings were used. */ "profile": { diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index 139b7f7491e..495f3f0a8ca 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -779,7 +779,8 @@ The following is a description of the JSON structure: "src_path": "/path/to/my-package/src/lib.rs", "edition": "2018", "test": true, - "doctest": true + "doctest": true, + "harness": false }, /* The profile settings for this unit. These values may not match the profile defined in the manifest. diff --git a/src/etc/man/cargo-metadata.1 b/src/etc/man/cargo-metadata.1 index ad6dee4ebb5..39f4aa970fa 100644 --- a/src/etc/man/cargo-metadata.1 +++ b/src/etc/man/cargo-metadata.1 @@ -180,7 +180,13 @@ The JSON output has the following format: "doctest": false, /* Whether or not this target should be built and run with `\-\-test` */ - "test": true + "test": true, + /* Whether or not the target uses the libtest harness. + Corresponds to the `harness` field in `Cargo.toml`. + This property is not included if `harness` is true + (the default). + */ + "harness": false } ], /* Set of features defined for the package. diff --git a/tests/testsuite/metadata.rs b/tests/testsuite/metadata.rs index d42455ff388..3e987751e55 100644 --- a/tests/testsuite/metadata.rs +++ b/tests/testsuite/metadata.rs @@ -112,6 +112,129 @@ fn cargo_metadata_warns_on_implicit_version() { .run(); } +#[cargo_test] +fn cargo_metadata_no_harness() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2015" + + [[test]] + name = "no_harness_test" + path = "tests/no_harness_test.rs" + harness = false + + [[bench]] + name = "no_harness_bench" + path = "benches/no_harness_bench.rs" + harness = false + "#, + ) + .file("src/lib.rs", "") + .file("tests/no_harness_test.rs", "fn main() {}") + .file("benches/no_harness_bench.rs", "fn main() {}") + .build(); + + p.cargo("metadata --no-deps --format-version 1") + .with_stdout_data( + str![[r#" +{ + "metadata": null, + "packages": [ + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "path+[ROOTURL]/foo#0.1.0", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[ROOT]/foo/Cargo.toml", + "metadata": null, + "name": "foo", + "publish": null, + "readme": null, + "repository": null, + "rust_version": null, + "source": null, + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "foo", + "src_path": "[ROOT]/foo/src/lib.rs", + "test": true + }, + { + "crate_types": [ + "bin" + ], + "doc": false, + "doctest": false, + "edition": "2015", + "harness": false, + "kind": [ + "test" + ], + "name": "no_harness_test", + "src_path": "[ROOT]/foo/tests/no_harness_test.rs", + "test": true + }, + { + "crate_types": [ + "bin" + ], + "doc": false, + "doctest": false, + "edition": "2015", + "harness": false, + "kind": [ + "bench" + ], + "name": "no_harness_bench", + "src_path": "[ROOT]/foo/benches/no_harness_bench.rs", + "test": false + } + ], + "version": "0.1.0" + } + ], + "resolve": null, + "target_directory": "[ROOT]/foo/target", + "build_directory": "[ROOT]/foo/target", + "version": 1, + "workspace_default_members": [ + "path+[ROOTURL]/foo#0.1.0" + ], + "workspace_members": [ + "path+[ROOTURL]/foo#0.1.0" + ], + "workspace_root": "[ROOT]/foo" +} +"#]] + .is_json(), + ) + .run(); +} + #[cargo_test] fn library_with_several_crate_types() { let p = project()