diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ee2551ce5..974057f0d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,21 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - build: [stable, beta, nightly, linux32, macos, aarch64-macos, aarch64-ios, win32, win64, mingw32, mingw64, windows-2019] + build: + [ + stable, + beta, + nightly, + linux32, + macos, + aarch64-macos, + aarch64-ios, + win32, + win64, + mingw32, + mingw64, + windows-2019, + ] include: - build: stable os: ubuntu-latest @@ -72,29 +86,53 @@ jobs: os: windows-2019 rust: stable-x86_64 target: x86_64-pc-windows-msvc + - build: windows-clang + os: windows-2019 + rust: stable + target: x86_64-pc-windows-msvc + CC: clang + CXX: clang++ + - build: windows-clang-cl + os: windows-2019 + rust: stable + target: x86_64-pc-windows-msvc + CC: clang-cl + CXX: clang-cl steps: - - uses: actions/checkout@v4 - - name: Install Rust (rustup) - run: | - set -euxo pipefail - rustup toolchain install ${{ matrix.rust }} --no-self-update --profile minimal --target ${{ matrix.target }} - rustup default ${{ matrix.rust }} - shell: bash - - name: Install g++-multilib - run: | - set -e - # Remove the ubuntu-toolchain-r/test PPA, which is added by default. - # Some packages were removed, and this is causing the g++multilib - # install to fail. Similar issue: - # https://github.com/scikit-learn/scikit-learn/issues/13928. - sudo add-apt-repository --remove ppa:ubuntu-toolchain-r/test - sudo apt-get update - sudo apt-get install g++-multilib - if: matrix.build == 'linux32' - - uses: Swatinem/rust-cache@v2 - - run: cargo test ${{ matrix.no_run }} --workspace --target ${{ matrix.target }} - - run: cargo test ${{ matrix.no_run }} --workspace --target ${{ matrix.target }} --release - - run: cargo test ${{ matrix.no_run }} --workspace --target ${{ matrix.target }} --features parallel + - uses: actions/checkout@v4 + - name: Install Rust (rustup) + run: | + set -euxo pipefail + rustup toolchain install ${{ matrix.rust }} --no-self-update --profile minimal --target ${{ matrix.target }} + rustup default ${{ matrix.rust }} + shell: bash + - name: Install g++-multilib + run: | + set -e + # Remove the ubuntu-toolchain-r/test PPA, which is added by default. + # Some packages were removed, and this is causing the g++multilib + # install to fail. Similar issue: + # https://github.com/scikit-learn/scikit-learn/issues/13928. + sudo add-apt-repository --remove ppa:ubuntu-toolchain-r/test + sudo apt-get update + sudo apt-get install g++-multilib + if: matrix.build == 'linux32' + - name: add clang to path + if: startsWith(matrix.build, 'windows-clang') + run: | + echo "C:\msys64\mingw64\bin" >> "$GITHUB_PATH" + echo -e "AR=llvm-ar\nRUSTFLAGS=-Clinker=lld-link\nCC=${CC}\nCXX=${CXX}" >> "$GITHUB_ENV" + shell: bash + env: + CC: ${{ matrix.CC }} + CXX: ${{ matrix.CXX }} + - name: setup dev environment + uses: ilammy/msvc-dev-cmd@v1 + if: startsWith(matrix.build, 'windows-clang') + - uses: Swatinem/rust-cache@v2 + - run: cargo test ${{ matrix.no_run }} --workspace --target ${{ matrix.target }} + - run: cargo test ${{ matrix.no_run }} --workspace --target ${{ matrix.target }} --release + - run: cargo test ${{ matrix.no_run }} --workspace --target ${{ matrix.target }} --features parallel # This is separate from the matrix above because there is no prebuilt rust-std component for these targets. check-tvos: @@ -120,37 +158,37 @@ jobs: target: x86_64-apple-tvos no_run: --no-run steps: - - uses: actions/checkout@v4 - - name: Install Rust (rustup) - run: | - set -euxo pipefail - rustup toolchain install ${{ matrix.rust }} --no-self-update --profile minimal - rustup component add rust-src --toolchain ${{ matrix.rust }} - rustup default ${{ matrix.rust }} - shell: bash - - uses: Swatinem/rust-cache@v2 - - run: cargo test -Z build-std=std ${{ matrix.no_run }} --workspace --target ${{ matrix.target }} - - run: cargo test -Z build-std=std ${{ matrix.no_run }} --workspace --target ${{ matrix.target }} --release - - run: cargo test -Z build-std=std ${{ matrix.no_run }} --workspace --target ${{ matrix.target }} --features parallel + - uses: actions/checkout@v4 + - name: Install Rust (rustup) + run: | + set -euxo pipefail + rustup toolchain install ${{ matrix.rust }} --no-self-update --profile minimal + rustup component add rust-src --toolchain ${{ matrix.rust }} + rustup default ${{ matrix.rust }} + shell: bash + - uses: Swatinem/rust-cache@v2 + - run: cargo test -Z build-std=std ${{ matrix.no_run }} --workspace --target ${{ matrix.target }} + - run: cargo test -Z build-std=std ${{ matrix.no_run }} --workspace --target ${{ matrix.target }} --release + - run: cargo test -Z build-std=std ${{ matrix.no_run }} --workspace --target ${{ matrix.target }} --features parallel cuda: name: Test CUDA support runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v4 - - name: Install cuda-minimal-build-11-8 - shell: bash - run: | - # https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=x86_64&Distribution=Ubuntu&target_version=20.04&target_type=deb_network - wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-keyring_1.0-1_all.deb - sudo dpkg -i cuda-keyring_1.0-1_all.deb - sudo apt-get update - sudo apt-get -y install cuda-minimal-build-11-8 - - uses: Swatinem/rust-cache@v2 - - name: Test 'cudart' feature - shell: bash - run: | - PATH="/usr/local/cuda/bin:$PATH" cargo test --manifest-path dev-tools/cc-test/Cargo.toml --features test_cuda + - uses: actions/checkout@v4 + - name: Install cuda-minimal-build-11-8 + shell: bash + run: | + # https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=x86_64&Distribution=Ubuntu&target_version=20.04&target_type=deb_network + wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-keyring_1.0-1_all.deb + sudo dpkg -i cuda-keyring_1.0-1_all.deb + sudo apt-get update + sudo apt-get -y install cuda-minimal-build-11-8 + - uses: Swatinem/rust-cache@v2 + - name: Test 'cudart' feature + shell: bash + run: | + PATH="/usr/local/cuda/bin:$PATH" cargo test --manifest-path dev-tools/cc-test/Cargo.toml --features test_cuda msrv: name: MSRV @@ -160,42 +198,42 @@ jobs: matrix: os: [ubuntu-latest, windows-latest] steps: - - uses: actions/checkout@v4 - - name: Install Rust - run: | - rustup toolchain install 1.53.0 --no-self-update --profile minimal - rustup toolchain install nightly --no-self-update --profile minimal - rustup default 1.53.0 - shell: bash - - name: Create Cargo.lock with minimal version - run: cargo +nightly update -Zminimal-versions - - name: Cache downloaded crates since 1.53 is really slow in fetching - uses: Swatinem/rust-cache@v2 - - run: cargo check --lib -p cc --locked - - run: cargo check --lib -p cc --locked --all-features + - uses: actions/checkout@v4 + - name: Install Rust + run: | + rustup toolchain install 1.53.0 --no-self-update --profile minimal + rustup toolchain install nightly --no-self-update --profile minimal + rustup default 1.53.0 + shell: bash + - name: Create Cargo.lock with minimal version + run: cargo +nightly update -Zminimal-versions + - name: Cache downloaded crates since 1.53 is really slow in fetching + uses: Swatinem/rust-cache@v2 + - run: cargo check --lib -p cc --locked + - run: cargo check --lib -p cc --locked --all-features clippy: name: Clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Install Rust - run: | - rustup toolchain install stable --no-self-update --profile minimal --component rustfmt - rustup default stable - shell: bash - - uses: Swatinem/rust-cache@v2 - - run: cargo clippy + - uses: actions/checkout@v4 + - name: Install Rust + run: | + rustup toolchain install stable --no-self-update --profile minimal --component rustfmt + rustup default stable + shell: bash + - uses: Swatinem/rust-cache@v2 + - run: cargo clippy rustfmt: name: Rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Install Rust - run: | - rustup toolchain install stable --no-self-update --profile minimal --component rustfmt - rustup default stable - shell: bash - - uses: Swatinem/rust-cache@v2 - - run: cargo fmt -- --check + - uses: actions/checkout@v4 + - name: Install Rust + run: | + rustup toolchain install stable --no-self-update --profile minimal --component rustfmt + rustup default stable + shell: bash + - uses: Swatinem/rust-cache@v2 + - run: cargo fmt -- --check diff --git a/dev-tools/cc-test/build.rs b/dev-tools/cc-test/build.rs index 7ebeeab22..f26cbbb68 100644 --- a/dev-tools/cc-test/build.rs +++ b/dev-tools/cc-test/build.rs @@ -30,7 +30,8 @@ fn main() { run_forked_capture_output(&out, "warnings-on"); } - cc::Build::new() + let mut build = cc::Build::new(); + build .file("src/foo.c") .flag_if_supported("-Wall") .flag_if_supported("-Wfoo-bar-this-flag-does-not-exist") @@ -38,6 +39,8 @@ fn main() { .define("BAR", "1") .compile("foo"); + let compiler = build.get_compiler(); + cc::Build::new() .file("src/bar1.c") .file("src/bar2.c") @@ -84,6 +87,14 @@ fn main() { // nmake which runs vanilla cl, and then also test it after we remove all // the relevant env vars from our own process. if target.contains("msvc") { + let cc_frontend = if compiler.is_like_msvc() { + "MSVC" + } else if compiler.is_like_clang() { + "CLANG" + } else { + unimplemented!("Unknown compiler that targets msvc but isn't clang-like or msvc-like") + }; + let out = out.join("tmp"); fs::create_dir(&out).unwrap(); println!("nmake 1"); @@ -92,6 +103,7 @@ fn main() { .env_remove("MAKEFLAGS") .arg("/fsrc/NMakefile") .env("OUT_DIR", &out) + .env("CC_FRONTEND", cc_frontend) .status() .unwrap(); assert!(status.success()); @@ -99,7 +111,10 @@ fn main() { fs::remove_dir_all(&out).unwrap(); fs::create_dir(&out).unwrap(); - env::remove_var("PATH"); + // windows registry won't find clang in path + if !compiler.path().to_string_lossy().starts_with("clang") { + env::remove_var("PATH"); + } env::remove_var("VCINSTALLDIR"); env::remove_var("INCLUDE"); env::remove_var("LIB"); @@ -109,6 +124,7 @@ fn main() { .env_remove("MAKEFLAGS") .arg("/fsrc/NMakefile") .env("OUT_DIR", &out) + .env("CC_FRONTEND", cc_frontend) .status() .unwrap(); assert!(status.success()); diff --git a/dev-tools/cc-test/src/NMakefile b/dev-tools/cc-test/src/NMakefile index 03c73dff0..e310a4f36 100644 --- a/dev-tools/cc-test/src/NMakefile +++ b/dev-tools/cc-test/src/NMakefile @@ -1,14 +1,20 @@ all: $(OUT_DIR)/msvc.lib $(OUT_DIR)/msvc.exe +!IF "$(CC_FRONTEND)" == "MSVC" +EXTRA_CFLAGS=-nologo +CFLAG_OUTPUT=-Fo +!ELSE +CFLAG_OUTPUT=-o +!ENDIF + $(OUT_DIR)/msvc.lib: $(OUT_DIR)/msvc.o lib -nologo -out:$(OUT_DIR)/msvc.lib $(OUT_DIR)/msvc.o - rc -h $(OUT_DIR)/msvc.o: src/msvc.c - $(CC) -nologo -c -Fo:$@ src/msvc.c -MD + $(CC) $(EXTRA_CFLAGS) -c $(CFLAG_OUTPUT)$@ src/msvc.c -MD $(OUT_DIR)/msvc.exe: $(OUT_DIR)/msvc2.o - $(CC) -nologo -Fo:$@ $(OUT_DIR)/msvc2.o + $(CC) $(EXTRA_CFLAGS) $(CFLAG_OUTPUT)$@ $(OUT_DIR)/msvc2.o $(OUT_DIR)/msvc2.o: src/msvc.c - $(CC) -nologo -c -Fo:$@ src/msvc.c -DMAIN -MD + $(CC) $(EXTRA_CFLAGS) -c $(CFLAG_OUTPUT)$@ src/msvc.c -DMAIN -MD diff --git a/src/command_helpers.rs b/src/command_helpers.rs index 10dafeb1c..fe919a523 100644 --- a/src/command_helpers.rs +++ b/src/command_helpers.rs @@ -385,17 +385,20 @@ for help)" } } -pub(crate) fn command_add_output_file( - cmd: &mut Command, - dst: &Path, - cuda: bool, - msvc: bool, - clang: bool, - gnu: bool, - is_asm: bool, - is_arm: bool, -) { - if msvc && !clang && !gnu && !cuda && !(is_asm && is_arm) { +pub(crate) struct CmdAddOutputFileArgs { + pub(crate) cuda: bool, + pub(crate) is_assembler_msvc: bool, + pub(crate) msvc: bool, + pub(crate) clang: bool, + pub(crate) gnu: bool, + pub(crate) is_asm: bool, + pub(crate) is_arm: bool, +} + +pub(crate) fn command_add_output_file(cmd: &mut Command, dst: &Path, args: CmdAddOutputFileArgs) { + if args.is_assembler_msvc + || (args.msvc && !args.clang && !args.gnu && !args.cuda && !(args.is_asm && args.is_arm)) + { let mut s = OsString::from("-Fo"); s.push(dst); cmd.arg(s); diff --git a/src/detect_compiler_family.c b/src/detect_compiler_family.c index 290d2fc72..e66dcc044 100644 --- a/src/detect_compiler_family.c +++ b/src/detect_compiler_family.c @@ -5,7 +5,3 @@ #ifdef __GNUC__ #pragma message "gcc" #endif - -#ifdef _MSC_VER -#pragma message "msvc" -#endif diff --git a/src/lib.rs b/src/lib.rs index b37105196..e0318765c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -637,12 +637,15 @@ impl Build { command_add_output_file( &mut cmd, &obj, - self.cuda, - target.contains("msvc"), - clang, - gnu, - false, - is_arm, + CmdAddOutputFileArgs { + cuda: self.cuda, + is_assembler_msvc: false, + msvc: compiler.is_like_msvc(), + clang, + gnu, + is_asm: false, + is_arm, + }, ); // Checking for compiler flags does not require linking @@ -1603,7 +1606,17 @@ impl Build { }; let is_arm = target.contains("aarch64") || target.contains("arm"); command_add_output_file( - &mut cmd, &obj.dst, self.cuda, msvc, clang, gnu, is_asm, is_arm, + &mut cmd, + &obj.dst, + CmdAddOutputFileArgs { + cuda: self.cuda, + is_assembler_msvc, + msvc: compiler.is_like_msvc(), + clang, + gnu, + is_asm, + is_arm, + }, ); // armasm and armasm64 don't requrie -c option if !is_assembler_msvc || !is_arm { @@ -2435,8 +2448,8 @@ impl Build { fn assemble_progressive(&self, dst: &Path, objs: &[&Path]) -> Result<(), Error> { let target = self.get_target()?; - if target.contains("msvc") { - let (mut cmd, program, any_flags) = self.get_ar()?; + let (mut cmd, program, any_flags) = self.get_ar()?; + if target.contains("msvc") && !program.to_string_lossy().contains("llvm-") { // NOTE: -out: here is an I/O flag, and so must be included even if $ARFLAGS/ar_flag is // in use. -nologo on the other hand is just a regular flag, and one that we'll skip if // the caller has explicitly dictated the flags they want. See @@ -2455,8 +2468,6 @@ impl Build { cmd.args(objs); run(&mut cmd, &program, &self.cargo_output)?; } else { - let (mut ar, cmd, _any_flags) = self.get_ar()?; - // Set an environment variable to tell the OSX archiver to ensure // that all dates listed in the archive are zero, improving // determinism of builds. AFAIK there's not really official @@ -2479,12 +2490,16 @@ impl Build { // // In any case if this doesn't end up getting read, it shouldn't // cause that many issues! - ar.env("ZERO_AR_DATE", "1"); + cmd.env("ZERO_AR_DATE", "1"); // NOTE: We add cq here regardless of whether $ARFLAGS/ar_flag have been used because // it dictates the _mode_ ar runs in, which the setter of $ARFLAGS/ar_flag can't // dictate. See https://github.com/rust-lang/cc-rs/pull/763 for further discussion. - run(ar.arg("cq").arg(dst).args(objs), &cmd, &self.cargo_output)?; + run( + cmd.arg("cq").arg(dst).args(objs), + &program, + &self.cargo_output, + )?; } Ok(()) @@ -3124,12 +3139,17 @@ impl Build { Ok(self.get_base_archiver_variant("RANLIB", "ranlib")?.0) } - fn get_base_archiver_variant(&self, env: &str, tool: &str) -> Result<(Command, String), Error> { + fn get_base_archiver_variant( + &self, + env: &str, + tool: &str, + ) -> Result<(Command, PathBuf), Error> { let target = self.get_target()?; - let mut name = String::new(); + let mut name = PathBuf::new(); let tool_opt: Option = self .env_tool(env) .map(|(tool, _wrapper, args)| { + name = tool.clone(); let mut cmd = self.cmd(tool); cmd.args(args); cmd @@ -3139,11 +3159,11 @@ impl Build { // Windows use bat files so we have to be a bit more specific if cfg!(windows) { let mut cmd = self.cmd("cmd"); - name = format!("em{}.bat", tool); + name = format!("em{}.bat", tool).into(); cmd.arg("/c").arg(&name); Some(cmd) } else { - name = format!("em{}", tool); + name = format!("em{}", tool).into(); Some(self.cmd(&name)) } } else if target.starts_with("wasm32") { @@ -3154,7 +3174,7 @@ impl Build { // of "llvm-ar"... let compiler = self.get_base_compiler().ok()?; if compiler.is_like_clang() { - name = format!("llvm-{}", tool); + name = format!("llvm-{}", tool).into(); search_programs(&mut self.cmd(&compiler.path), &name, &self.cargo_output) .map(|name| self.cmd(name)) } else { @@ -3170,10 +3190,10 @@ impl Build { Some(t) => t, None => { if target.contains("android") { - name = format!("llvm-{}", tool); + name = format!("llvm-{}", tool).into(); match Command::new(&name).arg("--version").status() { Ok(status) if status.success() => (), - _ => name = format!("{}-{}", target.replace("armv7", "arm"), tool), + _ => name = format!("{}-{}", target.replace("armv7", "arm"), tool).into(), } self.cmd(&name) } else if target.contains("msvc") { @@ -3200,7 +3220,7 @@ impl Build { } if lib.is_empty() { - name = String::from("lib.exe"); + name = PathBuf::from("lib.exe"); let mut cmd = match windows_registry::find(&target, "lib.exe") { Some(t) => t, None => self.cmd("lib.exe"), @@ -3210,7 +3230,7 @@ impl Build { } cmd } else { - name = lib; + name = lib.into(); self.cmd(&name) } } else if target.contains("illumos") { @@ -3218,7 +3238,7 @@ impl Build { // but the OS comes bundled with a GNU-compatible variant. // // Use the GNU-variant to match other Unix systems. - name = format!("g{}", tool); + name = format!("g{}", tool).into(); self.cmd(&name) } else if self.get_host()? != target { match self.prefix_for_target(&target) { @@ -3238,16 +3258,16 @@ impl Build { break; } } - name = chosen; + name = chosen.into(); self.cmd(&name) } None => { - name = default; + name = default.into(); self.cmd(&name) } } } else { - name = default; + name = default.into(); self.cmd(&name) } } @@ -3939,7 +3959,7 @@ fn which(tool: &Path, path_entries: Option) -> Option { } // search for |prog| on 'programs' path in '|cc| -print-search-dirs' output -fn search_programs(cc: &mut Command, prog: &str, cargo_output: &CargoOutput) -> Option { +fn search_programs(cc: &mut Command, prog: &Path, cargo_output: &CargoOutput) -> Option { let search_dirs = run_output( cc.arg("-print-search-dirs"), "cc", @@ -3952,7 +3972,7 @@ fn search_programs(cc: &mut Command, prog: &str, cargo_output: &CargoOutput) -> let search_dirs = std::str::from_utf8(&search_dirs).ok()?; for dirs in search_dirs.split(|c| c == '\r' || c == '\n') { if let Some(path) = dirs.strip_prefix("programs: =") { - return which(Path::new(prog), Some(OsString::from(path))); + return which(prog, Some(OsString::from(path))); } } None diff --git a/src/tool.rs b/src/tool.rs index 7a2ad4da1..850065800 100644 --- a/src/tool.rs +++ b/src/tool.rs @@ -5,12 +5,13 @@ use std::{ ffi::{OsStr, OsString}, io::Write, path::{Path, PathBuf}, - process::Command, + process::{Command, Stdio}, sync::Mutex, }; use crate::{ command_helpers::{run_output, CargoOutput}, + run, tempfile::NamedTempfile, Error, ErrorKind, }; @@ -135,11 +136,20 @@ impl Tool { cargo_output.print_debug(&stdout); + // https://gitlab.kitware.com/cmake/cmake/-/blob/69a2eeb9dff5b60f2f1e5b425002a0fd45b7cadb/Modules/CMakeDetermineCompilerId.cmake#L267-271 + let accepts_cl_style_flags = + run(Command::new(path).arg("-?").stdout(Stdio::null()), path, &{ + // the errors are not errors! + let mut cargo_output = cargo_output.clone(); + cargo_output.warnings = cargo_output.debug; + cargo_output + }) + .is_ok(); + let clang = stdout.contains(r#""clang""#); - let msvc = stdout.contains(r#""msvc""#); let gcc = stdout.contains(r#""gcc""#); - match (clang, msvc, gcc) { + match (clang, accepts_cl_style_flags, gcc) { (clang_cl, true, _) => Ok(ToolFamily::Msvc { clang_cl }), (true, false, _) => Ok(ToolFamily::Clang { zig_cc: is_zig_cc(path, cargo_output), diff --git a/tests/support/mod.rs b/tests/support/mod.rs index a17fe43f4..5b9f3b5c0 100644 --- a/tests/support/mod.rs +++ b/tests/support/mod.rs @@ -34,6 +34,11 @@ impl Test { // lesser of the two evils. env::remove_var("RUSTC_WRAPPER"); + // cc-rs prefers these env vars to the wrappers. We set these in some tests, so unset them so the wrappers get used + env::remove_var("CC"); + env::remove_var("CXX"); + env::remove_var("AR"); + let mut gcc = env::current_exe().unwrap(); gcc.pop(); if gcc.ends_with("deps") {