cuviper / autocfg

Automatic cfg for Rust compiler features
Apache License 2.0
95 stars 24 forks source link

Test if a dependency contains a path or symbol #60

Open flaviojs opened 4 months ago

flaviojs commented 4 months ago

Sorry if this is the wrong crate. This crate is the closest that I could find to the functionality that I need.

I'm porting code from C to rust that has #if defined(...) code. The crate libc has the symbols I need but I can't find a way to automatically test if they actually exist (within cargo/rust) and do conditional compilation with that. Using '#[cfg(target_os = "...")]' combinations would be very error prone and a huge maintenance burden that I'm not gonna introduce.

What I need is something like this for build.rs:

let ac = autocfg::new();
// TODO some way to say that the test depends on the crate libc
ac.emit_path_cfg("libc::B76800", "has_libc_b76800");

Is it possible to use or adapt this crate for this use case? If not, can you point me to any other crate that I can look into?

cuviper commented 4 months ago

I think it would be a useful extension here, but I don't know a good way to accomplish it. The crude way would be to add -L target/$profile/deps to the rustc commands, if we can figure out that path, and you would also need extern crate libc; in the probe code to load it. However, this would fail if there are multiple candidate libraries in that path, which can happen due to changing build flags or other factors.

The clean way to add dependency crates would be with --extern, as cargo would pass to rustc itself. I think there's already a feature request issue on cargo to communicate that to build scripts, but I can't find it right now...

flaviojs commented 4 months ago

Tried to mess a bit with autocfg but got blocked by the rustc_private feature. In the meantime I found a temporary solution:

extern crate autocfg;
use std::process::Command;
use std::process::Output;

fn main() {
    // TODO how to get around rustc_private with autocfg
    // FIXME temporary solution: rust-script compiles for and runs in the local system? I want to test-compile for the target system or similar

    // does rust-script work? (warning: outputs to stdout/stderr)
    let status = Command::new("rust-script").arg("--version").status().expect("rust-script is required, running 'cargo install rust-script' in the console should fix this error\n");
    if !status.success() {
        panic!("rust-script: {}", status);
    }
    // sanity check: libc::size_t should always exist
    assert!(Command::new("rust-script").args(["--dep", "libc", "--expr", "{ let _: libc::size_t = 0; }"]).output().unwrap().status.success());
    fn probe_dep_path(dep: &str, path: &str) -> Output {
        let expr = format!("{{ use {}; }}", path);
        Command::new("rust-script").args(["--dep", dep, "--expr", &expr]).output().unwrap()
    }
    fn emit_dep_path_cfg(dep: &str, path: &str, cfg: &str) {
        if probe_dep_path(dep, path).status.success() {
            autocfg::emit(cfg);
        }
    }
    emit_dep_path_cfg("libc", "libc::B76800", "has_libc_B76800");
    // ...
}