Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ jobs:
uses: wasmerio/setup-wasmer@v2
with:
version: "prerelease"
- name: Set up wasixcc
# Puts wasixcc/wasixcc++/wasixar on PATH so the wasixcc integration
# tests exercise the real thing instead of skipping.
uses: wasix-org/wasixcc@v0.4.3
with:
# Authenticated GitHub API requests/downloads; without this the
# action hits anonymous per-IP rate limits on shared runners.
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: test
shell: bash
run: cargo test --all-features -j1
Expand Down
41 changes: 41 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,47 @@ fn rmain(config: &mut Config) -> Result<()> {
}
}

// Point the `cc` crate (and anything else honoring its conventions) at
// the wasixcc toolchain when it's installed. The variables are scoped to
// the target — `cc` checks `CC_<target>` before `TARGET_CC` and `CC` —
// so a host compiler configured in the user's environment neither blocks
// this nor leaks into the WASIX build, and the user's generic setting is
// never touched. A target-scoped variable already set by the user (in
// either the dashed or underscored spelling) is respected.
for (tool, wasix_tool) in [
("CC", "wasixcc"),
("CXX", "wasixcc++"),
("AR", "wasixar"),
("RANLIB", "wasixranlib"),
] {
let dashed = format!("{tool}_{target}");
let underscored = format!("{tool}_{}", target.replace('-', "_"));
if env::var_os(&dashed).is_some() || env::var_os(&underscored).is_some() {
continue;
}
if which::which(wasix_tool).is_ok() {
// SAFETY: not safe in multi-threaded environment
unsafe { env::set_var(&underscored, wasix_tool) };
config.verbose(|| config.info(&format!("Set {underscored}={wasix_tool}")));
}
}

// For the -dl target, set wasixcc-specific environment variables
if target.ends_with("-dl") {
// Enable position-independent code for dynamic linking
if std::env::var("WASIXCC_PIC").is_err() {
// SAFETY: not safe in multi-threaded environment
unsafe { env::set_var("WASIXCC_PIC", "1") };
config.verbose(|| config.info("Set WASIXCC_PIC=1 for -dl target"));
}
// Enable WASM exceptions for better performance and C++ exception support
if std::env::var("WASIXCC_WASM_EXCEPTIONS").is_err() {
// SAFETY: not safe in multi-threaded environment
unsafe { env::set_var("WASIXCC_WASM_EXCEPTIONS", "1") };
config.verbose(|| config.info("Set WASIXCC_WASM_EXCEPTIONS=1 for -dl target"));
}
}

// Make sure the project resolves crates through the WASIX overlay
// registry before running cargo. Every subcommand from here on resolves
// the dependency graph (`download-toolchain` returned above), including
Expand Down
131 changes: 131 additions & 0 deletions tests/tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1136,3 +1136,134 @@ fn registry_config_respects_existing_replacement() -> Result<()> {
assert_eq!(written, existing);
Ok(())
}
#[test]
fn wasixcc_env_vars_set() -> Result<()> {
// The wasixcc tools are pointed at via target-scoped variables (the ones
// the `cc` crate checks first), so a host compiler in the generic CC/CXX
// never blocks them and is never modified.
if which::which("wasixcc").is_err() {
eprintln!("SKIPPED wasixcc_env_vars_set: wasixcc not on PATH");
return Ok(());
}

let p = support::project()
.file("src/main.rs", "fn main() {}")
.file(
"build.rs",
r#"
fn main() {
for var in [
"CC_wasm32_wasmer_wasi",
"CXX_wasm32_wasmer_wasi",
"AR_wasm32_wasmer_wasi",
"CC",
] {
// Changed env vars must re-run this script, or later
// builds replay the first run's cached warnings.
println!("cargo:rerun-if-env-changed={}", var);
match std::env::var(var) {
Ok(v) => println!("cargo:warning={} is set to: {}", var, v),
Err(_) => println!("cargo:warning={} is not set", var),
}
}
println!("cargo:rerun-if-env-changed=CC_wasm32-wasmer-wasi");
}
"#,
)
.build();

// A generic host CC in the environment must not block the target-scoped
// variables (and must be passed through untouched).
let mut cmd = p.cargo_wasix("build");
cmd.env("CC", "host-cc-do-not-use");
let output = cmd.assert().success();
let stderr = String::from_utf8_lossy(&output.get_output().stderr);
assert!(
stderr.contains("CC_wasm32_wasmer_wasi is set to: wasixcc"),
"{stderr}"
);
assert!(
stderr.contains("CXX_wasm32_wasmer_wasi is set to: wasixcc++"),
"{stderr}"
);
assert!(
stderr.contains("AR_wasm32_wasmer_wasi is set to: wasixar"),
"{stderr}"
);
assert!(
stderr.contains("CC is set to: host-cc-do-not-use"),
"{stderr}"
);

// A user-set target-scoped variable wins over the wasixcc default.
let mut cmd = p.cargo_wasix("build");
cmd.env("CC_wasm32_wasmer_wasi", "my-custom-wasix-cc");
let output = cmd.assert().success();
let stderr = String::from_utf8_lossy(&output.get_output().stderr);
assert!(
stderr.contains("CC_wasm32_wasmer_wasi is set to: my-custom-wasix-cc"),
"{stderr}"
);

// The dashed spelling (checked first by `cc`) also counts as a user
// override: the underscored variant must not be set on top of it.
let mut cmd = p.cargo_wasix("build");
cmd.env("CC_wasm32-wasmer-wasi", "my-dashed-wasix-cc");
let output = cmd.assert().success();
let stderr = String::from_utf8_lossy(&output.get_output().stderr);
assert!(
stderr.contains("CC_wasm32_wasmer_wasi is not set"),
"{stderr}"
);

Ok(())
}

#[test]
fn wasixcc_pic_and_exceptions_for_dl_target() -> Result<()> {
// Test that WASIXCC_PIC and WASIXCC_WASM_EXCEPTIONS are set for -dl target
let p = support::project()
.file("src/main.rs", "fn main() {}")
.file(
"build.rs",
r#"
fn main() {
if let Ok(pic) = std::env::var("WASIXCC_PIC") {
println!("cargo:warning=WASIXCC_PIC is set to: {}", pic);
}
if let Ok(exceptions) = std::env::var("WASIXCC_WASM_EXCEPTIONS") {
println!("cargo:warning=WASIXCC_WASM_EXCEPTIONS is set to: {}", exceptions);
}
}
"#,
)
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = '1.0.0'

[package.metadata]
dl = true
"#,
)
.build();

let output = p.cargo_wasix("build").assert().success();
let stderr = String::from_utf8_lossy(&output.get_output().stderr);

// For -dl target, WASIXCC_PIC and WASIXCC_WASM_EXCEPTIONS should be set
assert!(
stderr.contains("WASIXCC_PIC is set to: 1"),
"Expected WASIXCC_PIC=1 for -dl target, stderr:\n{}",
stderr
);
assert!(
stderr.contains("WASIXCC_WASM_EXCEPTIONS is set to: 1"),
"Expected WASIXCC_WASM_EXCEPTIONS=1 for -dl target, stderr:\n{}",
stderr
);

Ok(())
}
Loading