如何使用功能构建两个版本的应用程序,使用单个 `cargo build` 命令

huangapple go评论67阅读模式
英文:

How to build two versions of an application, using features, with a single `cargo build` command

问题

I need to build a CLI application that comes in two flavours. Most of the code is common to both, excepting some low-level layer.

So far, I've been able to specify the creation of multiple binaries using the [[bin]] notation, and using ad-hoc features. However, that requires to invoke cargo build twice, once with --features="flavour1" and once with --features="flavour2".

  • is there any way to have Cargo to build these two flavours with a single command?
  • is this an anti-pattern? I appreciate features are additive, and it is recommended to avoid mutually exclusive features, but I couldn't find another way to do this.

To illustrate the question, here follows a sample Cargo.toml and the corresponding src/main.rs file:

Cargo.toml

[package]
name = "sample-flavours"
version = "0.1.0"
edition = "2021"


[features]
flavour1=[]
flavour2=[]

[[bin]]
name="bin-flavour1"
path="src/main.rs"
required-features=["flavour1"]

[[bin]]
name="bin-flavour2"
path="src/main.rs"
required-features=["flavour2"]

src/main.rs

#[cfg(feature="flavour1")]
fn sublayer() -> &'static str {
    "flavour 1"
}

#[cfg(feature="flavour2")]
fn sublayer() -> &'static str {
    "flavour 2"
} 

#[cfg(not(any(feature="flavour1",feature="flavour2"))]
compile_error!("Need a flavour");


fn main() {
    println!("{}", sublayer());
}
英文:

I need to build a CLI application that comes in two flavours. Most of the code is common to both, excepting some low-level layer.

So far, I've been able to specify the creation of multiple binaries using the [[bin]] notation, and using ad-hoc features. However, that requires to invoke cargo build twice, once with --features="flavour1" and once with --features="flavour2".

  • is there any way to have Cargo to build these two flavours with a single command?
  • is this an anti-pattern? I appreciate features are additive, and it is recommended to avoid mutually exclusive features, but I couldn't find another way to do this.

To illustrate the question, here follows a sample Cargo.toml and the corresponding src/main.rs file:

Cargo.toml

[package]
name = "sample-flavours"
version = "0.1.0"
edition = "2021"


[features]
flavour1=[]
flavour2=[]

[[bin]]
name="bin-flavour1"
path="src/main.rs"
required-features=["flavour1"]

[[bin]]
name="bin-flavour2"
path="src/main.rs"
required-features=["flavour2"]

src/main.rs

#[cfg(feature="flavour1")]
fn sublayer() -> &'static str {
    "flavour 1"
}

#[cfg(feature="flavour2")]
fn sublayer() -> &'static str {
    "flavour 2"
} 

#[cfg(not(any(feature="flavour1",feature="flavour2")))]
compile_error!("Need a flavour");


fn main() {
    println!("{}", sublayer());
}

答案1

得分: 1

No. Cargo can build multiple binaries in one command, but only with a common feature set.

As you yourself noted, features should be additive. You haven't told us any detail about the code differences between flavour1 and flavour2, but one possibility is that you can replace them with generics — perhaps struct SomeImportantStruct<F> is instantiated as SomeImportantStruct<Flavour1> and SomeImportantStruct<Flavour2> — or simply have two different structs/modules/functions/whatever.

If you can achieve that, then it will be possible to compile both of your binaries with a single command. Now, this does not necessarily solve all build problems; for example, it might be desirable to disable a feature for smaller code size or performance, or a feature might be actually impossible to compile on a given target. Or, unrelated to features, you might want to use a different profile for the two builds. In any of those cases, you'll want to use a separate build command anyway.

英文:

> Is there any way to have Cargo to build these two flavours with a single command?

No. Cargo can build multiple binaries in one command, but only with a common feature set.

If you find you need to run multiple build commands for your project, and want to automate this, you will need something external to invoke Cargo. I like the xtask pattern, which lets you write your commands in Rust without any dependencies on additional command-line tools, but it may be overkill for your use case; a shell script might do.

> Is this an anti-pattern?

As you yourself noted, features should be additive. You haven't told us any detail about the code differences between flavour1 and flavour2, but one possibility is that you can replace them with generics — perhaps struct SomeImportantStruct&lt;F&gt; is instantiated as SomeImportantStruct&lt;Flavour1&gt; and SomeImportantStruct&lt;Flavour2&gt; — or simply have two different structs/modules/functions/whatever.

If you can achieve that, then it will be possible to compile both of your binaries with a single command. Now, this does not necessarily solve all build problems; for example, it might be desirable to disable a feature for smaller code size or performance, or a feature might be actually impossible to compile on a given target. Or, unrelated to features, you might want to use a different profile for the two builds. In any of those cases, you'll want to use a separate build command anyway.

huangapple
  • 本文由 发表于 2023年3月9日 17:19:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/75682581.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定