Skip to content
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

Cannot cross-compile .a file from linux to windows #523

Open
jyn514 opened this issue Jun 27, 2020 · 19 comments
Open

Cannot cross-compile .a file from linux to windows #523

jyn514 opened this issue Jun 27, 2020 · 19 comments

Comments

@jyn514
Copy link
Member

jyn514 commented Jun 27, 2020

Given the following Cargo.toml:

[package]
name = "cc-example"
version = "0.1.0"

[build-dependencies]
cc = "1"

build.rs:

extern crate cc;

fn main() {
    cc::Build::new()
        .file("example.c")
        .compile("example.a");
}

and example.c:

int f() {
    return 0;
}

the cc crate will incorrectly try to use the Windows toolchain instead of the host toolchain.

Full output from cargo build
$ cargo build --target x86_64-pc-windows-msvc
   Compiling cc-example v0.1.0 (/home/joshua/src/rust/cc-example)
error: failed to run custom build command for `cc-example v0.1.0 (/home/joshua/src/rust/cc-example)`

Caused by:
  process didn't exit successfully: `/home/joshua/.local/lib/cargo/target/debug/build/cc-example-88984b21b33634e3/build-script-build` (exit code: 1)
--- stdout
TARGET = Some("x86_64-pc-windows-msvc")
OPT_LEVEL = Some("0")
HOST = Some("x86_64-unknown-linux-gnu")
CC_x86_64-pc-windows-msvc = None
CC_x86_64_pc_windows_msvc = None
TARGET_CC = None
CC = None
CROSS_COMPILE = None
CFLAGS_x86_64-pc-windows-msvc = None
CFLAGS_x86_64_pc_windows_msvc = None
TARGET_CFLAGS = None
CFLAGS = None
CRATE_CC_NO_DEFAULTS = None
DEBUG = Some("true")
CARGO_CFG_TARGET_FEATURE = Some("fxsr,sse,sse2")
running: "cc" "-O0" "-ffunction-sections" "-fdata-sections" "-g" "-fno-omit-frame-pointer" "-m64" "-Wall" "-Wextra" "-Fo/home/joshua/.local/lib/cargo/target/x86_64-pc-windows-msvc/debug/build/cc-example-183c547311261870/out/example.o" "-c" "example.c"
exit code: 0
AR_x86_64-pc-windows-msvc = None
AR_x86_64_pc_windows_msvc = None
TARGET_AR = None
AR = None
running: "lib.exe" "-out:/home/joshua/.local/lib/cargo/target/x86_64-pc-windows-msvc/debug/build/cc-example-183c547311261870/out/libexample.a.a" "-nologo" "/home/joshua/.local/lib/cargo/target/x86_64-pc-windows-msvc/debug/build/cc-example-183c547311261870/out/example.o"

--- stderr


error occurred: Failed to find tool. Is `lib.exe` installed?

Note that it correctly detects the host and target platforms, but still tries to use lib.exe instead of ld.

@alexcrichton
Copy link
Member

Thanks for the report, but you'll need to configure the toolchain if it's non-standard, such as in this case. Otherwise you'll need to arrange to have an appropriate toolchain installed.

@jyn514
Copy link
Member Author

jyn514 commented Jun 29, 2020

you'll need to configure the toolchain if it's non-standard

Could you give an example of what you mean? The crate docs say

This crate will automatically detect situations such as cross compilation or other environment variables set by Cargo and will build code appropriately.

So I was under the impression things like this would be detected automatically. I don't see anything on Build that would help except https://docs.rs/cc/1.0.56/cc/struct.Build.html#method.target, which says it's set automatically from TARGET.

@alexcrichton
Copy link
Member

This crate performs a best effort, but it can't figure out everything under the sun. More common than not the toolchain doesn't even exist on the system, but unfortunately this crate can't really figure that out.

@ChrisDenton
Copy link
Contributor

If anyone wants to work on this then PRs would be very welcome!

@dot-asm
Copy link
Contributor

dot-asm commented Nov 30, 2022

The problem description is misleading. Would cargo build --target=x86_64-pc-windows-msvc work on Linux with pure Rust code? Without involving cc-rs that is. No(*). Is it going to be fixed? ... Does it even make sense to do it? Either way, even if it does, cc-rs is not the right spot to start. Because as it stands now, no matter which hoops you jump through, it's going to fail even harder later on. If one wants to cross-compile on Linux for Windows, the answer is cargo build --target=x86_64-pc-windows-gnu [with mingw-w64, or equivalent, installed separately]. It's working and even supported.

(*) It might be possible to install and run MSVC compiler under wine, but then it would be more appropriate to run matching Rust installation there too.

@jyn514
Copy link
Member Author

jyn514 commented Nov 30, 2022

I don't mind if this doesn't work honestly, I just wish the error message were more helpful, rather than looking like a bug in the cc crate.

@ChrisDenton
Copy link
Contributor

