Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Obtain the Cargo compiler configuration for use in subcommands without building the package #8923

Closed
volks73 opened this issue Nov 30, 2020 · 3 comments
Labels
C-feature-request Category: proposal for a feature. Before PR, ping rust-lang/cargo if this is not `Feature accepted`

Comments

@volks73
Copy link
Contributor

volks73 commented Nov 30, 2020

Describe the problem you are trying to solve

I would like to obtain the Rust compiler (rustc) configuration, i.e. the output from the cargo rustc -- --print cfg, within cargo subcommands. The output from the cargo rustc -- --print cfg command will be different from the rustc --print cfg command because Cargo will pass additional options based on Cargo environment variables and configuration files. For example, the RUSTFLAGS environment variable is "managed" by Cargo and not recognized by rustc. So, the following could occur:

PS C:\> $env:RUSTFLAGS="-C target-feature=+crt-static";
PS C:\> cargo rustc -- --print cfg
  Compiling <PACKAGE> vX.X.X (<PACKAGE_PATH>)
debug_assertions
target_arch="x86_64"
target_endian="little"
target_env="msvc"
target_family="windows"
target_feature="crt-static"
target_feature="fxsr"
target_feature="sse"
target_feature="sse2"
target_os="windows"
target_pointer_width="64"
target_vendor="pc"
windows
  Finished dev [unoptimized + debuginfo] target(s) in 0.10s
PS C:\> rustc --print cfg
debug_assertions
target_arch="x86_64"
target_endian="little"
target_env="msvc"
target_family="windows"
target_feature="fxsr"
target_feature="sse"
target_feature="sse2"
target_os="windows"
target_pointer_width="64"
target_vendor="pc"
windows

Note the existence of the target_feature="crt-static" in the output from the cargo rustc -- --print cfg command versus the output from the rustc --print cfg command, where the target_feature="crt-static line is missing.

There are also a couple of other issues with the above example. One, the cargo rustc -- --print cfg actually builds the package. It does not skip building the package, whereas the rustc --print cfg command simply prints the configuration and does not compile. Ideally, the cargo rustc -- --print cfg command would also not compile the entire package. Second, the format is human-readable, but it would also be nice to have more machine/subcommand readable format, such as JSON that also omits the "Compiling...." and "Finished..." lines. Third, the cargo rustc -- --print cfg command only works if a single Cargo target is defined for the package; otherwise, an error message appears indicating that passing arguments to the compiler requires a specific Cargo target, e.g. --lib or --bin <NAME>.

Much of this came about from developing the cargo-rustc-cfg crate for use in the cargo-wix subcommand, where we ultimately want to know if a user's project is statically linking the Windows C Runtime (CRT), provide information to WiX templates for conditional builds of Windows installers, and working on supporting cross-compilation, mostly between x86_64, i686, MSVC, and GNU rustc targets on Windows; hence, the Windows-centric examples.

