1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
Library naming
--------------
The way Boost names library files by default is insane. It's absolutely not
compatible between OSs, compilers, Boost versions, etc. On Linux, for example,
it would create stage/lib/libboost_filesystem.a, while on Windows it would
become something insane like stage\lib\libboost_filesystem-vc142-mt-s-x64-1_72.lib.
More than that, older Boost versions don't include architecture information
(the "x64" part) in the file name, so you cannot store libraries for both x86
and x64 in the same directory. On Linux, on the other hand, you can't even
store debug/release binaries in the same directory. What's worse is that older
CMake versions don't support the architecture suffix, choking on the Windows
example above.
With all of that in mind, I decided to bring some uniformity by sacrificing
some flexibility. b2 is called with `--layout=system`, and libraries are put to
stage/\<platform\>/\<configuration\>/lib, where \<platform\> is x86/x64 and
\<configuration\> is CMake's CMAKE_BUILD_TYPE. That means that I can't have
libraries with different runtime-link values in the same directory, but I don't
really care.
Hate speech
-----------
Is there a person who doesn't hate Boost.Build? I'm not sure, I'm definitely
_not_ one of these people. Maybe it's the lack of adoption (meaning that
learning it is useless outside of Boost), maybe it's the incomprehensible
syntax. Maybe it's the absolutely insane compiler-specific configuration files
(tools/build/src/tools/*.jam), which are impossible to figure out. Maybe it's
the fact that the implementation switched from C to C++ while some half-baked
Python implementation has been there since at least 2015 (see the marvelous
memo "Status: mostly ported." at the top of tools/build/src/build_system.py).
What I hate the most though is how its various subtle, implicit and invisible
decision-making heuristics changed thoughout the release history of Boost. You
have a config and a compiler that will happily build version 1.65.0? Great!
Want to use the same config and the same compiler to build version 1.72.0?
Well, too fucking bad, it doesn't work anymore. This I really do hate the
most.
Three kinds of toolsets
-----------------------
b2 accepts the `toolset=` parameter. What about building b2 itself though?
Well, this is what the bootstrap.{sh,bat} scripts do. They also accept a
toolset argument, but it is _completely_ different to that of b2. That's sort
of OK, since e.g. cross-compiling b2 is something we rarely want to do (and
hence there must typically be a native toolset available).
bootstrap.sh and bootstrap.bat are completely different (of course!), and
accept different arguments for their toolset parameters.
Config file insanity
--------------------
Say, we're building Boost on Windows using the GCC from a MinGW-w64
distribution. We can pass `toolset=gcc` and all the required flags on the
command line no problem. What if we want to make a user configuration file so
that 1) the command line is less polluted, and 2) it can possibly be shared?
Well, if we put
using gcc : : : <name>value... ;
there, Boost 1.65.0 will happily build everything, while Boost 1.72.0 will
complain about "duplicate initialization of gcc". This is because when we ran
`bootstrap.bat gcc` earlier, it wrote `using gcc ;` in project-config.jam. And
while Boost 1.65.0 detects that `toolset=gcc` means we're going to use the
MinGW GCC, and magically turns `toolset=gcc` to `toolset=gcc-mingw`, Boost
1.72.0 does no such thing, and chokes on the "duplicate" GCC declaration.
We also cannot put
using gcc : custom : : <options> ;
without the executable path, since Boost insists that `g++ -dumpversion` must
equal to "custom" (which makes total sense, lol). So we have to force it, and
do provide the path.
Windows & Clang
---------------
Building Boost using Clang on Windows is a sad story. As of 2020, there're
three main ways to install the native Clang toolchain on Windows:
* download the installer from llvm.org (`choco install llvm` does this)
a.k.a. the upstream,
* install it as part of a MSYS2 installation (`pacman -S mingw-w64-x86_64-clang`),
* install as part of a Visual Studio installation.
Using the latter method, you can switch a project to use the LLVM toolset using
Visual Studio, but that's stupid. The former two, on the other hand, give us
the the required clang/clang++/clang-cl executables, so everything seems to be
fine.
Except it's not fine. Let's start with the fact that prior to 1.66.0,
`toolset=clang` is completely broken on Windows. It's just an alias for
clang-linux, and it's hardcoded to require the ar & ranlib executables to
create static libraries. Which is fine on Linux, since, and I'm quoting the
source, "ar is always available". But it's not fine on Windows, since
ar/ranlib are not, in fact, available there by default. Sure, you can install
some kind of MinGW toolchain, and it might even work, but what the hell,
honestly?
Luckily, both the upstream distribution and the MSYS2 mingw-w64-x86_64-llvm
package come with the llvm-ar and llvm-ranlib utilities. So we can put
something like this in the config:
using clang : custom : clang++.exe : <archiver>llvm-ar <ranlib>llvm-ranlib.exe ;
and later call
b2 toolset=clang-custom --user-config=path/to/config.jam ...
But, as I mentioned, prior to 1.66.0, `toolset=clang` is _hardcoded_ to use ar
& ranlib, these exact utility names. So either get them as part of some MinGW
distribution or build Boost using another toolset.
Now, it's all fine, but building stuff on Windows adds another thing into the
equation: debug runtimes. When you build Boost using MSVC, for example, it
picks one of the appropriate `/MT[d]` or `/MD[d]` flags to build the Boost
libraries with. Emulating these flags with `toolset=clang` is complicated and
inconvenient. Luckily, there's the clang-cl.exe executable, which aims to
provide command line interface compatible with that of cl.exe.
Boost.Build even supports `toolset=clang-win`, which should use clang-cl.exe.
But alas, it's completely broken prior to 1.69.0. It just doesn't work at all.
So, if you want to build w/ clang-cl.exe, either use Boost 1.69.0 or later, or
build using another toolset.
Cygwin & Clang
--------------
Now, a few words about Clang on Cygwin. When building 1.65.0, I encountered
the following error:
/usr/include/w32api/synchapi.h:127:26: error: conflicting types for 'Sleep'
WINBASEAPI VOID WINAPI Sleep (DWORD dwMilliseconds);
^
./boost/smart_ptr/detail/yield_k.hpp:64:29: note: previous declaration is here
extern "C" void __stdcall Sleep( unsigned long ms );
^
GCC doesn't emit an error here because /usr/include is in a pre-configured
"system" include directories list, and the declaration there take precedence, I
guess? The root of the problem BTW is that sizeof(unsigned long) is
* 4 for MSVC and MinGW-born GCCs,
* 8 for Clang (and, strangely, Cygwin GCC; why don't we get runtime
errors?).
The fix is to add `define=BOOST_USE_WINDOWS_H`. I don't even know what's the
point of not having it as a default.
|