ChrisDenton commented Dec 1, 2022

It's definitely possible to cross compile Rust to msvc targets using clang-cl. The only thing missing is the libraries which would need to be acquired and added. In fairness this is also true on Windows but in that case, once installed, cc-rs can autodetect them.

As far as cc-rs is concerned, cross compiling could be made to work in theory, assuming that clang-cl is in the environment (or a versioned equivalent such as clang-cl-14). And the logic in cc-rs would need to be updated to work with or without .exe extensions (depending on the host, e.g. llvm-lib vs. llvm-lib.exe).

@ChrisDenton
Copy link
Contributor

I think an immediate fix here would be, as jyn suggests, to provide a more helpful error when using a GNU tool for msvc targets because this is incompatible. This would still allow manual overrides (e.g. by setting TARGET_CC et al).

Attempting to automatically find and compile with clang-cl (or even clang proper, but that might be more work) could also be useful but is much more involved and may still requires some manual set up by the user. A more limited approach would be to correctly use llvm tools if clang-cl is set (aka Tool::MSVC { clang: true }) so that it's not necessary to e.g. set llvm-lib manually.

In any case, better diagnostics are good even if we try something more involved later.

@dot-asm
Copy link
Contributor

dot-asm commented Dec 1, 2022

It's definitely possible to cross compile Rust to msvc targets using clang-cl.

Well, while Linux clang-cl is indeed capable of generating PE-COFF .obj files, you lack headers, libraries and link.exe(*). In other words, you still can't do the actual useful stuff, like compiling arbitrary C(**) or link an executable binary. Be it just Rust or a mixture of [again, "pure"!] C and Rust.

(*) No, clang-cl does not do the linking, but relies on MSVC link.exe being available. Just like rustc.
(**) You're limited to so to say "pure" C, which effectively means "no references to CRT."

@ChrisDenton
Copy link
Contributor

llvm-link can be used instead of link.exe.

@dot-asm
Copy link
Contributor

dot-asm commented Dec 1, 2022

llvm-link can be used instead of link.exe.

llvm-link is a bitcode linker. It won't emit PE-COFF (or any other) executable.

@ChrisDenton
Copy link
Contributor

ChrisDenton commented Dec 1, 2022

Sorry, I mistyped that. lld-link is what I meant.

@dot-asm
Copy link
Contributor

dot-asm commented Dec 1, 2022

Oh! And so you mean that you'd be able to actually replace MSVC linker with it? Why doesn't clang-cl call it? BTW, Rust comes with lld-link. As far as I understand it's used with no-std targets, like embedded ones. Would Rust's lld-link up to the job?

@ChrisDenton
Copy link
Contributor

Rust's lld-link should indeed work. It's just a front-end to rust-lld which in turn is basically just LLVM's lld renamed. On Windows I have compiled code without msvc tools (though with the necessary libs). E.g.:

rustc -C linker="lld-link" main.rs

or

rustc -C linker="rust-lld" main.rs

@dot-asm
Copy link
Contributor

dot-asm commented Dec 1, 2022

Wow! Well, it's still only one out of three missing components on Linux, headers, libraries, link.exe. Yet I'd still argue that it makes less sense to make it, the cross-compilation in question, work. Because by doing so you would take up a maintenance burden of walking each potential user through an error-prone manual procedure.

Hmm, #758 could have picked it up with $CC set to clang-cl...

@ChrisDenton
Copy link
Contributor

Hm, adjusting that code to search for llvm-lib on non-windows hosts (instead of llvm-lib.exe) should work, I think. Or better yet, use std::env::consts::EXE_SUFFIX so it's always the right extension for whatever the host is.

@dot-asm
Copy link
Contributor

dot-asm commented Dec 1, 2022

The which() function does use EXE_SUFFIX, but omitting .exe on the line 2678 wouldn't be sufficient, because customarily there is no llvm-lib on Linux $PATH, but llvm-lib-N. One can for go for "another possibility" mentioned in the commentary. I.e. instead of replacing clang-cl with llvm-lib, one would replace it with clang, call it with --print-search-dirs and find llvm-lib there...

@ChrisDenton
Copy link
Contributor

My Ubuntu 22.04 install does have llvm-lib in both /usr/lib/llvm-14/bin and /usr/bin so it would work for at least some distros (llvm-lib-14 is only in /usr/bin though):

/usr/bin$ ls llvm-lib*
llvm-lib  llvm-lib-14  llvm-libtool-darwin-14

Granted though, a more thorough search would be nice.

@dot-asm
Copy link
Contributor

dot-asm commented Dec 1, 2022

Well, I for one don't have llvm-lib on the PATH. So one probably shouldn't count on it. But either way, the question about #758 not picking up llvm-lib was rather "academic." As it won't make the cross-compilation scenario in question maintainable. If one wants to cross-compile for Windows, mingw is the answer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants