How to extract nightly features used in a crate? - rust

I want to extract all nightly features used by a crate for some research purposes.
Just to be clear, "nightly features" means codes like #![feature(...)]", but not crate provided optional features.
I tried using regex, and it works, but not accurate enough. cfg defined features may not be detected: #![cfg_attr(target_os = "macos", feature(...))].
So I turn to rustc compiler, I wish to modify some source code and print out what features crate uses. Now I'm still trying to find out how rustc deals with nightly features.
I hope someone can give me some tips and help about the process of rustc dealing with nightly features. I'm almost lost in codes.

Access to enabled features is done via rustc_session::Session::features. You can add code after they are set in rustc_interface, or look at how they're computed.
If you are at a later stage, you probably have access to the Session of the compiled crate. For example, TyCtxt has a sess() method (but also a features() method for direct access using the query system), and InferCtxt has a tcx member to access the TyCtxt.

Related

Is it documented that Cargo can download and bundle multiple versions of the same crate?

By forking and playing with some code, I noticed that Cargo can download and bundle multiple versions of the same crate in the same project (native-tls 0.1.5 and 0.2.1, for example). I have wasted so much time by looking at the documentation of the wrong version.
I have looked for some information about this behaviour but I was not able to find anything. Is this documented somewhere?
Is there an easy way to determine/detect the version used by the code you're working on (current edited file)? Or can we tell Cargo to show some warnings/prevent the build if two versions the same crate are required?
It was a conscious decision when designing Rust to allow multiple versions of the same crate.
You have probably heard of Dependency Hell before, which occurs when 2 (or more) dependencies A and B have a common dependency C but each require a version which is incompatible with the other.
Rust was designed to ensure that this would not be an issue.
In general, cargo will attempt to find a common version which satisfies all requirements. As long as crate authors use SemVer correctly, and the requirements give enough leeway, a single version of the dependency can be computed and used successfully.
Sometimes, however, multiple versions of the same dependency are necessary, such as in your case since 0.1.x and 0.2.x are considered two different major versions. In this case, Rust has two features to allow the two versions to be used within the same binary:
a unique hash per version is appended to each symbol.
the type system considers the same type Foo from two versions of C to be different types.
There is a limitation, of course. If a function of A returns an instance of C::Foo and you attempt to pass it to a function of B, the compiler will refuse it (it considers the two types to be different). This is an intractable problem1.
Anytime the dependency on C is internal, or the use of C is isolated, it otherwise works automatically. As your experience shows, it is so seamless that the user may not even realize it is happening.
1 See the dtolnay trick that crate authors can use to allow some types to be interchangeable.
Cargo can indeed link multiple versions of some crate, but only one of those versions can be a direct dependency. The others are indirect references.
The direct reference is always the version referenced by Cargo.toml and on top-level of Cargo.lock (while the indirect references are in the dependencies subsections).
I am not sure how much it is documented, unfortunately.

How can I make two features enabled lead to a conflict in Rust?

I have two features: feature_1 and feature_2:
[features]
default = ["feature_1"]
feature_1 = []
feature_2 = []
I want to let the user choose only one of them at a time because choosing both at the same time will lead to a duplicating of some important code and by some other reasons. How can I do that?
Fundamentally, you can't. Cargo features are additive, and features can be enabled by any crate in the dependency tree. There's an implicit assumption on the part of Cargo that it is always valid to enable additional features.
What's more, features aren't simply requests, they are demands. If one crate demands feature_1, and another crate demands feature_2, you've got two crates that cannot possibly work together. Cargo (and Rust itself) go to some lengths to try and make such situations difficult to create.
The best solution is to change how your crate is written such that both features can be enabled simultaneously. If that's really impossible, the best you can do is prevent compilation from succeeding. This can be done either with a build script for your crate that detects incompatible features and fails, or by placing non-compiling code in your crate that is only enabled when incompatible features are enabled.
For what it's worth, I opted to use this approach:
#[cfg(all(feature = "feature_1", feature = "feature_2"))]
compile_error!("Feature 1 and 2 are mutually exclusive and cannot be enabled together");
I hope it'll help others looking for a solution to the same problem.

Does CMake support glibc feature test macros

Let's say I want to use a specific Linux / POSIX feature, that is provided conditionally based on feature test macros. For example, the type cpu_set_t, the macro CPU_SET_ZERO, and the function sched_setaffinity.
Ideally I would just like to tell CMake that I need those, and it should figure out what extra feature test macros to set or fail with a nice error message if it can't be provided on the current system. Is that possible?
I am aware that I can lookup in the manpages and manually use add_definitions(-D_GNU_SOURCE), but that can become tedious once multiple functionalities that were introduced and deprecated in different versions of the POSIX standard are combined. In my experience, it can become difficult to maintain portability across different versions of the glibc implementation.
There are the CMake platform checks, but they only seem to help in checking. So I get the error during cmake rather than make, but I still have to figure out the right feature test macros manually.
cmake-compile-features seem to offer only features directly related to the compiler, not the library.
If you are just looking to determine whether a function or variable exists, you can use the CheckSymbolExists module. Likewise, there is CheckStructHasMember for structs (assuming there is a standard member you can check for). So:
include (CheckSymbolExists)
include (CheckStructHasMember)
CHECK_SYMBOL_EXISTS(CPU_SET_ZERO sched.h CPU_SET_ZERO_exists)
CHECK_SYMBOL_EXISTS(sched_setaffinity sched.h sched_setaffinity_exists)
CHECK_STRUCT_HAS_MEMBER(cpu_set_t <member?> sched.h cpu_set_t_exists)
It appears that cpu_set_t is an opaque type, so instead you can use the CheckCXXSourceCompiles module, which is a frontend for the try_compile command. It is a generic way to determine if any particular code compiles. try_compile is used extensively by 'base' CMake to determine features (try a search in the Modules directory!). Essentially, you pass it in a minimal source file, which should fail compilation if your feature is not present, and it reports back to your CMake script the result.

How can I use a preprocessor in Rust to detect the version number? [duplicate]

In C++, you could use something like __clang_version__. Is there something similar for Rust? I searched on the internet, but found nothing.
Not directly.
There is the rustc_version crate which tells you the version of rustc accessible on the command-line; this is designed to be used in a build script. There's also rustc_version_runtime which does something similar, but exposes the information as a runtime call (i.e. it detects the compiler version at compile time, but exposes it at runtime).
Standard disclaimer: be very careful writing anything that depends on compiler version. You should ideally only test for minimum versions for which features are supported using semver (which both of the above libraries support directly).

Is there a way to detect the compiler version from within a Rust program?

In C++, you could use something like __clang_version__. Is there something similar for Rust? I searched on the internet, but found nothing.
Not directly.
There is the rustc_version crate which tells you the version of rustc accessible on the command-line; this is designed to be used in a build script. There's also rustc_version_runtime which does something similar, but exposes the information as a runtime call (i.e. it detects the compiler version at compile time, but exposes it at runtime).
Standard disclaimer: be very careful writing anything that depends on compiler version. You should ideally only test for minimum versions for which features are supported using semver (which both of the above libraries support directly).

Resources