mirror of
https://github.com/rust-lang/cargo
synced 2024-08-25 10:35:53 +00:00
Ability to specify the output name for a bin target different from the crate name
This commit is contained in:
parent
b51439fd8b
commit
9e2790f828
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
291
tests/testsuite/binary_name.rs
Normal file
291
tests/testsuite/binary_name.rs
Normal 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();
|
||||
}
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue