RFC: cfg_target_version
This RFC builds on the work of @rylev and @chriswailes. As suggested by @tmandry I have stripped it down to an MVP that just adds target_version (aka os_version_min in previous RFCs).
The idea is great, but I think the syntax can be improved. It makes more sense to have this feature under the existing target_os config, so here's an idea:
cfg(target_os("windows", min_version = "..."))
Another idea:
cfg(target_os = "windows >= ...")
That may work for windows but not, for example, Linux where you may want to cfg on the kernel version or the libc version (or even both).
libc isn't really a target_os.
Right, so this could be done with a target_libc attribute, like
cfg(target_libc("glibc", min_version = "..."))
But of course this would add more complexity.
Ok I've rewritten this a bit and further simplified it based on feedback.
Update: https://github.com/rust-lang/rfcs/pull/3857 was just posted, with that, things in here would look like:
pub fn random_u64() -> u64 {
let mut rand = 0_u64.to_ne_bytes();
if cfg!(since(windows, "10.0.10240")) {
// For an API version greater or equal to Windows 10, we use `ProcessPrng`
unsafe { ProcessPrng(rand.as_mut_ptr(), rand.len()) };
} else {
// Otherwise we fallback to `RtlGenRandom`
unsafe { RtlGenRandom(rand.as_mut_ptr().cast(), rand.len() as u32) };
}
u64::from_ne_bytes(rand)
}
// Always available under these conditions.
#[cfg(any(
since(macos, "11.0"),
since(ios, "14.0"),
since(tvos, "14.0"),
since(watchos, "7.0"),
since(visionos, "1.0")
))]
let preadv = {
extern "C" {
fn preadv(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize;
}
Some(preadv)
};
// Otherwise `preadv` needs to be weakly linked.
// We do that using a `weak!` macro, defined elsewhere.
#[cfg(not(any(
since(macos, "11.0"),
since(ios, "14.0"),
since(tvos, "14.0"),
since(watchos, "7.0"),
since(visionos, "1.0")
)))]
weak!(fn preadv(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
if let Some(preadv) = preadv {
preadv(...) // Use preadv, it's available
} else {
// ... fallback impl
}
I'm up for updating this RFC to match RFC 3857, but let's way a bit to let RFC 3857 progress further.
I may be missing something, but how would I build targeting an older version than my host, or select the target version when cross compiling? I would expect a -C kernel-version= and -C libc-version= (similar for other platforms like windows of course, and all subject to name bikeshedding that I don't care about).
This RFC is deliberately minimal in contrast to previous RFCs in this series which lang felt were doing too much at once.
As such it only allows for a single version for each target which is purely informational. This is still useful since crates can simply use this cfg instead of trying to infer it from target tuple. In the future this could (and hopefully will) be extended to allow configuring the target version within a supported range but that decision is deliberately deferred.
I may be missing something, but how would I build targeting an older version than my host, or select the target version when cross compiling?
It's in Future possibilities. I think it's beyond the scope of this RFC.
I have nominated this for you, T-lang, because while I am sure you are aware of this RFC, those I have talked to think it is waiting on you to poke at it, but I haven't seen it on any recent T-lang meeting agenda. So, we would like to know T-lang's disposition on the RFC, aside from the obvious "well, maybe cfg(since(..)) will affect what we want to do here?"
I have nominated this for you, T-lang, because while I am sure you are aware of this RFC, those I have talked to think it is waiting on you to poke at it, but I haven't seen it on any recent T-lang meeting agenda. So, we would like to know T-lang's disposition on the RFC, aside from the obvious "well, maybe
cfg(since(..))will affect what we want to do here?"
For my part, it seems a problem worth solving, but as you say, I'm expecting that an approach more similar to https://github.com/rust-lang/rfcs/pull/3857 will subsume this.
Are there any more specific questions that you have here?
Yeah, #3857 steals much of the lunch of this RFC assuming it is accepted in something functionally equivalent to its current state. This will need to be rewritten in light of that and may well be effectively a new RFC so I'm wondering if I should create a new one rather than reusing this.
In either case the question left to answer here would be whether we're ok adding since values for external system libraries (i.e. those outside of Rust's control) and bikeshedding their names (e.g. generic kernel or libc vs. specific linux, glibc, musl, etc). Also this RFC proposes that it's not an error to use names that don't exist for the current target (they just evaluate to false). I think that would need to be strengthened to an allowed set of built-in names or else simply made an error (though people weren't so enthusiastic about the latter).
Also this RFC proposes that it's not an error to use names that don't exist for the current target (they just evaluate to false). I think that would need to be strengthened to an allowed set of built-in names or else simply made an error (though people weren't so enthusiastic about the latter).
I personally would prefer it to be an error by default lint.
Hey all! Platform Versioning lead for Fuchsia here. Glad to see progress is being made on this! I just created https://fxbug.dev/452324620 on the Fuchsia side to incorporate this once it lands :crossed_fingers:
In terms of feedback on the RFC, two comments:
-
First, cosmetically, it'd be nice if the syntax was clearer about the
>=semantics. As written, if a library wanted to use some functionality that was supported between Fuchsia API levels 20 and 25, it'd look like:#[cfg(all(target_version("fuchsia", "20"), not(target_version("fuchsia", "26")))... which is kinda like, "duh, it's not version 26 - we just asserted it was version 20". Whereas
#[cfg(all(target_version("fuchsia", ">=20"), not(target_version("fuchsia", ">=26")))... (or another of the infinite bikeshed colors) would be much clearer.
-
More significantly, the idea of deriving the version from the target is interesting. Of course, the standard library will use some OS functionality, and there will be some minimum version where that functionality is supported, so it makes sense that a target will have a minimum supported version. But third party libraries can use a much broader set of APIs, so they'll want to be able to condition their behavior based on specific API levels.
It brings to mind two approaches:
- Keep the "version is specified by the target" property, and create a new target for each API level, e.g.
arm64-unknown-fuchsia-20,arm64-unknown-fuchsia-21, etc. We mint new API levels about quarterly, at the moment. I don't know enough about Rust's internals to understand whether this is a, "huh, that's unusual but in principle it could work..." proposal, or a "are you completely nuts? Ew, no" proposal. - Allow specifying the API level on the command line, as you mention at the end. Then we'd only create a new target when we remove some functionality used by the standard library, which will be quite rare. I suspect this will be the way to go, and that we'll need this command line option before we can use this RFC properly.
- Keep the "version is specified by the target" property, and create a new target for each API level, e.g.
Thanks again for pushing this forward!