Ability to specify the output name for a bin target different from the crate name

This commit is contained in:
Tejas Sanap 2021-06-26 17:51:55 +05:30 committed by Eric Huss
parent b51439fd8b
commit 9e2790f828
11 changed files with 376 additions and 9 deletions

View file

@ -103,11 +103,18 @@ impl FileType {
/// The filename for this FileType that Cargo should use when "uplifting"
/// it to the destination directory.
pub fn uplift_filename(&self, target: &Target) -> String {
let name = if self.should_replace_hyphens {
target.crate_name()
} else {
target.name().to_string()
let name = match target.binary_filename() {
Some(name) => name,
None => {
// For binary crate type, `should_replace_hyphens` will always be false.
if self.should_replace_hyphens {
target.crate_name()
} else {
target.name().to_string()
}
}
};
format!("{}{}{}", self.prefix, name, self.suffix)
}

View file

@ -365,7 +365,12 @@ impl<'cfg> Compilation<'cfg> {
/// that are only relevant in a context that has a unit
fn fill_rustc_tool_env(mut cmd: ProcessBuilder, unit: &Unit) -> ProcessBuilder {
if unit.target.is_bin() {
cmd.env("CARGO_BIN_NAME", unit.target.name());
let name = unit
.target
.binary_filename()
.unwrap_or(unit.target.name().to_string());
cmd.env("CARGO_BIN_NAME", name);
}
cmd.env("CARGO_CRATE_NAME", unit.target.crate_name());
cmd

View file

@ -467,6 +467,9 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> {
let meta = &self.metas[unit];
let meta_opt = meta.use_extra_filename.then(|| meta.meta_hash.to_string());
let path = out_dir.join(file_type.output_filename(&unit.target, meta_opt.as_deref()));
// If, the `different_binary_name` feature is enabled, the name of the hardlink will
// be the name of the binary provided by the user in `Cargo.toml`.
let hardlink = self.uplift_to(unit, &file_type, &path);
let export_path = if unit.target.is_custom_build() {
None

View file

@ -985,7 +985,10 @@ fn build_base_args(
let exe_path = cx
.files()
.bin_link_for_target(bin_target, unit.kind, cx.bcx)?;
let key = format!("CARGO_BIN_EXE_{}", bin_target.name());
let name = bin_target
.binary_filename()
.unwrap_or(bin_target.name().to_string());
let key = format!("CARGO_BIN_EXE_{}", name);
cmd.env(&key, exe_path);
}
}

View file

@ -405,6 +405,9 @@ features! {
// Allow to specify which codegen backend should be used.
(unstable, codegen_backend, "", "reference/unstable.html#codegen-backend"),
// Allow specifying different binary name apart from the crate name
(unstable, different_binary_name, "", "reference/unstable.html#different-binary-name"),
}
pub struct Feature {

View file

@ -194,6 +194,8 @@ pub struct Target {
struct TargetInner {
kind: TargetKind,
name: String,
// Note that `bin_name` is used for the cargo-feature `different_binary_name`
bin_name: Option<String>,
// Note that the `src_path` here is excluded from the `Hash` implementation
// as it's absolute currently and is otherwise a little too brittle for
// causing rebuilds. Instead the hash for the path that we send to the
@ -350,6 +352,7 @@ compact_debug! {
[debug_the_fields(
kind
name
bin_name
src_path
required_features
tested
@ -627,6 +630,7 @@ impl Target {
inner: Arc::new(TargetInner {
kind: TargetKind::Bin,
name: String::new(),
bin_name: None,
src_path,
required_features: None,
doc: false,
@ -662,6 +666,7 @@ impl Target {
pub fn bin_target(
name: &str,
bin_name: Option<String>,
src_path: PathBuf,
required_features: Option<Vec<String>>,
edition: Edition,
@ -670,6 +675,7 @@ impl Target {
target
.set_kind(TargetKind::Bin)
.set_name(name)
.set_binary_name(bin_name)
.set_required_features(required_features)
.set_doc(true);
target
@ -911,11 +917,17 @@ impl Target {
Arc::make_mut(&mut self.inner).name = name.to_string();
self
}
pub fn set_binary_name(&mut self, bin_name: Option<String>) -> &mut Target {
Arc::make_mut(&mut self.inner).bin_name = bin_name;
self
}
pub fn set_required_features(&mut self, required_features: Option<Vec<String>>) -> &mut Target {
Arc::make_mut(&mut self.inner).required_features = required_features;
self
}
pub fn binary_filename(&self) -> Option<String> {
self.inner.bin_name.clone()
}
pub fn description_named(&self) -> String {
match self.kind() {
TargetKind::Lib(..) => "lib".to_string(),

View file

@ -1968,6 +1968,8 @@ struct TomlTarget {
crate_type2: Option<Vec<String>>,
path: Option<PathValue>,
// Note that `filename` is used for the cargo-feature `different_binary_name`
filename: Option<String>,
test: Option<bool>,
doctest: Option<bool>,
bench: Option<bool>,

View file

@ -266,7 +266,14 @@ fn clean_bins(
"autobins",
);
// This loop performs basic checks on each of the TomlTarget in `bins`.
for bin in &bins {
// For each binary, check if the `filename` parameter is populated. If it is,
// check if the corresponding cargo feature has been activated.
if bin.filename.is_some() {
features.require(Feature::different_binary_name())?;
}
validate_target_name(bin, "binary", "bin", warnings)?;
let name = bin.name();
@ -321,8 +328,14 @@ fn clean_bins(
Err(e) => anyhow::bail!("{}", e),
};
let mut target =
Target::bin_target(&bin.name(), path, bin.required_features.clone(), edition);
let mut target = Target::bin_target(
&bin.name(),
bin.filename.clone(),
path,
bin.required_features.clone(),
edition,
);
configure(features, bin, &mut target)?;
result.push(target);
}

View file

@ -74,6 +74,7 @@ Each new feature described below should explain how to use it.
* Output behavior
* [out-dir](#out-dir) — Adds a directory where artifacts are copied to.
* [terminal-width](#terminal-width) — Tells rustc the width of the terminal so that long diagnostic messages can be truncated to be more readable.
* [Different binary name](#different-binary-name) — Assign a name to the built binary that is seperate from the crate name.
* Compile behavior
* [mtime-on-use](#mtime-on-use) — Updates the last-modified timestamp on every dependency every time it is used, to provide a mechanism to delete unused artifacts.
* [doctest-xcompile](#doctest-xcompile) — Supports running doctests with the `--target` flag.
@ -1288,6 +1289,32 @@ The primary use case is to run `cargo rustc --print=cfg` to get config values
for the appropriate target and influenced by any other RUSTFLAGS.
### Different binary name
* Tracking Issue: [#9778](https://github.com/rust-lang/cargo/issues/9778)
* PR: [#9627](https://github.com/rust-lang/cargo/pull/9627)
The `different-binary-name` feature allows setting the filename of the binary without having to obey the
restrictions placed on crate names. For example, the crate name must use only `alphanumeric` characters
or `-` or `_`, and cannot be empty.
The `filename` parameter should **not** include the binary extension, `cargo` will figure out the appropriate
extension and use that for the binary on its own.
The `filename` parameter is only available in the `[[bin]]` section of the manifest.
```toml
cargo-features = ["different-binary-name"]
[project]
name = "foo"
version = "0.0.1"
[[bin]]
name = "foo"
filename = "007bar"
path = "src/main.rs"
```
## Stabilized and removed features

View file

@ -0,0 +1,291 @@
use cargo_test_support::install::{
assert_has_installed_exe, assert_has_not_installed_exe, cargo_home,
};
use cargo_test_support::project;
#[cargo_test]
fn gated() {
let p = project()
.file(
"Cargo.toml",
r#"
[project]
name = "foo"
version = "0.0.1"
[[bin]]
name = "foo"
filename = "007bar"
path = "src/main.rs"
"#,
)
.file("src/main.rs", "fn main() { assert!(true) }")
.build();
// Run cargo build.
p.cargo("build")
.masquerade_as_nightly_cargo()
.with_status(101)
.with_stderr_contains("[..]feature `different-binary-name` is required")
.run();
}
#[cargo_test]
// This test checks if:
// 1. The correct binary is produced
// 2. The deps file has the correct content
// 3. Fingerprinting works
// 4. `cargo clean` command works
fn binary_name1() {
// Create the project.
let p = project()
.file(
"Cargo.toml",
r#"
cargo-features = ["different-binary-name"]
[project]
name = "foo"
version = "0.0.1"
[[bin]]
name = "foo"
filename = "007bar"
path = "src/main.rs"
"#,
)
.file("src/main.rs", "fn main() { assert!(true) }")
.build();
// Run cargo build.
p.cargo("build").masquerade_as_nightly_cargo().run();
// Check the name of the binary that cargo has generated.
// A binary with the name of the crate should NOT be created.
let foo_path = p.bin("foo");
assert!(!foo_path.is_file());
// A binary with the name provided in `filename` parameter should be created.
let bar_path = p.bin("007bar");
assert!(bar_path.is_file());
// Check if deps file exists.
let deps_path = p.bin("007bar").with_extension("d");
assert!(deps_path.is_file(), "{:?}", bar_path);
let depinfo = p.read_file(deps_path.to_str().unwrap());
// Prepare what content we expect to be present in deps file.
let deps_exp = format!(
"{}: {}",
p.bin("007bar").to_str().unwrap(),
p.root().join("src").join("main.rs").to_str().unwrap()
);
// Compare actual deps content with expected deps content.
assert!(
depinfo.lines().any(|line| line == deps_exp),
"Content of `{}` is incorrect",
deps_path.to_string_lossy()
);
// Run cargo second time, to verify fingerprint.
p.cargo("build -p foo -v")
.masquerade_as_nightly_cargo()
.with_stderr(
"\
[FRESH] foo [..]
[FINISHED] [..]
",
)
.run();
// Run cargo clean.
p.cargo("clean -p foo").masquerade_as_nightly_cargo().run();
// Check if the appropriate file was removed.
assert!(
!bar_path.is_file(),
"`cargo clean` did not remove the correct files"
);
}
#[cargo_test]
// This test checks if:
// 1. Check `cargo run`
// 2. Check `cargo test`
// 3. Check `cargo install/uninstall`
fn binary_name2() {
// Create the project.
let p = project()
.file(
"Cargo.toml",
r#"
cargo-features = ["different-binary-name"]
[project]
name = "foo"
version = "0.0.1"
[[bin]]
name = "foo"
filename = "007bar"
"#,
)
.file(
"src/main.rs",
r#"
fn hello(name: &str) -> String {
format!("Hello, {}!", name)
}
fn main() {
println!("{}", hello("crabs"));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_crabs() {
assert_eq!(hello("crabs"), "Hello, crabs!");
}
}
"#,
)
.build();
// Run cargo build.
p.cargo("build").masquerade_as_nightly_cargo().run();
// Check the name of the binary that cargo has generated.
// A binary with the name of the crate should NOT be created.
let foo_path = p.bin("foo");
assert!(!foo_path.is_file());
// A binary with the name provided in `filename` parameter should be created.
let bar_path = p.bin("007bar");
assert!(bar_path.is_file());
// Check if `cargo test` works
p.cargo("test")
.masquerade_as_nightly_cargo()
.with_stderr(
"\
[COMPILING] foo v0.0.1 ([CWD])
[FINISHED] test [unoptimized + debuginfo] target(s) in [..]
[RUNNING] [..] (target/debug/deps/foo-[..][EXE])",
)
.with_stdout_contains("test tests::check_crabs ... ok")
.run();
// Check if `cargo run` is able to execute the binary
p.cargo("run")
.masquerade_as_nightly_cargo()
.with_stdout("Hello, crabs!")
.run();
p.cargo("install").masquerade_as_nightly_cargo().run();
assert_has_installed_exe(cargo_home(), "007bar");
p.cargo("uninstall")
.with_stderr("[REMOVING] [ROOT]/home/.cargo/bin/007bar[EXE]")
.masquerade_as_nightly_cargo()
.run();
assert_has_not_installed_exe(cargo_home(), "007bar");
}
#[cargo_test]
fn check_env_vars() {
let p = project()
.file(
"Cargo.toml",
r#"
cargo-features = ["different-binary-name"]
[project]
name = "foo"
version = "0.0.1"
[[bin]]
name = "foo"
filename = "007bar"
"#,
)
.file(
"src/main.rs",
r#"
fn main() {
println!("{}", option_env!("CARGO_BIN_NAME").unwrap());
}
"#,
)
.file(
"tests/integration.rs",
r#"
#[test]
fn check_env_vars2() {
let value = option_env!("CARGO_BIN_EXE_007bar").expect("Could not find environment variable.");
assert!(value.contains("007bar"));
}
"#
)
.build();
// Run cargo build.
p.cargo("build").masquerade_as_nightly_cargo().run();
p.cargo("run")
.masquerade_as_nightly_cargo()
.with_stdout("007bar")
.run();
p.cargo("test")
.masquerade_as_nightly_cargo()
.with_status(0)
.run();
}
#[cargo_test]
fn check_msg_format_json() {
// Create the project.
let p = project()
.file(
"Cargo.toml",
r#"
cargo-features = ["different-binary-name"]
[project]
name = "foo"
version = "0.0.1"
[[bin]]
name = "foo"
filename = "007bar"
path = "src/main.rs"
"#,
)
.file("src/main.rs", "fn main() { assert!(true) }")
.build();
let output = r#"
{
"reason": "compiler-artifact",
"package_id": "foo 0.0.1 [..]",
"manifest_path": "[CWD]/Cargo.toml",
"target": "{...}",
"profile": "{...}",
"features": [],
"filenames": "{...}",
"executable": "[ROOT]/foo/target/debug/007bar[EXE]",
"fresh": false
}
{"reason":"build-finished", "success":true}
"#;
// Run cargo build.
p.cargo("build --message-format=json")
.masquerade_as_nightly_cargo()
.with_json(output)
.run();
}

View file

@ -13,6 +13,7 @@ mod alt_registry;
mod bad_config;
mod bad_manifest_path;
mod bench;
mod binary_name;
mod build;
mod build_plan;
mod build_script;