Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Target configuration for binaries #9208

Open
Tracked by #84
MichaelMcDonnell opened this issue Feb 25, 2021 · 5 comments
Open
Tracked by #84

Target configuration for binaries #9208

MichaelMcDonnell opened this issue Feb 25, 2021 · 5 comments
Labels
A-cargo-targets Area: selection and definition of targets (lib, bins, examples, tests, benches) A-manifest Area: Cargo.toml issues C-feature-request Category: proposal for a feature. Before PR, ping rust-lang/cargo if this is not `Feature accepted` S-needs-design Status: Needs someone to work further on the design for the feature or fix. NOT YET accepted.

Comments

@MichaelMcDonnell
Copy link

MichaelMcDonnell commented Feb 25, 2021

I would like to be able to specify different main files depending on the operating system. I ran into this problem in Servo where there are two main files. The main.rs file is for Android and includes the main2.rs file for the desktop using the include macro:

#[cfg(not(target_os = "android"))]
include!("main2.rs");

#[cfg(target_os = "android")]
pub fn main() {
    println!(
        "Cannot start /ports/servo/ on Android. \
         Use /support/android/apk/ + /ports/libsimpleservo/ instead"
    );
}

Using the include macro is not great because it breaks my IDE. I am not able to easily jump to the code. I also suspect using macros adds a small compile time overhead.

It could be solved using cfg_if crate (see my Servo cfg_if-main branch), and the code is not that nested, but it could be simpler with cargo support.

Another use case could be something like BusyBox but where certain system binaries are only needed on specific platforms. For example, something that queries Linux settings would only be compiled for Linux.

The Cargo.toml file shows how I would like the feature to work:

[package]
name = "target-cfg-bin-example"
version = "0.2.0"
authors = ["Michael Mc Donnell <michael@mcdonnell.dk>"]
edition = "2018"
autobins = false

# Use the standard `main.rs` for all operating systems except Android.
[[bin]]
name = "target-cfg-bin-example"
path = "src/main.rs"
target = "cfg(not(android))"

# Use the `main_android.rs` main file on Android
[[bin]]
name = "target-cfg-bin-example"
path = "src/main_android.rs"
target = "cfg(android)"

[dependencies]

It currently results in the following error message:

error: failed to parse manifest at `/home/michael/src/target-cfg-bin-example/Cargo.toml`

Caused by:
  found duplicate binary name target-cfg-bin-example, but all binary targets must have a unique name

You can clone the example from https://github.com/MichaelMcDonnell/target-cfg-bin-example

UPDATE 1: I changed the Cargo.toml proposal to add a target key/value pair on the binary definition instead of using the target array. Thanks to @ehuss for the suggestions. It became apparent to me too when I tried to implement it. For dependencies the target has to be applied to each dependency key/value pair so it make sense it is defined for the table/array that has them. For binaries it needs to be applied to the table but not to each key/value pair, so having it as a key/value pair makes sense.

UPDATE 2: I tried getting rid of the include macro using the cfg_if crate (see my Servo cfg_if-main branch), and the code is not that nested, but it could be simpler with cargo support. I've updated issue description with this information.

See also

@MichaelMcDonnell MichaelMcDonnell added the C-feature-request Category: proposal for a feature. Before PR, ping rust-lang/cargo if this is not `Feature accepted` label Feb 25, 2021
@ehuss
Copy link
Contributor

ehuss commented Mar 10, 2021

I think the usual solution here is to place the cfg expressions in main.rs. For example:

cfg_if! {
    if #[cfg(target_os = "android")] {
        mod impl_android;
        use impl_android::*;
    } else {
        mod impl_default;
        use impl_default::*;
    }
}

Or some variant of that. I'm not sure I follow the comment about using include_bytes, maybe you could explain that more or show an example?

I would maybe lean more towards having some kind of requirements specification for the binary definition instead of using something like the [[target]] array. Cargo targets already have required-features, so maybe something more extensible could help. I thought there was already an issue or RFC about this, but I can't find it.

Also, unfortunately the word target is overloaded and means two completely different things in this context.

@ehuss ehuss added the A-cargo-targets Area: selection and definition of targets (lib, bins, examples, tests, benches) label Mar 10, 2021
@MichaelMcDonnell
Copy link
Author

Thanks for the help @ehuss! I've updated the issue description. I will try to create an example to show what using the cfg_if macro in Servo would look like. I'm also working on the implementation itself.

I also tried searching for an existing issue but didn't find any. Do you know of anybody who might know how to find the issue or RFC?

@ithinuel
Copy link

Hi, I was looking for a solution to that with a slightly different use case.

Building a library targetting embedded devices. An example demonstrates the features using linux-embedded-hal. This example cannot build for windows or macos so cfg_if the whole example to have an empty main as an alternative doesn't seem great.

I was hoping that I could "disable" the example using something along:

[[example]]
name = "demo"
required-features = ["use_std"]
target = 'cfg(target_os = "linux")'

Although If I ever come to add support for windows or mac, I don't necessarily need the name to be the same.

@weihanglo weihanglo added the A-manifest Area: Cargo.toml issues label Oct 7, 2022
@epage epage added the S-needs-design Status: Needs someone to work further on the design for the feature or fix. NOT YET accepted. label Oct 19, 2023
@epage
Copy link
Contributor

epage commented Oct 19, 2023

btw there is #6179 for doing this on the package level

Like my proposal there, I'd suggest we call the field required-target to mirror required-features to suggest that it is required and this build-target should be skipped if it isn't present

@mqudsi
Copy link

mqudsi commented Apr 29, 2024

I think this should be possible without anything in Cargo.toml (so no package-level management) in order to handle auto-discovered code like examples without introducing an undue burden.

A lot of no_std-compatible packages without default = [ "std" ] or crates with various optional features and samples under examples/ that demonstrate their use if compiled with --features foo will fail a bare cargo test when it autodiscovers and tries to compile all the code under examples/.

The naïve approach I think most developers would reach for in this case is to just put #![cfg(feature = "foo")] at the top of examples/foo.rs, but then cargo test fails with

consider adding a main function to examples/foo.rs

There isn't a simple solution for this error. If you try to include #[cfg(not(feature = "foo"))] fn main() { panic!("Requisite feature not enabled!") } after, it won't be seen because of the preceding #![cfg(...)]. If you try placing it before, the #![cfg(...)] will break because that needs to come before anything else (other than comments and whitespace). You have to annotate everything in the file (functions, each line of import, etc) separately with #[cfg(feature = "foo")] and provide the fallback #[cfg(not(feature = "foo"))] fn main() { ... } to work around this issue.

If there were some way for a bare cargo test (not specifying a particular target) to detect this specific error and swallow it, that would be the most "natural" solution. But I know that this would represent somewhat of an inversion of control since cargo should not (generally) be in the business of doing anything with rustc errors..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-cargo-targets Area: selection and definition of targets (lib, bins, examples, tests, benches) A-manifest Area: Cargo.toml issues C-feature-request Category: proposal for a feature. Before PR, ping rust-lang/cargo if this is not `Feature accepted` S-needs-design Status: Needs someone to work further on the design for the feature or fix. NOT YET accepted.
Projects
None yet
Development

No branches or pull requests

6 participants