I have a binary that depends on a library. In the library I specified these features:
[features]
default = []
fet = []
I would expect feature fet would ONLY be activated when it is compiled with the feature flag. In my binary the Cargo.toml looks like this:
[dependencies]
a = { path = "./a" }
[features]
default = []
I haven't specified the fet feature in the Cargo.toml, however this gives me a compilation error in my binary:
fn main() {
if cfg!(feature = "fet") {
compile_error!("not expected");
}
}
Why is feature fet still activated in my binary? I executed these commands and get the same error:
cargo run
cargo run --features=default
Ideally, I want my binary to activate certain features from dependencies if they are explicitly mentioned in the cargo run command (in the --features flag). Is this possible? I was hoping this would work in my binary Cargo.toml:
[features]
default = []
fet = ["a/fet"]
and feature fet would be activated if I ran this command:
cargo run --features=fet
The cfg! macro will be expanded to true or false depending on if the condition is true or false, so if the feature flag is not set, it will still expand to this:
fn main() {
if false {
compile_error!("not expected");
}
}
The compile_error! macro will still be invoked, so you'll get the compiler error either way.
The cfg! macro is mostly useful when you can rely on the compiler optimizing away branches that will never be taken, such as an if false branch. The code still needs to compile before optimization, though, so it's less useful when the code will only compile if the condition is true or false.
What you'd want to use instead is either a #[cfg] attribute or the cfg_if::cfg_if! macro:
// with #[cfg] attribute
fn main() {
#[cfg(feature = "fet")]
compile_error!("not expected");
}
// with cfg_if!
use cfg_if::cfg_if;
fn main() {
cfg_if!{
if #[cfg(feature = "fet")] {
compile_error!("not expected");
}
}
}
Related
I would like to specify a separate [profile.release] for my library when cfg(target_os = "ios") is active. I tried this:
[profile.'cfg(not(target_os = "ios"))'.release]
lto = true # Use link-time optimization.
[profile.'cfg(target_os = "ios")'.release]
lto = true # Use link-time optimization.
opt-level = "z" # Mobile devices are fast enough. Optimize for size instead.
strip = "symbols" # Not relevant for windows.
However when I now try to build my project / workspace I get the following error:
error: failed to parse manifest at `/path/to/Cargo.toml`
Caused by:
invalid character `(` in profile name `cfg(not(target_os = "ios"))`
Allowed characters are letters, numbers, underscore, and hyphen.
which is to be expected, because according to the documentation only [profile.<name>] is allowed.
Is there any method to achieve the desired behaviour?
P.S. The full target name would be aarch64-apple-ios in case this is needed.
This was requested in issue #4897, Per-target profiles?, but not yet implemented.
In the meantime, you can use a script that checks the target and set environment variables to override config (for example, set CARGO_PROFILE_RELEASE_LTO or CARGO_PROFILE_RELEASE_OPT_LEVEL) then invokes Cargo with them.
Here is an example, from users.rust-lang.org - How to modify profile.release only for a target?
Create a binary in the workspace named e.g. custom_build. In custom_build/src/main.rs put:
use std::env;
use std::process::Command;
fn main() {
let mut cargo = Command::new(env::var_os("CARGO").unwrap());
cargo.env("CARGO_CUSTOM_BUILD", "1"); // So we can disallow regular builds, to prevent mistakes
cargo.args(env::args_os().skip(1));
// You can determine the target OS by various way, but providing it manually to the build script is the simplest.
let for_ios = env::var("build_ios").is_ok();
if for_ios {
cargo.env("CARGO_PROFILE_RELEASE_LTO", "true");
cargo.env("CARGO_PROFILE_RELEASE_OPT_LEVEL", "z");
cargo.env("CARGO_PROFILE_RELEASE_STRIP", "symbols");
} else {
cargo.env("CARGO_PROFILE_RELEASE_LTO", "true");
}
let cargo_succeeded = cargo.status().ok().map_or(false, |status| status.success());
if !cargo_succeeded {
std::process::exit(1);
}
}
Then you can create a build.rs file to prevent manually running cargo:
use std::env;
fn main() {
if !env::var("CARGO_CUSTOM_BUILD").ok().map_or(false, |s| s == "1") {
panic!("Do not run `cargo ...`, run `cargo custom_build ...` instead")
}
}
If you look at the Tokio docs on docs.rs there's a blue tag indicating that a feature must be activated in order to access this API:
I would like to enable this for my crate as well, how can this be done?
The bad news is: It's a nightly-only feature for now.
The good news is: docs.rs uses nightly by default.
To get this to work all you need is to enable the doc_cfg feature and apply #doc(cfg) to the item being documented
#![feature(doc_cfg)]
#[doc(cfg(feature = "macros"))]
pub fn test() {}
Because this is a nightly-only feature, you probably don't want to enable it all the time. tokio defines the following in its Cargo.toml to only enable this feature on docs.rs:
# docs.rs-specific configuration
[package.metadata.docs.rs]
# document all features
all-features = true
# defines the configuration attribute `docsrs`
rustdoc-args = ["--cfg", "docsrs"]
and then they use
// only enables the `doc_cfg` feature when
// the `docsrs` configuration attribute is defined
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
pub fn test() {}
In the latest nightly (since v1.57 maybe), you can use feature doc_auto_cfg (merged in PR#90502) and you no longer need to manually mark features for doc, just write cfg as before:
#![feature(doc_auto_cfg)]
#[cfg(feature = "macros")]
pub fn test() {}
To check it locally, run cargo +nightly doc --all-features.
If you want to continue using stable for commands other than cargo doc, you can:
#![cfg_attr(doc, feature(doc_auto_cfg))]
#[cfg(feature = "macros")]
pub fn test() {}
UPDATE: The above method still requires doc-tests to run nightly, and it also requires dependents' doc command to run nightly. A workaround is that we can enable the doc_auto_cfg feature only under nightly.
In Cargo.toml, adding:
[build-dependencies]
rustc_version = "0.4.0"
Create a build.rs file with the following contents:
use rustc_version::{version_meta, Channel};
fn main() {
// Set cfg flags depending on release channel
let channel = match version_meta().unwrap().channel {
Channel::Stable => "CHANNEL_STABLE",
Channel::Beta => "CHANNEL_BETA",
Channel::Nightly => "CHANNEL_NIGHTLY",
Channel::Dev => "CHANNEL_DEV",
};
println!("cargo:rustc-cfg={}", channel)
}
Then we can enable the feature doc_auto_cfg this way:
#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
Since docs.rs uses nightly by default, the documentation on there will be shown as you expected.
If you look at the Tokio docs on docs.rs there's a blue tag indicating that a feature must be activated in order to access this API:
I would like to enable this for my crate as well, how can this be done?
The bad news is: It's a nightly-only feature for now.
The good news is: docs.rs uses nightly by default.
To get this to work all you need is to enable the doc_cfg feature and apply #doc(cfg) to the item being documented
#![feature(doc_cfg)]
#[doc(cfg(feature = "macros"))]
pub fn test() {}
Because this is a nightly-only feature, you probably don't want to enable it all the time. tokio defines the following in its Cargo.toml to only enable this feature on docs.rs:
# docs.rs-specific configuration
[package.metadata.docs.rs]
# document all features
all-features = true
# defines the configuration attribute `docsrs`
rustdoc-args = ["--cfg", "docsrs"]
and then they use
// only enables the `doc_cfg` feature when
// the `docsrs` configuration attribute is defined
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
pub fn test() {}
In the latest nightly (since v1.57 maybe), you can use feature doc_auto_cfg (merged in PR#90502) and you no longer need to manually mark features for doc, just write cfg as before:
#![feature(doc_auto_cfg)]
#[cfg(feature = "macros")]
pub fn test() {}
To check it locally, run cargo +nightly doc --all-features.
If you want to continue using stable for commands other than cargo doc, you can:
#![cfg_attr(doc, feature(doc_auto_cfg))]
#[cfg(feature = "macros")]
pub fn test() {}
UPDATE: The above method still requires doc-tests to run nightly, and it also requires dependents' doc command to run nightly. A workaround is that we can enable the doc_auto_cfg feature only under nightly.
In Cargo.toml, adding:
[build-dependencies]
rustc_version = "0.4.0"
Create a build.rs file with the following contents:
use rustc_version::{version_meta, Channel};
fn main() {
// Set cfg flags depending on release channel
let channel = match version_meta().unwrap().channel {
Channel::Stable => "CHANNEL_STABLE",
Channel::Beta => "CHANNEL_BETA",
Channel::Nightly => "CHANNEL_NIGHTLY",
Channel::Dev => "CHANNEL_DEV",
};
println!("cargo:rustc-cfg={}", channel)
}
Then we can enable the feature doc_auto_cfg this way:
#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
Since docs.rs uses nightly by default, the documentation on there will be shown as you expected.
If you look at the Tokio docs on docs.rs there's a blue tag indicating that a feature must be activated in order to access this API:
I would like to enable this for my crate as well, how can this be done?
The bad news is: It's a nightly-only feature for now.
The good news is: docs.rs uses nightly by default.
To get this to work all you need is to enable the doc_cfg feature and apply #doc(cfg) to the item being documented
#![feature(doc_cfg)]
#[doc(cfg(feature = "macros"))]
pub fn test() {}
Because this is a nightly-only feature, you probably don't want to enable it all the time. tokio defines the following in its Cargo.toml to only enable this feature on docs.rs:
# docs.rs-specific configuration
[package.metadata.docs.rs]
# document all features
all-features = true
# defines the configuration attribute `docsrs`
rustdoc-args = ["--cfg", "docsrs"]
and then they use
// only enables the `doc_cfg` feature when
// the `docsrs` configuration attribute is defined
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
pub fn test() {}
In the latest nightly (since v1.57 maybe), you can use feature doc_auto_cfg (merged in PR#90502) and you no longer need to manually mark features for doc, just write cfg as before:
#![feature(doc_auto_cfg)]
#[cfg(feature = "macros")]
pub fn test() {}
To check it locally, run cargo +nightly doc --all-features.
If you want to continue using stable for commands other than cargo doc, you can:
#![cfg_attr(doc, feature(doc_auto_cfg))]
#[cfg(feature = "macros")]
pub fn test() {}
UPDATE: The above method still requires doc-tests to run nightly, and it also requires dependents' doc command to run nightly. A workaround is that we can enable the doc_auto_cfg feature only under nightly.
In Cargo.toml, adding:
[build-dependencies]
rustc_version = "0.4.0"
Create a build.rs file with the following contents:
use rustc_version::{version_meta, Channel};
fn main() {
// Set cfg flags depending on release channel
let channel = match version_meta().unwrap().channel {
Channel::Stable => "CHANNEL_STABLE",
Channel::Beta => "CHANNEL_BETA",
Channel::Nightly => "CHANNEL_NIGHTLY",
Channel::Dev => "CHANNEL_DEV",
};
println!("cargo:rustc-cfg={}", channel)
}
Then we can enable the feature doc_auto_cfg this way:
#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
Since docs.rs uses nightly by default, the documentation on there will be shown as you expected.
More specifically, I would like to have different constants for normal builds, test builds and benchmark builds:
#[cfg(not(test))]
const X: usize = 16;
#[cfg(test)]
const X: usize = 10;
#[cfg(benchmarking)] // <-- this doesn't work, and would conflict with not(test)
const X: usize = 100;
You need to add a feature(benchmarking) section to your Cargo.toml file. The specifics can be found in the Cargo documentation.
[features]
benchmarking = []
After you add your feature, you need to write the specific benchmarking code for that feature in the code
.
.
#[cfg(feature = "benchmarking")]
{
// count iterations check memory etc.
}
.
.
Then if you want to get these codes are added into compilation you need to set this feature while building like following:
cargo build --features "benchmarking"
For the detailed info you can check the reference