Struggling with [target.'cfg(blah)'.dependencies] in Cargo.toml

I'm getting some strange behaviour trying to create setup different dependencies depending on the cargo build --target while cross compiling.

I'm on Linux and trying to both build for Linux which works fine if I omit --target and cross-compile for Windows by including --target=x86_64-pc-windows-gnu.

To some up I've been trying to set up a pair of either or dependencies along the lines of (as described in this post):

[dependencies]
# snip
termion = { version = "1.5", optional = true }
crossterm = { version = "0.17", optional = true }

[target.'cfg(not(windows))'.dependencies]
tui = { version = "0.10.0", features = ["termion", "crossterm"], default-features = false }

[target.'cfg(windows)'.dependencies]
tui = { version = "0.10.0", features = ["crossterm"], default-features = false }

Having failed to get that and several variations on that working I'm obviously not understanding how this works because using the following

[target.'cfg(target_os = "blimey")'.dependencies]
termion = { version = "1.5", optional = true }
tui = { version = "0.10.0", features = ["termion", "crossterm"], default-features = false }

[target.'cfg(target_os = "linux")'.dependencies]
tui = { version = "0.10.0", features = ["crossterm"], default-features = false }

The first set of dependencies for my new OS 'blimey' is used regardless of whether I provide --target=x86_64-pc-windows-gnu or not.

But if I remove the first target dependencies and just have this:

[target.'cfg(target_os = "linux")'.dependencies]
tui = { version = "0.10.0", features = ["crossterm"], default-features = false }

tui is only available if I omit --target=x86_64-pc-windows-gnu.

And if change to just this rule:

[target.'cfg(target_os = "windows")'.dependencies]
tui = { version = "0.10.0", features = ["crossterm"], default-features = false }

tui is only available when I use --target=x86_64-pc-windows-gnu.

And if I only have the 'blimey' dependencies tui is never available and I can't build with or without --target.

So if I have just one target dependency, I can select it or not with --target, but if I have two, even if the first one is for an OS called 'blimey', then that set of dependencies are active. But if I only have one set, for an invalid OS 'blimey' then that can't be selected (as expected).

Any ideas what's going on here?! I can't figure out the logic or what I might be doing wrong.

So, recap of the setup you tried, and the error you got:

I can't really tell you how to solve your issue, it may be that Cargo is not that smart when dealing with target.'cfg(not(...))'.dependencies or combinations of those, I don't know.


Anyways, in practice a setup that always works is to kind of sacrifice the automatic feature choice, and rely exclusively on explicitly queried features:

[dependencies]
# snip
crossterm = "0.17"
termion = { version = "1.5", optional = true }
tui = { version = "0.10.0", default-features = false, features = ["crossterm"] }

[features]
default = []

windows = []
not-windows = ["termion", "tui/termion"]

and then use:

# For Linux, from Linux
cargo build --bin logtail \
    --features not-windows

# For Windows, from Linux
cargo build --bin logtail-crossterm \
    --features windows --target x86_64-pc-windows-gnu

This is a known problem with the current way that Cargo resolves features. There is a fix available in the nightly toolchain if you pass Cargo the experimental -Zfeatures=itarget flag. This fix should eventually become the default behavior of the stable toolchain.

See the documentation and the tracking issue for more details.

3 Likes

Thanks @mbrubeck, I think that will indeed explain the behaviour I'm seeing. I'm not used to the toolchains and trying to cross compile with +nightly I'm getting another error and not sure how to solve it.

$ cargo build +nightly -Z features=itarget --bin logtail-crossterm --features="crossterm" --target=x86_64-pc-windows-gnu
error[E0463]: can't find crate for `core`
  |
  = note: the `x86_64-pc-windows-gnu` target may not be installed

error: aborting due to previous error

I can build for windows without +nighlty so not sure if that requires some extra rustup magic here? rustup show says I have it as expected:

$ rustup show 
Default host: x86_64-unknown-linux-gnu
rustup home:  /home/mrh/.rustup

installed toolchains
--------------------

stable-x86_64-unknown-linux-gnu
nightly-x86_64-unknown-linux-gnu

installed targets for active toolchain
--------------------------------------

arm-unknown-linux-musleabi
x86_64-pc-windows-gnu
x86_64-unknown-linux-gnu

active toolchain
----------------

stable-x86_64-unknown-linux-gnu (default)
rustc 1.44.0 (49cae5576 2020-06-01)

Thanks @Yandros, your solution works (if I make the crossterm dependency optional, as I had it originally).

It's ugly, as is using +nightly (if I can get that to work) for publishing to crates.io but I'm pleased to have a better understanding of what's happening. My thanks to you and @mbrubeck.

1 Like

To download the Windows target for the nightly toolchain, you can use:

rustup target add x86_64-pc-windows-gnu --toolchain nightly
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.