Improve the #[cfg] operation?

I'm working on a c library wrapped for rust.

In build.rs, I pass the c library version to the rust cfg using: cargo:rustc-cfg=some_version=VERSION.

Because I have to adapt a lot of versions of the c library, but some api release in higger version, so I want to do something like this:

#[cfg(some_version >= "1.1")]
pub fn some_api() {
    the_c_library_sys::some_api();
}

I know that the value of cfg is just a string, the operation such as >= is not suitable, is it a good idea to support some string operation in cfg? such as start_with, semantics_version_match, like:

#[cfg(semantics_version_match(some_version, "^1.1"))]
pub fn some_api() {
    the_c_library_sys::some_api();
}

I've seen this pattern used:

#[cfg(feature = "v1_1")]
[features]
v1_5 = ["v1_4"]
v1_4 = ["v1_3"]
v1_3 = ["v1_2"]
v1_2 = ["v1_1"]
v1_1 = []
4 Likes

The other thing to do is to be "smarter" about how you pass the version in build.rs. Rather than just setting a cfg for the current version, set cfgs more directly in line with what you actually want to cfg gate.

E.g. instead of cfg:lib_version="1.2", cfg:lib_some_api="1.1" to say that some_api is available at the 1.1 introduced ABI.

For example, this is how PyO3 does it: use_pyo3_cfgs in pyo3_build_config - Rust

I think using #[cfg(...)] in build.rs is often not what you want when cross compiling unless it is actually a conditional for your build.rs rather than the target, and the CARGO_CFG_TARGET_* environment variables should be used instead.

In the tectonic_cfg_support crate, we tried to emulate cfg macros with the target_cfg https://crates.io/crates/tectonic_cfg_support

It isn't able to completely support all the variadic ways which cfg can be used though in which case you have to fall back to the environment variable parsing.

Figured i'd mention it though...

That's what I do for standback. I also use a build script to emit "since this version" cfgs.

This way the users have to detect what version of c library they are using, rather than auto detect in build.rs, because build.rs don't support cfg feature.

Does the library ensure backwards compatibility? If so, what is wrong about targeting the version specified by the user or the first version if the user doesn't specify anything? That will ensure that you don't accidentally depend on a newer version than intended and allows binaries compiled on a system with a new version of the library to run on a system with an older version. Glibc doesn't do this and it makes life a lot harder for some people.

build scripts can set config values, so you can still use the auto-detection approach, and similarly set cfg for all previous versions.

But if you support that kind of compile-time flexibility, you can't use #[cfg()] any more to hide unsupported interfaces. In code like this the function must exist always:

if foo_version() > 1.5 {
   foo_new_feature();
}

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.