frankenapps
frankenapps

Reputation: 8241

How to create a target specific profile in Cargo.toml?

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.

Upvotes: 3

Views: 2900

Answers (1)

Chayim Friedman
Chayim Friedman

Reputation: 70900

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")
    }
}

Upvotes: 5

Related Questions