How to modify profile.release only for a target?

Hi,

I'd like to deactivate LTO for profile.release only on windows.

I tried in Cargo.toml:

[target.'cfg(windows)'.profile.release]
lto = false

but on cargo build --release:

warning: unused manifest key: target.cfg(windows).profile

Is it possible or am I writing it wrong?

EDIT: I said lto but it's opt-level that I want to change
and same question for profile.release.package.<name>

1 Like

Found the anwser, not possible yet: https://github.com/rust-lang/cargo/issues/4897

Indeed, there is no clean / direct way to achieve this. There is a hacky-ish way though: do not directly call cargo build, but call something that will perform some platform detection setting to only then tweak profile.release.

Let's say cargo smart.

cargo smart

First, make sure to add the following files:

.
├── .cargo  // <--
│   └── config  // <--
├── Cargo.toml
├── build.rs  // <-- (Optional)
├── smart  // <--
│   ├── Cargo.toml  // <--
│   └── main.rs  // <--
└── src
    └── lib.rs
    ...
  • .cargo/config

    [alias]
    smart = ["r", "-q", "--manifest-path", "smart/Cargo.toml", "--"]
    
  • smart/Cargo.toml

    [package]
    name = "smart"
    version = "0.0.0"
    edition = "2018"
    
    [[bin]]
    name = "smart"
    path = "main.rs"
    
    [workspace]
    
  • smart/main.rs

    use ::std::{env, ops::Not, process};
    
    fn main ()
    {
        let mut cmd = process::Command::new(::std::env::var("CARGO").unwrap());
        cmd.env("CARGO_SMART", "1");  // Optional, if used with the `build.rs`
        let ref args = env::args().skip(1).collect::<Vec<_>>();
        // dbg!(args); cmd.arg("-vv"); /* Uncomment when debugging this hack */
        cmd.args(args);
        // if args...contains... "--target" "...windows..." ...
        if cfg!(windows) {
            // See https://doc.rust-lang.org/cargo/reference/environment-variables.html#configuration-environment-variables
            cmd.env("CARGO_PROFILE_RELEASE_OPT_LEVEL", "0");
        }
        if cmd.status().ok().map_or(false, |it| it.success()).not() {
            process::exit(-1);
        }
    }
    
  • build.rs (Optional, to make sure we don't forget to call cargo using smart)

    use ::std::{env, ops::Not};
    
    fn main ()
    {
        if env::var("CARGO_SMART").ok().map_or(false, |s| s == "1").not() {
            panic!("Do not run `cargo ...`, run `cargo smart ...` instead")
        }
    }
    

With these changes, cargo smart <usual command> ought to work, such as cargo smart build --release

Feel free to rename smart to something else, and of course to tweak smart/main.rs to correctly handle things like the windows case if cross-compiling (since cfg!(windows) on a build script will only be enabled when running cargo from a Windows environment, not when compiling to one).

2 Likes

Thanks for the trick!

Do you know a similar trick to modify only profile.release.package.<name> on Windows?

So Windows build can still have most of the optim but for the problematic crate?

Sadly such config vars do not seem to have an associated env var, so in order for it to work, a an ephemeral .cargo/config override would need to be required, which surpasses my personal threshold for hacky things :sweat_smile:

1 Like