Skip to content

Stop passing an invalid target to llvm-mingw's cross-compilation wrappers #1495

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
28 changes: 28 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3003,6 +3003,23 @@ impl Build {
};
}

// Under cross-compilation scenarios, llvm-mingw's clang executable is just a
// wrapper script that calls the actual clang binary with a suitable `--target`
// argument, much like the Android NDK case outlined above. Passing a target
// argument ourselves in this case will result in an error, as they expect
// targets like `x86_64-w64-mingw32`, and we can't always set such a target
// string because it is specific to this MinGW cross-compilation toolchain.
//
// For example, the following command will always fail due to using an unsuitable
// `--target` argument we'd otherwise pass:
// $ /opt/llvm-mingw-20250613-ucrt-ubuntu-22.04-x86_64/bin/x86_64-w64-mingw32-clang --target=x86_64-pc-windows-gnu dummy.c
//
// Code reference:
// https://github.com/mstorsjo/llvm-mingw/blob/a1f6413e5c21fd74b64137b56167f4fba500d1d8/wrappers/clang-target-wrapper.sh#L31
if !cfg!(windows) && target.os == "windows" && is_llvm_mingw_wrapper(&tool.path) {
tool.has_internal_target_arg = true;
}

// If we found `cl.exe` in our environment, the tool we're returning is
// an MSVC-like tool, *and* no env vars were set then set env vars for
// the tool that we're returning.
Expand Down Expand Up @@ -4171,6 +4188,17 @@ fn android_clang_compiler_uses_target_arg_internally(clang_path: &Path) -> bool
false
}

fn is_llvm_mingw_wrapper(clang_path: &Path) -> bool {
if let Some(filename) = clang_path
.file_name()
.and_then(|file_name| file_name.to_str())
Comment on lines +4191 to +4194
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a more robust way of testing?

I.e. checking version etc?

Copy link
Author

@AlexTMjugador AlexTMjugador Jul 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review! I agree that a more robust detection method could be nice.

After taking a closer look at the llvm-mingw repository, it appears that this cross-compilation toolchain has been setting a target since its very first tagged release, 20181017.

For Unix host platforms, the wrapper script has consistently been symlinked from x86_64-w64-mingw32-clang and similar names, so the toolchain has been quite consistent in this regard. As a result, checking its version doesn't seem necessary.

Interestingly, running x86_64-w64-mingw32-clang -v on any version of the toolchain reports the default target as x86_64-w64-windows-gnu. This doesn't exactly match either x86_64-pc-windows-gnu or x86_64-w64-mingw32, but it still seems to work just fine. (Why are LLVM's target names so inconsistent? 😅 I don't think I'd want to rely on the reported target for any important decisions on cc-rs...)

That said, aside from the target string oddities, this is a fairly standard LLVM toolchain with varying standard LLVM versions that aren't particularly distinct from stock LLVM. I don't think it's worth running any command with potentially brittle output or implement any extra complexity to determine whether to skip passing a target parameter in this case.

Instead, I figured we could check whether the compiler path is a symlink to a clang-target-wrapper.sh script. If and only if it is, we return true in this function. This approach should have a negligible chance of false positives, and in the unforeseen case that something goes wrong, users still have a fallback option other than fiddling with compiler flags: modifying the wrapper script directly.

What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm worried about something else pretending to be it...or the script gets renamed in the future

cc @madsmtm @ChrisDenton do you think there's a more robust way?

{
filename.ends_with("-w64-mingw32-clang") || filename.ends_with("-w64-mingw32-clang++")
} else {
false
}
}

// FIXME: Use parsed target.
fn autodetect_android_compiler(raw_target: &str, gnu: &str, clang: &str) -> String {
let new_clang_key = match raw_target {
Expand Down
Loading