从Fedora Linux主机到Windows目标的交叉编译Rust找不到依赖项

时间:2019-12-18 04:09:20

标签: linux windows rust cross-compiling

我运行x86_64 Fedora Linux 31主机,并希望编译一些最简单的rust代码:

if ($_GET['PollID'] == 2) {
    $votePercent = "Have a good day!";
} else {
    $votePercent = "Have a good night!";
}

它可以从主机自然地编译到该主机,但是当我尝试将其交叉编译到i686或x86_64 Windows时,它将无法编译。

我阅读了stackoverflow(例如here),并在Internet上搜索了类似的问题,以及如何解决这个问题,但没有任何利润。

我已经安装了所有必需的目标:

fn main() {
    println!("Hello, world!");
}

我已经将mingw32像mingw64一样安装了,这个小的测试C ++代码已经被mingw32编译为mingw64了,而且没有错误:

[pfemidi@pfemidi hello_cargo]$ rustup show
Default host: x86_64-unknown-linux-gnu
rustup home:  /home/pfemidi/.rustup

installed targets for active toolchain
--------------------------------------

i686-pc-windows-gnu
i686-unknown-linux-gnu
i686-unknown-linux-musl
x86_64-pc-windows-gnu
x86_64-unknown-linux-gnu
x86_64-unknown-linux-musl

active toolchain
----------------

stable-x86_64-unknown-linux-gnu (default)
rustc 1.39.0 (4560ea788 2019-11-04)

[pfemidi@pfemidi hello_cargo]$

我已将Windows特定的mingw链接器和ar添加到我的.cargo / config:

#include <iomanip>
#include <iostream>

int main() {
    std::cout << "Hello, world!" << std::endl;
    return 0;
}

但是当我跑步时

[target.i686-pc-windows-gnu]
linker = "i686-w64-mingw32-gcc"
ar = "i686-w64-mingw32-ar"

[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"
ar = "x86_64-w64-mingw32-ar"

cargo build --release --target i686-pc-windows-gnu --verbose

都抱怨“找不到-lpthread”:

cargo build --release --target x86_64-pc-windows-gnu --verbose

为什么?我还应该怎么做才能满足一切?

2 个答案:

答案 0 :(得分:2)

经过一番思考,我能够解决问题,无需使用cross和类似工具,而只需在货物中指定特定目标,即可将Rust中的项目从Linux主机交叉编译到Windows目标。当然,测试和运行项目将会失败(如果没有安装任何类似wine的仿真器),但是仅创建Windows目标就可以顺利进行。 BTW Rust最初是作为交叉编译器开发的,因此,它具有完整的工作项目并通过了Linux中的所有测试,几乎可以肯定,在Windows中对该项目进行简单的交叉编译不会以任何方式破坏它。

自从我运行Fedora Linux 31以来,以下所有内容均适用于此发行版。但是我认为其他Linux发行版之间没有显着差异。而且我只考虑pc-windows-gnu Rust目标,pc-windows-msvc目标与我无关。

首先,确保已安装所有必需的用于交叉编译的MinGW软件包:

[pfemidi@pfemidi ~]$ rpm -qa | grep mingw | sort
mingw32-binutils-2.32-6.fc31.x86_64
mingw32-cpp-9.2.1-1.fc31.x86_64
mingw32-crt-6.0.0-2.fc31.noarch
mingw32-filesystem-110-1.fc31.noarch
mingw32-gcc-9.2.1-1.fc31.x86_64
mingw32-headers-6.0.0-2.fc31.noarch
mingw32-winpthreads-6.0.0-2.fc31.noarch
mingw32-winpthreads-static-6.0.0-2.fc31.noarch
mingw64-binutils-2.32-6.fc31.x86_64
mingw64-cpp-9.2.1-1.fc31.x86_64
mingw64-crt-6.0.0-2.fc31.noarch
mingw64-filesystem-110-1.fc31.noarch
mingw64-gcc-9.2.1-1.fc31.x86_64
mingw64-headers-6.0.0-2.fc31.noarch
mingw64-winpthreads-6.0.0-2.fc31.noarch
mingw64-winpthreads-static-6.0.0-2.fc31.noarch
mingw-binutils-generic-2.32-6.fc31.x86_64
mingw-filesystem-base-110-1.fc31.noarch
[pfemidi@pfemidi ~]$

让我们用Rust语言创建最简单的项目:

[pfemidi@pfemidi rust]$ cargo new foobar
     Created binary (application) `foobar` package
[pfemidi@pfemidi rust]$ cat foobar/src/main.rs 
fn main() {
    println!("Hello, world!");
}
[pfemidi@pfemidi rust]$

在项目目录中使用配置文件创建.cargo目录,其中包含以下内容:

[pfemidi@pfemidi rust]$ cd foobar
[pfemidi@pfemidi foobar]$ mkdir .cargo
[pfemidi@pfemidi foobar]$ cat > .cargo/config
[target.i686-pc-windows-gnu]
linker = "i686-w64-mingw32-gcc"
ar = "i686-w64-mingw32-ar"

[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"
ar = "x86_64-w64-mingw32-ar"
[pfemidi@pfemidi foobar]$

此步骤是必需的,以便在构建Windows目标时,Rust不使用安装的默认gcc中的链接器,而是MinGW链接器。

现在尝试将项目构建为i686-pc-windows-gnu目标:

[pfemidi@pfemidi foobar]$ cargo build --target i686-pc-windows-gnu
   Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
error: linking with `i686-w64-mingw32-gcc` failed: exit code: 1
  |
  = note: "i686-w64-mingw32-gcc" "-Wl,--enable-long-section-names" "-fno-use-linker-plugin" "-Wl,--nxcompat" "-nostdlib"

...

  = note: /usr/lib/gcc/i686-w64-mingw32/9.2.1/../../../../i686-w64-mingw32/bin/ld: /home/pfemidi/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/crt2.o:crtexe.c:(.text+0x75): undefined reference to `__onexitend'
          /usr/lib/gcc/i686-w64-mingw32/9.2.1/../../../../i686-w64-mingw32/bin/ld: /home/pfemidi/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/crt2.o:crtexe.c:(.text+0x7a): undefined reference to `__onexitbegin'
          collect2: error: ld returned 1 exit status


error: aborting due to previous error

error: could not compile `foobar`.

To learn more, run the command again with --verbose.
[pfemidi@pfemidi foobar]$

错误!在crt2.o文件中获得了“对__onexitend的未定义引用”和“对__onexitbegin的未定义引用”。事实是pc-windows-gnu目标的Rust组件是用MinGW 6.3.0构建的,但Fedora Linux 31中存在MinGW版本9.2.1。由于编译器版本不匹配,因此CRT有所不同。好的,让我们将crt2.o从Fedora Linux MinGW存储库复制到i686-pc-windows-gnu组件的Rust目录中。与crt2.o一起,我们还复制了dllcrt2.o,它是动态库的入口点,因为crt2.o是独立的可执行文件。以防万一,我们保存原始文件:

[pfemidi@pfemidi foobar]$ cd ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/
[pfemidi@pfemidi lib]$ mv crt2.o crt2.o.ori
[pfemidi@pfemidi lib]$ mv dllcrt2.o dllcrt2.o.ori
[pfemidi@pfemidi lib]$ cp /usr/i686-w64-mingw32/sys-root/mingw/lib/crt2.o .
[pfemidi@pfemidi lib]$ cp /usr/i686-w64-mingw32/sys-root/mingw/lib/dllcrt2.o .
[pfemidi@pfemidi lib]$ cd -
/home/pfemidi/mywork/rust/foobar
[pfemidi@pfemidi foobar]$ 

现在尝试再次将项目构建为i686-pc-windows-gnu目标:

[pfemidi@pfemidi foobar]$ cargo build --target i686-pc-windows-gnu
   Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
    Finished dev [unoptimized + debuginfo] target(s) in 0.19s
[pfemidi@pfemidi foobar]$ 

成功了!

现在尝试对x86_64-pc-windows-gnu目标执行相同的操作:

[pfemidi@pfemidi foobar]$ cargo build --target x86_64-pc-windows-gnu
   Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
error: linking with `x86_64-w64-mingw32-gcc` failed: exit code: 1
  |
  = note: "x86_64-w64-mingw32-gcc" "-Wl,--enable-long-section-names" "-fno-use-linker-plugin" "-Wl,--nxcompat" "-nostdlib" "-m64"

...

  = note: /usr/lib/gcc/x86_64-w64-mingw32/9.2.1/../../../../x86_64-w64-mingw32/bin/ld: /home/pfemidi/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/crt2.o:crtexe.c:(.rdata$.refptr.__onexitbegin[.refptr.__onexitbegin]+0x0): undefined reference to `__onexitbegin'
          /usr/lib/gcc/x86_64-w64-mingw32/9.2.1/../../../../x86_64-w64-mingw32/bin/ld: /home/pfemidi/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/crt2.o:crtexe.c:(.rdata$.refptr.__onexitend[.refptr.__onexitend]+0x0): undefined reference to `__onexitend'
          collect2: error: ld returned 1 exit status


error: aborting due to previous error

error: could not compile `foobar`.

To learn more, run the command again with --verbose.
[pfemidi@pfemidi foobar]$ 

该错误与之前提到的i686-pc-windows-gnu目标完全相同。与上面的32位目标类似,让我们复制文件crt2.o和dllcrt2.o:

[pfemidi@pfemidi foobar]$ cd ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/
[pfemidi@pfemidi lib]$ mv crt2.o crt2.o.ori
[pfemidi@pfemidi lib]$ mv dllcrt2.o dllcrt2.o.ori
[pfemidi@pfemidi lib]$ cp /usr/x86_64-w64-mingw32/sys-root/mingw/lib/crt2.o .
[pfemidi@pfemidi lib]$ cp /usr/x86_64-w64-mingw32/sys-root/mingw/lib/dllcrt2.o .
[pfemidi@pfemidi lib]$ cd -
/home/pfemidi/mywork/rust/foobar
[pfemidi@pfemidi foobar]$ 

并尝试再次将项目构建为x86_64-pc-windows-gnu目标:

[pfemidi@pfemidi foobar]$ cargo build --target x86_64-pc-windows-gnu
   Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
    Finished dev [unoptimized + debuginfo] target(s) in 0.20s
[pfemidi@pfemidi foobar]$ 

是的!它也起作用!因此,现在我们可以使用Rust分发中包含的标准商品轻松地为Windows建立目标!对于x86_64目标,这是完全正确的。但是对于x86目标,只有在我们在项目中使用任何出现紧急情况的功能(例如宏紧急情况,期望功能等)之后,一切都会好起来的。

让我们的简单项目陷入恐慌:​​

[pfemidi@pfemidi foobar]$ cat src/main.rs 
fn main() {
    println!("Hello, world!");
    panic!("I'm panicked!");    // <-- here it is
}
[pfemidi@pfemidi foobar]$

将其构建为x86_64目标:

[pfemidi@pfemidi foobar]$ cargo build --target x86_64-pc-windows-gnu
   Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
    Finished dev [unoptimized + debuginfo] target(s) in 0.16s
[pfemidi@pfemidi foobar]$ 

它运行得非常好(我检查的效果也一样)。现在尝试对x86目标执行相同操作:

[pfemidi@pfemidi foobar]$ cargo build --target i686-pc-windows-gnu
   Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
error: linking with `i686-w64-mingw32-gcc` failed: exit code: 1
  |
  = note: "i686-w64-mingw32-gcc" "-Wl,--enable-long-section-names" "-fno-use-linker-plugin" "-Wl,--nxcompat" "-nostdlib"

...

  = note: /usr/lib/gcc/i686-w64-mingw32/9.2.1/../../../../i686-w64-mingw32/bin/ld: /home/pfemidi/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/libpanic_unwind-0c029c00da54fbf5.rlib(panic_unwind-0c029c00da54fbf5.panic_unwind.2hgzd7yq-cgu.0.rcgu.o): in function `ZN12panic_unwind3imp5panic17h03027a0e504502cdE':
          /rustc/73528e339aae0f17a15ffa49a8ac608f50c6cf14\/src\libpanic_unwind/gcc.rs:73: undefined reference to `_Unwind_RaiseException'
          /usr/lib/gcc/i686-w64-mingw32/9.2.1/../../../../i686-w64-mingw32/bin/ld: /home/pfemidi/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/libpanic_unwind-0c029c00da54fbf5.rlib(panic_unwind-0c029c00da54fbf5.panic_unwind.2hgzd7yq-cgu.0.rcgu.o): in function `rust_eh_unwind_resume':
          /rustc/73528e339aae0f17a15ffa49a8ac608f50c6cf14\/src\libpanic_unwind/gcc.rs:327: undefined reference to `_Unwind_Resume'
          collect2: error: ld returned 1 exit status


error: aborting due to previous error

error: could not compile `foobar`.

To learn more, run the command again with --verbose.
[pfemidi@pfemidi foobar]$

糟糕!在Rust标准库的libpanic_unwind模块中,我们获得了“对_Unwind_RaiseException的未定义引用和对_Unwind_Resume的未定义引用”。但是,这个问题也得到了解决,尽管不像上一个问题那样简单,后者仅是替换CRT crt2.o和dllcrt2.o文件。

对于堆栈展开,Rust对32位Windows目标使用dwarf方法,对64位Windows目标使用seh方法,而标准Fedora Linux发行版中的MinGW对32位Windows目标使用sjlj方法,对64位Windows目标使用seh方法。差异here)。因此,可以通过MinGW链接器链接64位目标,没有任何问题,但是对于32位目标,则没有必要的符号和目标文件可以正确链接。要获取这些文件和符号,必须为32位Windows目标默认情况下使用矮支持而不是sjlj支持重建MinGW。

在这里我将不讨论如何重建MinGW的细节,我只会说一件事:用矮栈堆栈展开而不是sjlj方法重建MinGW之后,您只需要从MinGW中选择一个名为libgcc_eh.a的文件。刚建立并将其放在Rust i686-pc-windows-gnu目标的库目录中。完成此操作后,不仅会为64位Windows目标而且会为32位目标产生任何具有紧急功能的项目,而不会出现错误:

[pfemidi@pfemidi foobar]$ cd ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/
[pfemidi@pfemidi lib]$ cp ~/rpmbuild/BUILD/gcc-9.2.1-20190827/build_win32/i686-w64-mingw32/libgcc/libgcc_eh.a .
[pfemidi@pfemidi lib]$ cd -
/home/pfemidi/mywork/rust/foobar
[pfemidi@pfemidi foobar]$ cargo build --target i686-pc-windows-gnu
   Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
    Finished dev [unoptimized + debuginfo] target(s) in 0.30s
[pfemidi@pfemidi foobar]$ 

开心的生锈! :-)

答案 1 :(得分:1)

听起来似乎交叉编译并不总是那么容易。我建议您不要发明自己的轮子,而应使用经过测试的解决方案。

当前进行交叉编译的“标准”方法是使用cross,它是一种货物包装程序,可让您摆脱交叉编译的细节。

在交叉安装之前,您应该先安装dependencies。该如何做完全取决于您的分布。

依赖关系就绪后,您应该像这样安装cross(假设您有cargo)。

$ cargo install cross

在此之后,构建就可以神奇地完成

cross build --target x86_64-pc-windows-gnu