The Rust RFC Book
- Feature Name: none
- Start Date: 2025-02-11
- RFC PR: rust-lang/rfcs#3771
- Rust Issue: rust-lang/rust#0000
Summary
Demote target i686-pc-windows-gnu
from Tier 1 to Tier 2 (with host tools) to better reflect its current maintenance and usage status.
Motivation
Background
Rust has supported Windows for a long time, with two different flavors of Windows targets: MSVC-based and GNU-based.
MSVC-based targets (for example the main Windows target x86_64-pc-windows-msvc
) use Microsoft’s native link.exe
linker and libraries, while GNU-based targets (like i686-pc-windows-gnu
) are built entirely from free software components like gcc
, ld
, and MinGW.
The major reason to use a GNU-based toolchain instead of the native MSVC-based one is cross-compilation and licensing. link.exe
only runs on Windows (barring Wine hacks) and requires a license for commercial usage.
Rust currently supports the following major Windows targets. They all have host tools. The download count was extracted from the public dashboard on 2025-02-10.
We also show the std
download counts to account for cross-compilation usage.
Name | Tier | rustc download count |
std download count |
---|---|---|---|
x86_64-pc-windows-msvc |
1 | 6.72M | 3.56M |
x86_64-pc-windows-gnu |
1 | 375K | 1.06M |
i686-pc-windows-msvc |
1 | 260k | 793K |
i686-pc-windows-gnu |
1 | 76K | 56K |
aarch64-pc-windows-msvc |
2 | 46K | 241K |
To put the download numbers into perspective, some other targets:
Name | Tier | rustc download count |
std download count |
---|---|---|---|
x86_64-unknown-linux-gnu |
1 | 135M | 65M |
i686-unknown-linux-gnu |
1 | 332K | 437K |
x86_64-unknown-freebsd |
2 | 138K | 89K |
x86_64-unknown-netbsd |
2 | 36K | 32K |
From the download count alone, i686-pc-windows-gnu
better fits in next to other Tier 2 targets like FreeBSD and NetBSD.
But that is not everything. GNU-based Windows targets are, as the description at the start may imply, an alternative (you could say non-standard) way to compile for Windows, and as such subject to many kinds of unique problems.
The Rust Project currently does not have a lot of expertise for dealing with these issues.
The setup and build for Windows GNU is complicated and prone to errors, often failing in CI, leading to frequent efforts to fix them being carried out by people who are, at best, familiar with 64-bit Windows or Windows MSVC.
This results in Windows-GNU problems often being unaddressed, or worse: fixed in ways that turn up more errors later down the line.
Some example problems, found by searching for ignore-windows-gnu
in rust-lang/rust.
- https://github.com/rust-lang/rust/issues/128973
- https://github.com/rust-lang/rust/issues/128981
- https://github.com/rust-lang/rust/pull/116837
- https://github.com/rust-lang/rust/issues/128911
32-bit x86 Problems
While some of these issues apply to all GNU-based targets, 32-bit x86 seems to be especially affected.
And when a 32-bit Windows GNU issue comes up, contributors rarely actually investigate it, because it is such a complex and nonstandard environment compared to 64-bit Windows GNU, which is a lot easier to set up and work with.
That the 32-bit x86 architecture is unusual, and made moreso by how Windows operates on it, has also been noted by Windows experts1.
The Windows GNU experts that provide direct support to the Rust project focus almost exclusively on the 64-bit targets, and have previously recommended the retirement of the 32-bit targets2.
MSYS2, a major distributor of the GNU-based Windows platform, has been dropping some 32-bit packages and no longer distributes Clang for 32-bit, showing even their shift away from the platform.
In response to inquiries about their opinion on reducing support for the target, MSYS2 folks were positive.
Target Tier Policy Requirements
With this knowledge, we can look at the Tier 1 requirements of the target tier policy to check whether they are fulfilled.
Tier 1 targets must have substantial, widespread interest within the developer community, and must serve the ongoing needs of multiple production users of Rust across multiple organizations or projects.
While this cannot be quantified precisely, the download counts suggest that this target is less popular than some other Tier 2 targets like FreeBSD.
Therefore, we are going to treat this as false.
The target maintainer team must include at least 3 developers.
This is not the case at all. There is currently no maintainer team.
Though we should note that this is currently also true for many other Tier 1 targets, as this is a new rule not upheld everywhere yet.
But experience tells that it is highly unlikely that 3 maintainers for 32 bit Windows GNU will be found.
The target must build and pass tests reliably in CI, for all components that Rust’s CI considers mandatory.
As mentioned above, there are issues and it does cause a fair share of problems.
The target must not disable an excessive number of tests or pieces of tests in the testsuite in order to do so. This is a subjective requirement.
A fair amount of tests are disabled with an open issue with no comments.
I would say that it is on the edge of being excessive, not quite having reached that amount (but it is likely that will be reached eventually).
For example, #134777 observed and un-ignored a lot of ignored Windows tests, many of which were likely ignored on all of Windows because of Windows GNU issues.
Another example of an ignored test is #135572 that does not support Windows GNU because it was too mcuh effort to test locally.
The target must provide as much of the Rust standard library as is feasible and appropriate to provide […].
Windows is well-supported in the standard library.
Building the target and running the testsuite for the target must not take substantially longer than other targets
Building i686-pc-windows-gnu
is reasonably fast.
If running the testsuite requires additional infrastructure
GitHub Actions has Windows support, which is used for i686-pc-windows-gnu
(on a 64-bit host), no external infrastructure is required.
Tier 1 targets must not have a hard requirement for signed, verified, or otherwise “approved” binaries.
There are no such requirements.
All requirements for tier 2 apply.
We will not go through Tier 2 requirements here, but they are, apart from the (less strict than Tier 1) maintainer requirements, fulfilled.
When the maintainer requirements are enforced more strictly in the future, i686-pc-windows-gnu
(and x86_64-pc-windows-gnu
as well) may be demoted further if no maintainers are found.
Conclusion
Given the usage count and lack of maintenance leading to more than one requirement not being fulfilled, it becomes clear that i686-pc-windows-gnu
is not worthy of being a Tier 1 target and is already getting much worse support than expected from a Tier 1 target.
Explanation
i686-pc-windows-gnu
is now a Tier 2 with Host Tools target instead of a Tier 1 With Host Tools target.
Official builds of the standard library and rustc continue to be distributed for this target, but it is no longer tested in CI.
If necessary, further demotions (for example removing host tools) will not require RFCs, but go through a simpler MCP instead.
A blog post will be made to describe the change.
Drawbacks
By no longer doing automated testing for this target, this target will likely deteriorate more quickly than with continued automated testing.
Additionally, this opens the door for further demotions in the future, like removing host tools, which could still be useful to some people.
But such demotions will always have to be justified on their own.
Rationale and alternatives
The maintenance requirement violation can be solved by multiple people stepping up to maintain this target. This has not happened so far.
The popularity requirement could be fulfilled by more people using this target, but this does not seem possible, as 32-bit x86 as been on a decline for a long time, as new CPU models for this architecture are no longer being made.
Prior art
This is the first time since the Target Tier Policy was created (note that this links to an old version, see the rustc book for the latest version at the time of writing) that a Tier 1 target is being demoted.
Before that, there has been the demotion of i686-apple-darwin
from Tier 1 to Tier 3 in 2019.
The reasoning there was mostly Apple’s support being removed, which is not the case here.
The measures in this RFC are much less drastic.
The promotion of aarch64-apple-darwin
to Tier 1 cited popularity as the major motivation, matching unpopularity as one of the motivations here.
This is a continuation of MCP 822, which contains some additional details in the description and linked Zulip stream.
Unresolved questions
None so far.
Future possibilities
x86_64-pc-windows-gnu
will remain a Tier 1 target after this RFC.
While its popularity is more aligned with Tier 1, it suffers from the same lack of maintenance (but to a lesser degree) as its 32-bit cousin.
It may be demoted as well in the future.
i686-pc-windows-gnu
may be demoted to a Tier 2 target without host tools in the future if it is not deemed useful enough.
This will likely happen in the near future, but is not part of this RFC.
That demotion will not need an RFC.
The *-windows-gnullvm
targets, which are based on LLVM instead of GNU tools, may see increased maintenance and popularity in the future, replacing the *-windows-gnu
targets.
But it seems unlikely that i686-pc-windows-gnullvm
would ever acquire Tier 1 status.
-
https://devblogs.microsoft.com/oldnewthing/20220418-00/?p=106489 ↩
-
despite saying he is only a maintainer for x86_64-pc-windows-gnullvm, mati865 is effectively also our maintainer for x86_64-pc-windows-gnu https://rust-lang.zulipchat.com/#narrow/channel/233931-t-compiler.2Fmajor-changes/topic/Demote.20.60i686-pc-windows-gnu.60.20compiler-team.23822/near/490675824 ↩
4 releases
Uses old Rust 2015
0.4.0 | Jan 18, 2018 |
---|---|
0.3.2 | Dec 28, 2017 |
0.3.1 | Mar 14, 2017 |
0.3.0 | Nov 23, 2016 |
#280 in Windows APIs
Download history
1452883/week @ 2024-12-02
1459573/week @ 2024-12-09
1444180/week @ 2024-12-16
599019/week @ 2024-12-23
677820/week @ 2024-12-30
1229976/week @ 2025-01-06
1362659/week @ 2025-01-13
1336757/week @ 2025-01-20
1722667/week @ 2025-01-27
1520793/week @ 2025-02-03
1791694/week @ 2025-02-10
1692869/week @ 2025-02-17
1650437/week @ 2025-02-24
1850094/week @ 2025-03-03
1729884/week @ 2025-03-10
1872656/week @ 2025-03-17
7,227,027 downloads per month
Used in 43,577 crates
(3 directly)
51MB
Contains
(static library, 1.5MB) lib/libwinapi_ntdll.a,
(static library, 1MB) lib/libwinapi_kernel32.a,
(static library, 640KB) lib/libwinapi_advapi32.a,
(static library, 675KB) lib/libwinapi_icuuc.a,
(static library, 1MB) libwinapi_onecore_downlevel-kernel32.a,
(static library, 1MB) libwinapi_onecoreuap_downlevel-kernel32.a
and 1370 more.
winapi-rs
Documentation
Official communication channel: #windows-dev on the Rust Community Discord
This crate provides raw FFI bindings to all of Windows API. They are gathered by hand using the Windows 10 SDK from Microsoft. I aim to replace all existing Windows FFI in other crates with this crate through the «Embrace, extend, and extinguish» technique.
If this crate is missing something you need, feel free to create an issue, open a pull request, or contact me via other means.
This crate depends on Rust 1.6 or newer on Windows. On other platforms this crate is a no-op and should compile with Rust 1.2 or newer.
Frequently asked questions
How do I create an instance of a union?
Use std::mem::zeroed()
to create an instance of the union, and then assign the value you want using one of the variant methods.
Why am I getting errors about unresolved imports?
Each module is gated on a feature flag, so you must enable the appropriate feature to gain access to those items. For example, if you want to use something from winapi::um::winuser
you must enable the winuser
feature.
How do I know which module an item is defined in?
You can use the search functionality in the documentation to find where items are defined.
Why is there no documentation on how to use anything?
This crate is nothing more than raw bindings to Windows API. If you wish to know how to use the various functionality in Windows API, you can look up the various items on MSDN which is full of detailed documentation.
Can I use this library in no_std
projects?
Yes, absolutely! By default the std
feature of winapi
is disabled, allowing you to write Windows applications using nothing but core
and winapi
.
Why is winapi
‘s HANDLE
incompatible with std
‘s HANDLE
?
Because winapi
does not depend on std
by default, it has to define c_void
itself instead of using std::os::raw::c_void
. However, if you enable the std
feature of winapi
then it will re-export c_void
from std
and cause winapi
‘s HANDLE
to be the same type as std
‘s HANDLE
.
Should I still use those -sys
crates such as kernel32-sys
?
No. Those crates are a legacy of how winapi
0.2 was organized. Starting with winapi
0.3 all definitions are directly in winapi
itself, and so there is no longer any need to use those -sys
crates.
Example
Cargo.toml:
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winuser"] }
main.rs:
#[cfg(windows)] extern crate winapi;
use std::io::Error;
#[cfg(windows)]
fn print_message(msg: &str) -> Result<i32, Error> {
use std::ffi::OsStr;
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
use std::ptr::null_mut;
use winapi::um::winuser::{MB_OK, MessageBoxW};
let wide: Vec<u16> = OsStr::new(msg).encode_wide().chain(once(0)).collect();
let ret = unsafe {
MessageBoxW(null_mut(), wide.as_ptr(), wide.as_ptr(), MB_OK)
};
if ret == 0 { Err(Error::last_os_error()) }
else { Ok(ret) }
}
#[cfg(not(windows))]
fn print_message(msg: &str) -> Result<(), Error> {
println!("{}", msg);
Ok(())
}
fn main() {
print_message("Hello, world!").unwrap();
}
Financial Support
Do you use winapi
in your projects? If so, you may be interested in financially supporting me on Patreon. Companies in particular are especially encouraged to donate (I’m looking at you Microsoft).
No runtime deps
Provide feedback
Saved searches
Use saved searches to filter your results more quickly
Sign up
Appearance settings
Rust has great support for cross compilation, with
cross
, you can install the required c toolchain + linker
and cross compile your rust code to a binary that runs on your targeted
platform. Sweet!
If you’d like to look at the code and results, it’s in this repo
here: https://github.com/Takashiidobe/rust-build-binary-github-actions
Rust library writers use this feature to build and test for other
platforms than their own: hyperfine
for example builds for 11 different platforms.
The rustc
book has a page on targets and tiers of support. Tier 1 supports 8
targets:
Tier 1 |
---|
aarch64-unknown-linux-gnu |
i686-pc-windows-gnu |
i686-unknown-linux-gnu |
i686-pc-windows-msvc |
x86_64-apple-darwin |
x86_64-pc-windows-gnu |
x86_64-pc-windows-msvc |
x86_64-unknown-linux-gnu |
Tier 2 with Host tools supports 21 targets.
Tier 2 |
---|
aarch64-apple-darwin |
aarch64-pc-windows-msvc |
aarch64-unknown-linux-musl |
arm-unknown-linux-gnueabi |
arm-unknown-linux-gnueabihf |
armv7-unknown-linux-gnueabihf |
mips-unknown-linux-gnu |
mips64-unknown-linux-gnuabi64 |
mips64el-unknown-linux-gnuabi64 |
mipsel-unknown-linux-gnuabi |
powerpc-unknown-linux-gnu |
powerpc64-unknown-linux-gnu |
powerpc64le-unknown-linux-gnu |
riscv64gc-unknown-linux-gnu |
s390x-unknown-linux-gnu |
x86_64-unknown-freebsd |
x86_64-unknown-illumos |
arm-unknown-linux-musleabihf |
i686-unknown-linux-musl |
x86_64-unknown-linux-musl |
x86_64-unknown-netbsd |
Let’s try to build a binary for all 29 targets.
A Note on Targets
The Rust RFC for Target support: https://rust-lang.github.io/rfcs/0131-target-specification.html
A target is defined in three or four parts:
$architecture-$vendor-$os-$environment
The environment is optional, so some targets have three parts and
some have four.
Let’s take x86_64-apple-darwin
for example.
x86_64
is the architectureapple
is the vendordarwin
is the os
You’ll notice here that there is no $environment
. This
target assumes the environment, which is most likely to be
gnu
.
Let’s take one with four parts:
i686-pc-windows-msvc
.
i686
is the architecturepc
is the vendorwindows
is the osmsvc
is the environment
In this target, the environment is specified as msvc
,
the microsoft C compiler. This is the most popular compiler for windows,
but it need not be: if you look in the same tier 1 table, there’s this
target: i686-pc-windows-gnu
.
The only thing that’s changed is the environment is now
gnu
. Windows can use gcc
instead of
msvc
, so building for this target uses the gcc
instead of msvc
.
Architectures
Architecture | Notes |
---|---|
aarch64 | ARM 64 bit |
i686 | Intel 32 bit |
x86_64 | Intel 64 bit |
arm | ARM 32 bit |
armv7 | ARMv7 32 bit |
mips | MIPS 32 bit |
mips64 | MIPS 64 bit |
mips64el | MIPS 64 bit Little Endian |
mipsel | MIPS 32 bit Little Endian |
powerpc | IBM 32 bit |
powerpc64 | IBM 64 bit |
rsicv64gc | RISC-V 64 bit |
s390x | IBM Z 32 bit |
Vendors
Vendor | Notes |
---|---|
pc | Microsoft |
apple | Apple |
unknown | Unknown |
Operating Systems
Operating System | Notes |
---|---|
darwin | Apple’s OS |
linux | Linux OS |
windows | Microsoft’s OS |
freebsd | FreeBSD OS |
netbsd | NetBSD OS |
illumos | Illumos OS, a Solaris derivative |
Environments
Environment | Notes |
---|---|
musl | Musl C library |
gnu | GNU’s C library |
msvc | Microsoft Visual C library |
freebsd | FreeBSD’s C library |
netbsd | NetBSD’s C library |
illumos | Illumos’ C library |
When you go to the releases tab to download a particular binary,
you’ll need to know these four things to download a binary that runs on
your system.
Now, let’s start building for all these systems.
Building Binaries for ~30 Targets
We’re going to use Github Actions, a task runner on github.com to
build our binaries. Our binary is a simple hello world
binary.
If you’d just like to look at the github actions file, it’s located
here: https://github.com/Takashiidobe/rust-build-binary-github-actions/blob/master/.github/workflows/release.yml
Conceptually, we’d like to do the following:
- Set up our target environments.
- Download the C compiler (environment) we need.
- Download a docker image of the OS we require.
- Download the rust toolchain onto docker container.
- Build the binary.
- Optionally strip debug symbols.
- Publish it to the github releases tab.
We’ll first start out by defining our github action and setting up
the target environments:
name: release
env:
MIN_SUPPORTED_RUST_VERSION: "1.56.0"
CICD_INTERMEDIATES_DIR: "_cicd-intermediates"
on:
push:
tags:
- '*'
jobs:
build:
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
# Tier 1
- { target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
- { target: i686-pc-windows-gnu , os: windows-2019 }
- { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
- { target: i686-pc-windows-msvc , os: windows-2019 }
- { target: x86_64-apple-darwin , os: macos-10.15 }
- { target: x86_64-pc-windows-gnu , os: windows-2019 }
- { target: x86_64-pcwindows-msvc , os: windows-2019 }
- { target: x86_64-unknown-linux-gnu , os: ubuntu-20.04 }
# Tier 2 with Host Tools
- { target: aarch64-apple-darwin , os: macos-11.0 }
- { target: aarch64-pc-windows-msvc , os: windows-2019 }
- { target: aarch64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
- { target: arm-unknown-linux-gnueabi , os: ubuntu-20.04, use-cross: true }
- { target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true }
- { target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true }
- { target: mips-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
- { target: mips64-unknown-linux-gnuabi64 , os: ubuntu-20.04, use-cross: true }
- { target: mips64el-unknown-linux-gnuabi64, os: ubuntu-20.04, use-cross: true }
- { target: mipsel-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
- { target: powerpc-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
- { target: powerpc64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
- { target: powerpc64le-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
- { target: riscv64gc-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
- { target: s390x-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
- { target: x86_64-unknown-freebsd , os: ubuntu-20.04, use-cross: true }
- { target: x86_64-unknown-illumos , os: ubuntu-20.04, use-cross: true }
- { target: arm-unknown-linux-musleabihf , os: ubuntu-20.04, use-cross: true }
- { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
- { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
- { target: x86_64-unknown-netbsd , os: ubuntu-20.04, use-cross: true }
Checking out our code:
steps:
- name: Checkout source code
uses: actions/checkout@v2
Downloading the C compiler
Most of the time, the C compiler we need is already installed, but in
some cases it’ll be overriden by another compiler.
We’ll need to download the correct suitable C compiler in that case:
(i686-pc-windows-gnu has gcc, but it’s not on the $PATH).
- name: Install prerequisites
shell: bash
run: |
case ${{ matrix.job.target }} in
arm-unknown-linux-*) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;;
i686-pc-windows-gnu) echo "C:\msys64\mingw32\bin" >> $GITHUB_PATH
esac
Installing the Rust toolchain
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: ${{ matrix.job.target }}
override: true
profile: minimal # minimal component installation (ie, no documentation)
Building the executable
- name: Build
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.job.use-cross }}
command: build
args: --locked --release --target=${{ matrix.job.target }}
Stripping debug information from binary
- name: Strip debug information from executable
id: strip
shell: bash
run: |
# Figure out suffix of binary
EXE_suffix=""
case ${{ matrix.job.target }} in
*-pc-windows-*) EXE_suffix=".exe" ;;
esac;
# Figure out what strip tool to use if any
STRIP="strip"
case ${{ matrix.job.target }} in
arm-unknown-linux-*) STRIP="arm-linux-gnueabihf-strip" ;;
aarch64-pc-*) STRIP="" ;;
aarch64-unknown-*) STRIP="" ;;
armv7-unknown-*) STRIP="" ;;
mips-unknown-*) STRIP="" ;;
mips64-unknown-*) STRIP="" ;;
mips64el-unknown-*) STRIP="" ;;
mipsel-unknown-*) STRIP="" ;;
powerpc-unknown-*) STRIP="" ;;
powerpc64-unknown-*) STRIP="" ;;
powerpc64le-unknown-*) STRIP="" ;;
riscv64gc-unknown-*) STRIP="" ;;
s390x-unknown-*) STRIP="" ;;
x86_64-unknown-freebsd) STRIP="" ;;
x86_64-unknown-illumos) STRIP="" ;;
esac;
# Setup paths
BIN_DIR="${{ env.CICD_INTERMEDIATES_DIR }}/stripped-release-bin/"
mkdir -p "${BIN_DIR}"
BIN_NAME="${{ env.PROJECT_NAME }}${EXE_suffix}"
BIN_PATH="${BIN_DIR}/${BIN_NAME}"
TRIPLET_NAME="${{ matrix.job.target }}"
# Copy the release build binary to the result location
cp "target/$TRIPLET_NAME/release/${BIN_NAME}" "${BIN_DIR}"
# Also strip if possible
if [ -n "${STRIP}" ]; then
"${STRIP}" "${BIN_PATH}"
fi
# Let subsequent steps know where to find the (stripped) bin
echo ::set-output name=BIN_PATH::${BIN_PATH}
echo ::set-output name=BIN_NAME::${BIN_NAME}
And uploading to Github
- name: Create tarball
id: package
shell: bash
run: |
PKG_suffix=".tar.gz" ; case ${{ matrix.job.target }} in *-pc-windows-*) PKG_suffix=".zip" ;; esac;
PKG_BASENAME=${PROJECT_NAME}-v${PROJECT_VERSION}-${{ matrix.job.target }}
PKG_NAME=${PKG_BASENAME}${PKG_suffix}
echo ::set-output name=PKG_NAME::${PKG_NAME}
PKG_STAGING="${{ env.CICD_INTERMEDIATES_DIR }}/package"
ARCHIVE_DIR="${PKG_STAGING}/${PKG_BASENAME}/"
mkdir -p "${ARCHIVE_DIR}"
mkdir -p "${ARCHIVE_DIR}/autocomplete"
# Binary
cp "${{ steps.strip.outputs.BIN_PATH }}" "$ARCHIVE_DIR"
# base compressed package
pushd "${PKG_STAGING}/" >/dev/null
case ${{ matrix.job.target }} in
*-pc-windows-*) 7z -y a "${PKG_NAME}" "${PKG_BASENAME}"/* | tail -2 ;;
*) tar czf "${PKG_NAME}" "${PKG_BASENAME}"/* ;;
esac;
popd >/dev/null
# Let subsequent steps know where to find the compressed package
echo ::set-output name=PKG_PATH::"${PKG_STAGING}/${PKG_NAME}"
- name: "Artifact upload: tarball"
uses: actions/upload-artifact@master
with:
name: ${{ steps.package.outputs.PKG_NAME }}
path: ${{ steps.package.outputs.PKG_PATH }}
- name: Check for release
id: is-release
shell: bash
run: |
unset IS_RELEASE ; if [[ $GITHUB_REF =~ ^refs/tags/v[0-9].* ]]; then IS_RELEASE='true' ; fi
echo ::set-output name=IS_RELEASE::${IS_RELEASE}
- name: Publish archives and packages
uses: softprops/action-gh-release@v1
if: steps.is-release.outputs.IS_RELEASE
with:
files: |
${{ steps.package.outputs.PKG_PATH }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
And after building this github actions file, we find that… 3 targets
fail to build.
x86_64-unknown-freebsd
,
x86_64-unknown-illumos
,
powerpc-unknown-linux-gnu
.
Luckily, the error message that cross provides gives us a clear
indication of what to fix. Cross does not provide a proper image, so it
gets confused, defaults to the toolchain it’s running on (ubuntu 20.04),
and the linker cannot find the proper libraries required. Easy to fix:
Add a Cross.toml
file to the root of the project with
docker images for the particular targets, and build again.
[target.x86_64-unknown-freebsd]
image = "svenstaro/cross-x86_64-unknown-freebsd"
[target.powerpc64-unknown-linux-gnu]
image = "japaric/powerpc64-unknown-linux-gnu"
You’ll notice that illumos is missing here – I couldn’t find a
suitable docker image to build it on docker hub, so I gave up. If you
find one, let me know and i’ll update this article.
Results
Out of the 29 architectures provided in Tier 1 and Tier 2 with host
tools, it was easy enough to build a binary for 28 architectures (We
only need a solaris/illumos docker image to build for the last one).
That’s pretty good, given that this only took a couple of hours to
test out. I hope Rust continues to support this many architectures into
the future, and Github Actions keeps being a good platform to make
releases for.
If you’d like to take the repo for yourself to build rust binaries on
releases for 28 architectures, feel free to clone/fork the repo here: https://github.com/Takashiidobe/rust-build-binary-github-actions
Время на прочтение10 мин
Количество просмотров19K
Наверное не будет уж очень удивительным если я тут, на IT площадке Хабра, скажу что я иногда балую себя программированием.
Основная OS у меня Linux, но иногда приходится собирать исполняемые файлы и для Windows. И естественно что перегружаться в Windows только для сборки exe не особо хочется. С языками C и C++ проблем нет, давно существует кросскомпилятор MinGW
, который прекрасно с этим справляется. Про Python
и Java
даже упоминать не стоит, кроссплатформенность в них изначально. Но в прошлом году я решил попробовать такой пока что новомодный язык, как Rust. При сборке исполняемого файла при помощи включённого в дистрибутив Rust пакетного менеджера cargo
вроде как достаточно задать ключ --target
, при помощи которого указать результирующий процессор, архитектуру и ABI и при сборке из Linux в результате получить exe, который будет являться стандартным исполняемым файлом для Windows. Но пытаясь так сделать:
cargo build --target x86_64-pc-windows-gnu
я получил только сообщения об ошибках линкера:
error: linking with `gcc` failed: exit code: 1
[...]
= note: /usr/bin/ld: unrecognized option '--nxcompat'
/usr/bin/ld: use the --help option for usage information
collect2: error: ld returned 1 exit status
error: aborting due to previous error
error: could not compile `foobar`.
Если кому интересно как я это поборол и теперь спокойно могу кросскомпилировать программы на Rust для Windows, не покидая Linux, добро пожаловать под кат.
Disclaimer
Далее я рассматриваю только цели 32bit и 64bit pc-windows-gnu, цели pc-windows-msvc для меня интереса не представляют и поэтому в них я не углублялся. Так же речь будет идти о том дистрибутиве Linux, который установлен на моём компьютере, то есть Fedora Linux 31, но я не думаю что на других дистрибутивах Linux будут очень уж существенные различия. И я использую Rust установленный при помощи The Rust toolchain installer, а не входящий в репозиторий Fedora Rust по причине того, что мне иногда требуются nightly сборки Rust, которых в стандартном репозитории, естественно, нет.
Первым делом убеждаемся что у нас установлены необходимые цели, запустив следующую команду:
rustup target list
Получаем список всех возможных целей, и целей, которые у нас установлены:
aarch64-apple-ios
aarch64-fuchsia
[...]
i686-pc-windows-gnu (installed)
[...]
i686-unknown-linux-gnu (installed)
[...]
x86_64-pc-windows-gnu (installed)
x86_64-unknown-linux-gnu (installed)
[...]
Для создания исполняемых файлов для Windows из Linux нам необходимы цели i686-pc-windows-gnu
для 32bit exe и x86_64-pc-windows-gnu
для 64bit exe. Если данные цели не отмечены как (installed)
, то доставляем их при помощи команды
rustup target add имя_цели
После убеждаемся что у нас установлен кросскомпилятор MinGW
, запустив
rpm -qa | grep mingw
или другой пакетный менеджер для нашего дистрибутива Linux:
mingw32-gcc-9.2.1-1.fc31.x86_64
mingw32-binutils-2.32-6.fc31.x86_64
mingw64-gcc-9.2.1-1.fc31.x86_64
mingw-binutils-generic-2.32-6.fc31.x86_64
mingw-filesystem-base-110-1.fc31.noarch
mingw64-winpthreads-6.0.0-2.fc31.noarch
mingw32-winpthreads-6.0.0-2.fc31.noarch
mingw32-crt-6.0.0-2.fc31.noarch
mingw64-binutils-2.32-6.fc31.x86_64
mingw64-crt-6.0.0-2.fc31.noarch
mingw64-filesystem-110-1.fc31.noarch
mingw32-filesystem-110-1.fc31.noarch
mingw32-cpp-9.2.1-1.fc31.x86_64
mingw64-headers-6.0.0-2.fc31.noarch
mingw32-headers-6.0.0-2.fc31.noarch
mingw64-cpp-9.2.1-1.fc31.x86_64
При отсутствии MinGW
устанавливаем необходимые пакеты, запустив
sudo dnf install mingw32-gcc mingw64-gcc
Ну вот вроде бы теперь всё в наличии, далее будем решать проблемы по мере их появления (ага, можно сказать что это получается прям какой-то Test-Driven Development,
Создаём простейший проект на языке 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]$
Сначала компилируем и запускаем его как родное приложение Linux:
[pfemidi@pfemidi foobar]$ cargo run
Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
Finished dev [unoptimized + debuginfo] target(s) in 1.65s
Running `target/debug/foobar`
Hello, world!
[pfemidi@pfemidi foobar]$
Всё работает. Теперь пробуем его собрать как цель x86_64-pc-windows-gnu
:
cargo build --target x86_64-pc-windows-gnu
и получаем всё то же сообщение об ошибке сборки:
error: linking with `gcc` failed: exit code: 1
[...]
= note: /usr/bin/ld: unrecognized option '--nxcompat'
/usr/bin/ld: use the --help option for usage information
collect2: error: ld returned 1 exit status
error: aborting due to previous error
error: could not compile `foobar`.
Понятно, для сборки вызывается не линкер из MinGW
, а уже установленный в системе gcc
. Исправляем эту ситуацию, для этого создаём в проекте директорию .cargo и в ней файл config со следующим содержимым:
[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 в качестве линкера использовался не установленный в системе gcc
, а линкер из MinGW
.
Пробуем собрать проект снова:
cargo build --target x86_64-pc-windows-gnu
и получаем другую ошибку от линкера, уже от x86_64-w64-mingw32-gcc
:
error: linking with `x86_64-w64-mingw32-gcc` failed: exit code: 1
[...]
= note: /usr/lib/gcc/x86_64-w64-mingw32/9.2.1/../../../../x86_64-w64-mingw32/bin/ld: cannot find -lpthread
collect2: error: ld returned 1 exit status
error: aborting due to previous error
error: could not compile `foobar`.
Дело в том, что Rust по-умолчанию собирает всё в статическом виде, поэтому кроме пакетов mingw32-winpthreads
и mingw64-winpthreads
, которые dnf
автоматически установил как зависимости для mingw32-gcc
и mingw64-gcc
обязательно должны быть установлены пакеты статических библиотек mingw32-winpthreads-static
и mingw64-winpthreads-static
, без них линкер всё время будет жаловаться на отсутствующий -lpthread
и сборка не пройдёт. Доустанавливаем недостающие пакеты:
sudo dnf install mingw??-winpthreads-static
и опять запускаем компиляцию:
cargo build --target x86_64-pc-windows-gnu
Опять ошибка линковки! Но уже другая:
error: linking with `x86_64-w64-mingw32-gcc` failed: exit code: 1
[...]
= 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`.
Линкер жалуется на отсутствующие символы __onexitbegin
и __onexitend
в файле ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/crt2.o
, который мы установили в составе цели x86_64-pc-windows-gnu
. После некоторых раздумий, гугления, чтения доков на сайте Rust, изучения исходников самого Rust, того как и чем сам Rust собирается я понял: дело в том что сам Rust для Windows, и соответственно его компоненты для целей pc-windows-gnu, собраны с использованием MinGW 6.3.0, а у меня в Fedora Linux 31 версия MinGW 9.2.1, поэтому и происходит несоответствие в CRT. Ok, попробуем перенести crt2.o из федориного MinGW в директорию Rust для цели x86_64-pc-windows-gnu. И кроме 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]$ 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]$
и опять запускаем компиляцию нашего проекта на Rust:
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 4.46s
[pfemidi@pfemidi foobar]$
Прекрасно! Всё собралось! Т.к. у меня установлен wine, то тут же я могу и проверить как это работает:
[pfemidi@pfemidi foobar]$ cargo run --target x86_64-pc-windows-gnu
Finished dev [unoptimized + debuginfo] target(s) in 0.38s
Running `target/x86_64-pc-windows-gnu/debug/foobar.exe`
Hello, world!
[pfemidi@pfemidi foobar]$
И даже работает! Теперь пробуем сделать то же самое для 32bit версии исполняемого файла Windows, делаем сразу run
без предварительного build
:
error: linking with `i686-w64-mingw32-gcc` failed: exit code: 1
[...]
= 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`.
Ошибку с отсутствием символов __onexitbegin
и __onexitend
теперь уже в файле ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/crt2.o
мы уже проходили, лечится точно так же, как и для 64bit цели заменой файлов crt2.o и dllcrt2.o на аналогичные по именам, но из дистрибутива MinGW
из Fedora:
[pfemidi@pfemidi foobar]$ cd ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/
[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]$
Проверяем:
[pfemidi@pfemidi foobar]$
[pfemidi@pfemidi foobar]$ cargo run --target i686-pc-windows-gnu
Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
Finished dev [unoptimized + debuginfo] target(s) in 5.12s
Running `target/i686-pc-windows-gnu/debug/foobar.exe`
Hello, world!
[pfemidi@pfemidi foobar]$
Тут теперь тоже всё собирается и работает.
И всё было прекрасно пока я не использовал никакие функции, которые паникуют (macro panic!, функция expect и т.д.) в 32bit целях для Windows. В целях 64bit всё хорошо, а вот в целях 32bit нет.
Добавим в наш проект панику:
[pfemidi@pfemidi foobar]$ cat src/main.rs
fn main() {
println!("Hello, world!");
panic!("I'm panicked!"); // ВОТ НАША ПАНИКА!
}
[pfemidi@pfemidi foobar]
и попробуем собрать как исполняемый файл для 64bit Windows:
[pfemidi@pfemidi foobar]$ cargo run --target x86_64-pc-windows-gnu
Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
Finished dev [unoptimized + debuginfo] target(s) in 2.95s
Running `target/x86_64-pc-windows-gnu/debug/foobar.exe`
Hello, world!
thread 'main' panicked at 'I'm panicked!', src/main.rs:3:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
[pfemidi@pfemidi foobar]$
И компилируется, и собирается, и работает. Попробуем теперь сделать то же самое, но в качестве цели укажем 32bit Windows.
Упс:
[pfemidi@pfemidi foobar]$ cargo run --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: /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-1a1fb2d4d34efaf8.rlib(panic_unwind-1a1fb2d4d34efaf8.panic_unwind.2hbcqjo8-cgu.0.rcgu.o): in function `ZN12panic_unwind3imp5panic17hdaabfe6326236dacE':
/rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/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-1a1fb2d4d34efaf8.rlib(panic_unwind-1a1fb2d4d34efaf8.panic_unwind.2hbcqjo8-cgu.0.rcgu.o): in function `rust_eh_unwind_resume':
/rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/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`.
Опять линкер жалуется на отсутствие символов, но теперь это символы _Unwind_RaiseException
и _Unwind_Resume
в модуле libpanic
стандартной библиотеки Rust.
Снова раздумия, снова гугление, снова чтение доков и изучение исходников как самого Rust, так и его стандартной библиотеки. И я понял почему возникает такая ошибка.
Для разматывания стека при исключении Rust использует метод Dwarf
для 32bit целей Windows и SEH
для 64bit целей Windows, а MinGW
из стандартного репозитория Fedora Linux использует метод SJLJ
для 32bit целей Windows и SEH
для 64bit целей Windows (о различии между этими методами читать тут). Поэтому 64bit цели собираются без вопросов, а для 32bit просто нет необходимых символов и объектных файлов. Чтобы получить данные файлы необходимо пересобрать MinGW
с поддержкой Dwarf
вместо поддерки SJLJ
по умолчанию для 32bit целей Windows.
Я не буду вдаваться в подробности как именно пересобирать MinGW
, это уже не так сложно и не так интересно (configure
там надо запускать с параметром --disable-sjlj-exceptions
, остальное тривиально), скажу только одно: после того как MinGW
пересобран с разматыванием стека Dwarf
вместо SJLJ
оттуда надо взять всего один файл под названием libgcc_eh.a
и положить его в директорию с библиотеками для цели i686-pc-windows-gnu
. После этого проекты в которых используются паникующие функции начнут собираться не только для 64bit целей Windows, но и для 32bit:
[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 run --target i686-pc-windows-gnu
Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
Finished dev [unoptimized + debuginfo] target(s) in 4.57s
Running `target/i686-pc-windows-gnu/debug/foobar.exe`
Hello, world!
thread 'main' panicked at 'I'm panicked!', src/main.rs:3:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
[pfemidi@pfemidi foobar]$
Ну вот, как-то так.