Skip to content

Commit 3454fff

Browse files
committed
build-mingw-w64.sh: Document my findings
1 parent 2ba0344 commit 3454fff

File tree

1 file changed

+119
-2
lines changed

1 file changed

+119
-2
lines changed

build-mingw-w64.sh

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,127 @@
22

33
set -e
44

5+
# The Story So Far
6+
#
7+
# We need two cross-compilers to build Proton. These each have to run in the
8+
# Steam runtimes, each of which only supports 32- or 64-bit programs. Our build
9+
# VM is a 64-bit Linux machine. So our platform configurations are:
10+
#
11+
# 64-bit cross-compiler:
12+
# Build (where the compiler is built): 64-bit linux (our VM)
13+
# Host (where the compiler is run): 64-bit linux (64-bit Steam runtime)
14+
# Target (what the compiler outputs): 64-bit win32 (PE files to be run)
15+
#
16+
# 32-bit cross-compiler:
17+
# Build (where the compiler is built): 64-bit linux (our VM)
18+
# Host (where the compiler is run): 32-bit linux (32-bit Steam runtime)
19+
# Target (what the compiler outputs): 32-bit win32 (PE files to be run)
20+
#
21+
# The former is a pretty standard cross-compiler setup.
22+
#
23+
# gcc calls the latter, where all of the platforms are different, a "Canadian"
24+
# build. See "The GNU configure and build system" by Ian Lance Taylor, section
25+
# "Canadian Cross", available here:
26+
# https://www.airs.com/ian/configure/configure_6.html
27+
#
28+
# This turned out to be quite difficult, possibly due to the added complexity of
29+
# mingw-w64. I'm documenting the problems I had and how I solved/worked around
30+
# them here, to hopefully save myself or someone else some heartburn in the
31+
# future. If you are a compiler expert and find yourself shaking your head at
32+
# this poor fool, I welcome your assistance improving this.
33+
#
34+
# Canadian builds require the build machine to already have a cross-compiler for
35+
# the Target platform available on the Build machine. Without this, the gcc build
36+
# will fail with:
37+
#
38+
# i686-w64-mingw32-gcc -dumpspecs > tmp-specs
39+
# /bin/bash: i686-w64-mingw32-gcc: command not found
40+
#
41+
# We are building both C and C++ lang support, so we need both gcc and g++
42+
# cross-compilers available. With the Build->Target cross-compilers installed,
43+
# we can now build gcc, and use that to build mingw-w64. After that, we need to
44+
# build gcc's libraries, like libgcc and libstdc++v3, against those mingw-w64
45+
# libraries. However, the libatomic and/or libgomp builds will fail with:
46+
#
47+
# checking for suffix of object files...
48+
# configure: error: in `/home/vagrant/mingw/build-i686-w64-mingw32/gcc/i686-w64-mingw32/libatomic':
49+
# configure: error: C compiler cannot create executables
50+
# See `config.log' for more details
51+
#
52+
# checking for suffix of object files...
53+
# configure: error: in `/home/vagrant/mingw/build-i686-w64-mingw32/gcc/i686-w64-mingw32/libgomp':
54+
# configure: error: C compiler cannot create executables
55+
# See `config.log' for more details
56+
#
57+
# Digging into config.log shows it can't find libgcc:
58+
#
59+
# configure:3722: checking whether the C compiler works
60+
# configure:3744: i686-w64-mingw32-gcc -L/home/vagrant/mingw/output/i686-w64-mingw32/lib -L/home/vagrant/mingw/output/mingw/lib -isystem /home/vagrant/mingw/output/i686-w64-mingw32/include -isystem /home/vagrant/mingw/output/mingw/include -g -O2 conftest.c >&5
61+
# /home/vagrant/mingw/output/lib/gcc/i686-w64-mingw32/9.2.0/../../../../i686-w64-mingw32/bin/ld: cannot find -lgcc
62+
# /home/vagrant/mingw/output/lib/gcc/i686-w64-mingw32/9.2.0/../../../../i686-w64-mingw32/bin/ld: cannot find -lgcc_eh
63+
# /home/vagrant/mingw/output/lib/gcc/i686-w64-mingw32/9.2.0/../../../../i686-w64-mingw32/bin/ld: cannot find -lgcc
64+
# /home/vagrant/mingw/output/lib/gcc/i686-w64-mingw32/9.2.0/../../../../i686-w64-mingw32/bin/ld: cannot find -lgcc_eh
65+
# collect2: error: ld returned 1 exit status
66+
#
67+
# I don't know what causes this (maybe Canadian builds don't like being split up
68+
# like this?). However, installing libgcc explicitly first will work around it.
69+
# After installing libgcc explicitly, we can then build the rest of gcc's
70+
# libraries. That was all the easy (ha!) stuff. At this point, you will have a
71+
# cross-compiler that will build Proton.
72+
#
73+
# However, there's a sneaky problem. For some reason the libstdc++v3 build system
74+
# requires libstdc++ to be available to detect C-lang functions(?!?). It will
75+
# still build without those C functions, but it may have far worse performance
76+
# (see Proton github issue #3198). You can detect this situation by examining the
77+
# config.log output for libstdc++v3 after your build completes:
78+
#
79+
# configure:16811: checking for gettimeofday
80+
# configure:16827: checking sys/time.h usability
81+
# configure:16827: i686-w64-mingw32-c++ -L/home/vagrant/mingw/output/i686-w64-mingw32/lib -L/home/vagrant/mingw/output/mingw/lib -isystem /home/vagrant/mingw/output/i686-w64-mingw32/include -isystem /home/vagrant/mingw/output/mingw/include -c -g -O2 -fno-exceptions conftest.cpp >&5
82+
# configure:16827: $? = 0
83+
# configure:16827: result: yes
84+
# configure:16827: checking sys/time.h presence
85+
# configure:16827: i686-w64-mingw32-c++ -L/home/vagrant/mingw/output/i686-w64-mingw32/lib -L/home/vagrant/mingw/output/mingw/lib -isystem /home/vagrant/mingw/output/i686-w64-mingw32/include -isystem /home/vagrant/mingw/output/mingw/include -E conftest.cpp
86+
# configure:16827: $? = 0
87+
# configure:16827: result: yes
88+
# configure:16827: checking for sys/time.h
89+
# configure:16827: result: yes
90+
# configure:16840: checking for gettimeofday
91+
# configure:16875: i686-w64-mingw32-c++ -L/home/vagrant/mingw/output/i686-w64-mingw32/lib -L/home/vagrant/mingw/output/mingw/lib -isystem /home/vagrant/mingw/output/i686-w64-mingw32/include -isystem /home/vagrant/mingw/output/mingw/include -o conftest.exe -g -O2 -fno-exceptions conftest.cpp >&5
92+
# /home/vagrant/mingw/output/lib/gcc/i686-w64-mingw32/9.2.0/../../../../i686-w64-mingw32/bin/ld: cannot find -lstdc++
93+
# collect2: error: ld returned 1 exit status
94+
#
95+
# But that error doesn't kill configure, and it happily continues with this in
96+
# config.h:
97+
#
98+
# /* #undef _GLIBCXX_USE_GETTIMEOFDAY */
99+
#
100+
# This triggers the bad codepath in libstdc++v3 that caused the performance
101+
# problem mentioned above, and is clearly wrong anyway, as gettimeofday is
102+
# definitely available. Yes, this was a huge pain to puzzle out. I "solved" this
103+
# by letting the libstdc++v3 build continue, then re-running the build after
104+
# installing it, so now it will find libstdc++ and so correctly detect the C-lang
105+
# function that has nothing at all to do with libstdc++. Argh.
106+
#
107+
# After all that, we finally have a functioning set of cross-compilers. Most of
108+
# the configuration parameters used here are taken from the Arch Linux mingw-w64
109+
# PKGBUILDs, as those were written by someone with more knowledge than me, and
110+
# have had a lot of testing. We make one tweak to the gcc configuration, which is
111+
# to disable SJLJ exceptions and enable DWARF2 debug symbols. The former
112+
# apparently helps with performance on 32-bit, and the latter puts debug symbols
113+
# in a format that more Linux tooling can read.
114+
#
115+
# Side note, the Arch Linux PKGBUILDs work very differently from how this script
116+
# works. While working on this, I tried to adapt the PKGBUILDs' process here
117+
# (e.g. build a bootstrap C-lang compiler, use that to build mingw-w64, then
118+
# rebuild gcc against the mingw-w64 CRT with C++-lang support), but I ran into a
119+
# ton of problems, possibly caused by the Canadian-ness of the build, and
120+
# eventually ran out of ideas for working around them.
121+
5122
if [ -z "$1" ]; then
6123
echo "Makes a local build of mingw-w64 in this directory and installs it to the given path."
7124
echo ""
8-
echo "Note: Requires a system mingw-w64 to be present already, for us to bootstrap with."
125+
echo "Note: Requires a system mingw-w64 compiler to be present already on your build machine, for us to bootstrap with."
9126
echo ""
10127
echo "usage:"
11128
echo -e "\t$0 <installation path e.g. \$HOME/.local>"
@@ -175,7 +292,7 @@ function build_arch {
175292

176293
pushd gcc/
177294
#next step requires libgcc in default library location, but
178-
#"canadian" build doesn't handle that, so install it explicitly
295+
#"canadian" build doesn't handle that(?), so install it explicitly
179296
PATH=$NEWPATH make configure-target-libgcc
180297
PATH=$NEWPATH make -C $WIN32_TARGET_ARCH/libgcc $JOBS
181298
PATH=$NEWPATH make -C $WIN32_TARGET_ARCH/libgcc $JOBS install

0 commit comments

Comments
 (0)