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:
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.
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.
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.
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.
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: