build.rs file, it's generally a mistake to do
cfg!(target_foo = "thing"), ad it will test the value on the host instead of the target. This means code that does this is broken when cross compiling, which is unfortunate (but extremely common).
(While this is confusing at first, it's also hard to argue it is not the correct way for this to work. If when run from a build script it worked some other way, then, say,
#[cfg(target_arch = "x86_64)] might be insufficient to soundly guard use of
core::arch::asm! for x86_64 code).
Instead, I believe we're we're supposed to use the values provided by cargo for build scripts, such as
CARGO_CFG_<cfg>. That is:
let target_foo_is_thing = std::env::var("CARGO_CFG_TARGET_FOO") .map_or(false, |v| v == "thing");
This properly checks the target's value for
target_foo rather than the host's. All good? Well, no. this is still possibly not quite right.
This may work now, and it may stay that way, or it may break under different compilation settings. Consider the case of
cfgs which may be used with multiple values, like
cfg!(target_feature = "abc") and
cfg!(target_feature = "cde") both evaluate to true, so what does
CARGO_CFG_TARGET_FEATURE use? It uses all of the values, separated by commas. So, for
cfg keys which may hold multiple values, you must account for this and write your test in a slightly different manner, for example:
let have_static_crt = std::env::var("CARGO_CFG_TARGET_FEATURE") .map_or(false, |v| v.split(",").any(|v| v == "static-crt"))
In fact, the statement "for
cfg keys which may hold multiple values" may be too conservative. Somewhat recently, the
target_family cfg changed from being single-valued to multi-valued, as
wasm has been added as a
target_family, and now is returned on certain targets which are also
unix; for example,
wasm32-unknown-emscripten comes through in
"unix,wasm". This was done without much discussion, and was celebrated by many, and several people have expressed desires to add even more target_families, for cases like
linux (linux/android), and so on.
I think we should be careful here. I believe the change from
unix,wasm on those targets was probably a breaking change, probably an accidental one (I looked and I couldn't find this being mentioned at all in the discussion about the new target family, but perhaps I missed it). In practice, we almost certainly "got away" with it for a few reasons:
- Most build scripts use
#[cfg()]for this, which would keep working in this case, despite being incorrect for cross compilation.
- The targets which changed are the
emscriptentargets, which are rare.
- Use of
CARGO_CFG_UNIXis slightly more common from what I can tell thant
CARGO_CFG_TARGET_FAMILY(but the latter does seem to get used)
Alternatively, I suppose we could take the stance that actually all of the cfg values may gain multiple values in the future. If we do this, we should document this fact more clearly than we do. (I also think it will lead to annoying breakage when running some build scripts which suddenly do the wrong thing). I don't know if I think this is useful, since many of these clearly cannot take multiple values, but it's unclear how we would clarify this fact.
Anyway, I don't really have a point with this, I just saw someone mention more
target_family values as a desirable goal recently, and wanted to bring this up for discussion. I personally am concerned it would cause subtle breakage, and that it should be done carefully at the very least.