I recognize this is closely related to the discussion around --build-plan flag, the cargo-metadata crate, and the --unit-graph flag. It is possible that I have missed some field or output in these that would yield similar information to the cargo rustc -- --print cfg command, but in my review the metadata output appears to be too high level and does not contain compiler configuration information and the --unit-graph output only has the platform field, which is the target triple (Side note, this will be useful for the cargo-wix subcommand once it is stabilized but won't get us everything we would like to have). I understand there is some hesitation to expose too much Cargo build internals and the multi-targets feature could be a complication.

Describe the solution you'd like

I currently see multiple solutions:

  1. Add a --cfg flag to the cargo rustc subcommand, so that the following would be possible:

    PS C:\> cargo rustc --cfg
    debug_assertions
    target_arch="x86_64"
    target_endian="little"
    target_env="msvc"
    target_family="windows"
    target_feature="fxsr"
    target_feature="sse"
    target_feature="sse2"
    target_os="windows"
    target_pointer_width="64"
    target_vendor="pc"
    windows

    This would not build the project but any compiler arguments from Cargo-related environment variables and/or configuration files (.cargo/config.toml) would be applied. I think having the flag instead of inspecting the <args> Rustc args values for a --print cfg would be easier to implement. I am open to alternative names for the flags as well, i.e. --print-cfg, --config, --print-config, etc. However, I do know that Cargo looks for the --target option in the <args> Rustc args and warns about targets being defined in two places, so some inspection already exists.

  2. If 1 is implemented, then possibly add an option to change the format of the output, i.e. --cfg-format=JSON or --cfg-json. I would actually be fine with defaulting the output to JSON and not having key-value line-based output format. I think this would require some parsing of the rustc <ADDITIONAL_CARGO_COMPILER_ARGS> --print cfg output from within Cargo as opposed to just "forwarding" the output to stdout. I could easily add the conversion to JSON to the cargo-rustc-cfg crate if "forwarding" is quicker and easier.

  3. Add the compiler configuration to the --unit-graph output. This seems appropriate but I am not sure how this fits in with the larger discussion, desires, and goals of the --unit-graph feature.

  4. Add the compiler configuration to the cargo-metadata output. This does not seem appropriate as the metadata appears to be more about the package configuration than the compiler configuration.

  5. A combination of 1, 2, and 3.

Notes

I am willing to help implement any of these items or new ones, but I wanted to gauge interest and check if I missed something in obtaining the compiler configuration from within a cargo subcommand.

@volks73
Copy link
Contributor Author

volks73 commented Jan 17, 2021

For reference, a sequence that demonstrations some of the complications in obtaining Cargo-modified compiler configuration:

  1. Reviewing Cargo's CLI and help documentation, there is no flag or option to print anything for the compiler configuration. The closest is --unit-graph, but that only includes the target triple. Extra arguments can be used, which is consistent with other subcommands and usage (cargo test -- --test-threads=1 for example):

    PS C:\cargo> cargo rustc -- --print cfg
    error: extra arguments to `rustc` can only be passed to one target, consider filtering
    the package by passing, e.g., `--lib` or `--bin NAME` to specify a single target
    PS C:\cargo>
  2. Following the error message's instructions from the previous step to print the compiler configuration with Cargo modifications:

    PS C:\cargo> cargo rustc --lib -- --print cfg
       Compiling winapi v0.3.9
       Compiling jobserver v0.1.21
       Compiling cfg-if v0.1.10
       ...
    debug_assertions
    target_arch="x86_64"
    target_endian="little"
    ...
    target_feature="crt-static"
    ...
        Finished dev [unoptimized + debuginfo] target(s) in 1m 38s
    PS C:\cargo>

    This works! If it is a clean build, then the dependencies are compiled before the extra arguments are passed to the compiler for the Cargo target (--lib). This is not a huge problem for a relatively small project or if a build is already cached, but it is a another deviation from the behavior of the rustc --print cfg command. It seems like a shortcut or mechanism to skip some functionality of the cargo rustc subcommand is needed.

    Furthermore, what about packages that do not have a --lib Cargo target or have multiple Cargo targets? The package's manifest (Cargo.toml) needs to be inspected to determine which Cargo target is available and implement some "best Cargo target" selection rule or loop through all Cargo targets in a package. There is the cargo metadata subcommand and cargo-metadata crate to help with getting the Cargo targets, but this now becomes a multiple command process.

  3. The compiler configuration can be printed for a specific compiler target using rustc --target <TRIPLE> --print cfg. This is useful for "probing" different configurations for compiler targets and debugging builds. Compiler configurations can be specific to compiler targets. The cargo rustc command also has a --target <TRIPLE> option.

    PS C:\cargo> cargo rustc --target i686-pc-windows-msvc --lib -- --print cfg
       Compiling cfg-if v0.1.10
       Compiling lazy_static v1.4.0
       Compiling tinyvec v0.3.4
       Compiling matches v0.1.8
       Compiling regex-syntax v0.6.18
    error[E0463]: can't find crate for `core`
      |
      = note: the `i686-pc-windows-msvc` target may not be installed
    PS C:\cargo>

    Not sure about the error and its source. Admittedly, this is a little beyond me at the moment but I believe this is related to cross-compiling, where the dependencies are being compiled for the compiler target instead of the host. The error can be worked around with:

    PS C:\cargo> cargo rustc --lib -- --target i686-pc-windows-msvc --print cfg
       Compiling cargo v0.51.0 (C:\Users\cfield\Code\cargo)       
       ...
       Finished dev [unoptimized + debuginfo] target(s) in 0.44s    
    PS C:\cargo>
  4. There is also the multitarget feature to consider:

    PS C:\cargo> cargo rustc -Zmultitarget --target i686-pc-windows-msvc --target x86_64-pc-windows-gnu --lib -- --print cfg
    error: extra arguments to `rustc` can only be passed to one target, consider filtering
    the package by passing, e.g., `--lib` or `--bin NAME` to specify a single target
    PS C:\cargo>

    From Item 3, we already know this is not going to work, rustc --print cfg does not support multiple --target options, and --target cannot be used as an option for cargo rustc and as extra arguments. As a workaround, I think cargo metadata and cargo rustc --unit-graph need to be used to obtain the target triples to manually pass the compiler target triples, command by command, in combination with the Cargo targets.

bors added a commit that referenced this issue Feb 23, 2021
Add --cfg and --rustc-cfg flags to output compiler configuration

This PR is my attempt to address #8923.

I have added the `--cfg` flag to the `cargo rustc` subcommand and the `--rustc-cfg` flag to the `cargo build`, `cargo check`, `cargo test`, and `cargo bench` subcommands, respectively. Both versions of the flag, `--cfg` and `--rustc-cfg`, do the same thing, but I thought it looked weird for the `cargo rustc` subcommand to be `cargo rustc --rustc-cfg`. The following example invocations are possible, once stabilized:

- `cargo rustc --cfg`
- `cargo build --rustc-cfg`
- `cargo check --rustc-cfg`
- `cargo test --rustc-cfg`
- `cargo bench --rustc-cfg`

In each case, compilation is aborted and only compiler configuration is emitted. All of the context creation and configuration is still executed, but execution of the compilation job is aborted. Similar to the `--unit-graph` implementation, the `--cfg/--rustc-cfg` flag is hidden, marked as a "unstable", and requires the `-Z unstable-options` flag at the moment. A complete example invocation with this PR would be:

```bash
$ cargo +nightly rustc -Z unstable-options --cfg
```

I am open to alternatives for the flag name. I have thought of `--compiler-cfg`, `--compile-cfg`, and `--target-cfg`, but I went with `--rustc-cfg` because it is the Rust compiler (rustc) configuration (cfg). The `--target-cfg` could be confusing because there are Cargo targets and Compiler targets. A lone `--cfg` for the build, check, test, and bench subcommands would also be ambiguous and configuration that is being displayed.

Originally, I was only going to add the `--cfg` flag to the `cargo rustc` subcommand, but build, check, test, and bench all have the `--unit-graph` flag, and I used that flag's implementation and existence as a template for this implementation. I am not opposed to having just the `--cfg` flag for the `cargo rustc` subcommand as originally proposed in #8923.

I discovered during my initial investigation to implement the feature and familiarization with the Cargo codebase, that as part of the build context creation and compilation process, Cargo internally calls the `rustc --print cfg` command for all targets and stores the output in the `RustcTargetData` type. It does this for the host and any explicitly defined targets for cross-compilation. Compilation features, such as `+crt-static`, added to the compilation process through Cargo environment variables or TOML configuration directives are added to the internal `rustc --print cfg` command call. So, the `--cfg/--rustc-cfg` just emits the contents of the `RustcTagetData` type as JSON. There is no need to call the `rustc --print cfg` command again. The implementation then becomes nearly identical to the `--unit-graph` implementation.

The output is only in JSON, similar to the `--unit-graph` feature, instead of the key-value and name style of the `rustc --print cfg` output because it easily resolves issues related to multi-targets, explicit target (`--target <TRIPLE>`) versus host configurations, and cross compilation. Subcommands and other projects can parse the JSON to recreate the key-value and name style if desired based on a specific target or the host. Here is an example of the JSON output (formatted for humans, the actual output is condensed), which is also available as a comment in the `cargo::core::compiler::rustc_cfg` module:

```javascript
{
     "version": 1,
     "host": {
         "names": ["windows", "debug_assertions"],
         "arch":"x86_64",
         "endian":"little",
         "env":"msvc",
         "family":"windows",
         "features":["fxsr","sse","sse2"],
         "os":"windows",
         "pointer_width":"64",
         "vendor":"pc"
     },
     "targets": {
         "x86_64-unknown-linux-gnu": {
             "names": ["debug_assertions", "unix"],
             "arch":"x86_64",
             "endian":"little",
             "env":"gnu",
             "family":"unix",
             "features": ["fxsr","sse","sse2"],
             "os":"linux",
             "pointer_width":"64",
             "vendor":"unknown"
         },
         "i686-pc-windows-msvc": {
             "names": ["windows", "debug_assertions"],
             "arch":"x86",
             "endian":"little",
             "env":"msvc",
             "family":"windows",
             "features":["fxsr","sse","sse2"],
             "os":"windows",
             "pointer_width":"32",
             "vendor":"pc"
         }
     }
 }
```

I decided to remove the "target_" prefix from the relevant configurations to reduce "noise" in the output. Again, I am open to alternatives or suggestions on the JSON format.
@volks73
Copy link
Contributor Author

volks73 commented Feb 27, 2021

This has been addressed with PR #9002. The Cargo-based compiler configuration can be obtained using the cargo rustc --print cfg command. The --print option is forwarded to rustc and does not compile, but it does pass any compiler configurations from Cargo's configuration files and environment variables. The --print <FOO> is simply forwarded to rustc and its output is printed to Cargo's STDOUT/STDERR.

@ehuss
Copy link
Contributor

ehuss commented Apr 13, 2021

I opened tracking issue #9357 to track the new --print flag for more discussion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-feature-request Category: proposal for a feature. Before PR, ping rust-lang/cargo if this is not `Feature accepted`
Projects
None yet
Development

No branches or pull requests

2 participants