diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 147e669..c38bd54 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -14,6 +14,7 @@ jobs: matrix: rust: [1.0.0, 1.5.0, 1.10.0, 1.15.0, 1.20.0, 1.25.0, 1.30.0, 1.35.0, 1.40.0, 1.45.0, 1.50.0, 1.55.0, 1.60.0, 1.65.0, 1.70.0, 1.75.0, + 1.80.0, # first version with rustc-check-cfg stable, beta, nightly] steps: - uses: actions/checkout@v4 @@ -22,6 +23,8 @@ jobs: toolchain: ${{ matrix.rust }} - run: cargo build --verbose - run: cargo test --verbose + - run: cargo run + working-directory: ./ci/verify-check-cfg # try probing a target that doesn't have std at all no_std: diff --git a/ci/verify-check-cfg/.gitignore b/ci/verify-check-cfg/.gitignore new file mode 100644 index 0000000..4683d3e --- /dev/null +++ b/ci/verify-check-cfg/.gitignore @@ -0,0 +1,3 @@ +# Rust +/target +/Cargo.lock diff --git a/ci/verify-check-cfg/Cargo.toml b/ci/verify-check-cfg/Cargo.toml new file mode 100644 index 0000000..23817a4 --- /dev/null +++ b/ci/verify-check-cfg/Cargo.toml @@ -0,0 +1,14 @@ +[package] +# NOTE: Cannot be in workspace because of rustc 1.0 support +name = "autocfg-verify-check-cfg" +description = "A dummy crate to verify autocfg is emitting check-cfg directives" +version = "0.1.0" +edition = "2015" +# only for testing +publish = false + +build = "build.rs" + +[build-dependencies] +autocfg = { path = "../.." } + diff --git a/ci/verify-check-cfg/build.rs b/ci/verify-check-cfg/build.rs new file mode 100644 index 0000000..76098b7 --- /dev/null +++ b/ci/verify-check-cfg/build.rs @@ -0,0 +1,45 @@ +extern crate autocfg; + +pub fn main() { + let cfg = autocfg::AutoCfg::new().unwrap(); + + // + // tests + // + + // always true + cfg.emit_rustc_version(1, 0); + // should always be false + cfg.emit_rustc_version(7, std::u32::MAX as usize); + + // always true + cfg.emit_has_path("std::vec::Vec"); + cfg.emit_path_cfg("std::vec::Vec", "has_path_std_vec"); + // always false + cfg.emit_has_path("dummy::DummyPath"); + cfg.emit_path_cfg("dummy::DummyPath", "has_path_dummy"); + + // always true + cfg.emit_has_trait("std::ops::Add"); + cfg.emit_trait_cfg("std::ops::Add", "has_trait_add"); + // always false + cfg.emit_has_trait("dummy::DummyTrait"); + cfg.emit_trait_cfg("dummy::DummyTrait", "has_trait_dummy"); + + // always true + cfg.emit_has_type("i32"); + cfg.emit_type_cfg("i32", "has_type_i32"); + // always false + cfg.emit_has_type("i7billion"); + cfg.emit_type_cfg("i7billion", "has_type_i7billion"); + + // always true + cfg.emit_expression_cfg("3 + 7", "has_working_addition"); + // always false + cfg.emit_expression_cfg("3 ^^^^^ 12", "has_working_5xor"); + + // always true + cfg.emit_constant_cfg("7", "has_const_7"); + // false - Opening file should never be `const` + cfg.emit_constant_cfg("std::fs::File::open(\"foo.txt\")", "has_const_file_open"); +} diff --git a/ci/verify-check-cfg/src/main.rs b/ci/verify-check-cfg/src/main.rs new file mode 100644 index 0000000..1b459b8 --- /dev/null +++ b/ci/verify-check-cfg/src/main.rs @@ -0,0 +1,43 @@ +#![allow(unknown_lints)] +#![deny(unexpected_cfgs)] + +macro_rules! test_cfgs { + ($($cfg:ident,)*) => {$({ + let cfg_desc = format!("cfg!({})", stringify!($cfg)); + if cfg!($cfg) { + println!("Enabled: {}", cfg_desc); + } else { + println!("Disabled: {}", cfg_desc); + } + })*}; + +} + +pub fn main() { + test_cfgs!( + // emit_rustc_version + rustc_1_0, + rustc_7_4294967295, + // emit_has_path, emit_path_cfg + has_std__vec__Vec, + has_path_std_vec, + has_dummy__DummyPath, + has_path_dummy, + // emit_has_trait, emit_trait_cfg + has_std__ops__Add, + has_trait_add, + has_dummy__DummyTrait, + has_trait_dummy, + // emit_has_type, has_type_i32 + has_i32, + has_type_i32, + has_i7billion, + has_type_i7billion, + // emit_expression_cfg + has_working_addition, + has_working_5xor, + // emit_constant_cfg + has_const_7, + has_const_file_open, + ); +} diff --git a/src/lib.rs b/src/lib.rs index d117b9d..fc3c59b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -100,6 +100,12 @@ pub struct AutoCfg { /// This looks like: `cargo:rustc-cfg=CFG` /// /// Cargo will use this in arguments to rustc, like `--cfg CFG`. +/// +/// This does not automatically call [`emit_possibility`] +/// so the compiler my generate an [`unexpected_cfgs` warning][check-cfg-flags]. +/// However, all the builtin emit methods on [`AutoCfg`] call [`emit_possibility`] automatically. +/// +/// [check-cfg-flags]: https://blog.rust-lang.org/2024/05/06/check-cfg.html pub fn emit(cfg: &str) { println!("cargo:rustc-cfg={}", cfg); } @@ -125,6 +131,25 @@ pub fn rerun_env(var: &str) { println!("cargo:rerun-if-env-changed={}", var); } +/// Indicates to rustc that a config flag should not generate an [`unexpected_cfgs` warning][check-cfg-flags] +/// +/// This looks like `cargo:rustc-check-cfg=cfg(VAR)` +/// +/// As of rust 1.80, the compiler does [automatic checking of cfgs at compile time][check-cfg-flags]. +/// All custom configuration flags must be known to rustc, or they will generate a warning. +/// This is done automatically when calling the builtin emit methods on [`AutoCfg`], +/// but not when calling [`autocfg::emit`](crate::emit) directly. +/// +/// Versions before rust 1.80 will simply ignore this directive. +/// +/// This function indicates to the compiler that the config flag never has a value. +/// If this is not desired, see [the blog post][check-cfg]. +/// +/// [check-cfg-flags]: https://blog.rust-lang.org/2024/05/06/check-cfg.html +pub fn emit_possibility(cfg: &str) { + println!("cargo:rustc-check-cfg=cfg({})", cfg); +} + /// Creates a new `AutoCfg` instance. /// /// # Panics @@ -230,8 +255,10 @@ impl AutoCfg { /// Sets a `cfg` value of the form `rustc_major_minor`, like `rustc_1_29`, /// if the current `rustc` is at least that version. pub fn emit_rustc_version(&self, major: usize, minor: usize) { + let cfg_flag = format!("rustc_{}_{}", major, minor); + emit_possibility(&cfg_flag); if self.probe_rustc_version(major, minor) { - emit(&format!("rustc_{}_{}", major, minor)); + emit(&cfg_flag); } } @@ -345,8 +372,10 @@ impl AutoCfg { /// Emits a config value `has_CRATE` if `probe_sysroot_crate` returns true. pub fn emit_sysroot_crate(&self, name: &str) { + let cfg_flag = format!("has_{}", mangle(name)); + emit_possibility(&cfg_flag); if self.probe_sysroot_crate(name) { - emit(&format!("has_{}", mangle(name))); + emit(&cfg_flag); } } @@ -366,13 +395,12 @@ impl AutoCfg { /// Any non-identifier characters in the `path` will be replaced with /// `_` in the generated config value. pub fn emit_has_path(&self, path: &str) { - if self.probe_path(path) { - emit(&format!("has_{}", mangle(path))); - } + self.emit_path_cfg(path, &format!("has_{}", mangle(path))); } /// Emits the given `cfg` value if `probe_path` returns true. pub fn emit_path_cfg(&self, path: &str, cfg: &str) { + emit_possibility(cfg); if self.probe_path(path) { emit(cfg); } @@ -394,13 +422,12 @@ impl AutoCfg { /// Any non-identifier characters in the trait `name` will be replaced with /// `_` in the generated config value. pub fn emit_has_trait(&self, name: &str) { - if self.probe_trait(name) { - emit(&format!("has_{}", mangle(name))); - } + self.emit_trait_cfg(name, &format!("has_{}", mangle(name))); } /// Emits the given `cfg` value if `probe_trait` returns true. pub fn emit_trait_cfg(&self, name: &str, cfg: &str) { + emit_possibility(cfg); if self.probe_trait(name) { emit(cfg); } @@ -422,13 +449,12 @@ impl AutoCfg { /// Any non-identifier characters in the type `name` will be replaced with /// `_` in the generated config value. pub fn emit_has_type(&self, name: &str) { - if self.probe_type(name) { - emit(&format!("has_{}", mangle(name))); - } + self.emit_type_cfg(name, &format!("has_{}", mangle(name))); } /// Emits the given `cfg` value if `probe_type` returns true. pub fn emit_type_cfg(&self, name: &str, cfg: &str) { + emit_possibility(cfg); if self.probe_type(name) { emit(cfg); } @@ -447,6 +473,7 @@ impl AutoCfg { /// Emits the given `cfg` value if `probe_expression` returns true. pub fn emit_expression_cfg(&self, expr: &str, cfg: &str) { + emit_possibility(cfg); if self.probe_expression(expr) { emit(cfg); } @@ -465,6 +492,7 @@ impl AutoCfg { /// Emits the given `cfg` value if `probe_constant` returns true. pub fn emit_constant_cfg(&self, expr: &str, cfg: &str) { + emit_possibility(cfg); if self.probe_constant(expr) { emit(cfg); }