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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
|
A short guide to compiling, linking, running and debugging issues
in the viewer and its packaged libraries.
Introduction
A recent pass through some third-party libraries resulted in the
collection of a lot of information about how things should and
shouldn't be built in the viewer. Some of that is presented below
with hints and rules about doing things well. What's presented is
a guideline only. Not all suggestions are hard rules and you'll
find exceptions all over. Some exceptions arise from solid
reasoning, others may be legacy that hasn't been re-examined.
Use good engineering judgement when applying this information.
Compilation
Windows Targets
Significant compilation flags and defines follow:
----------------------------------------------------------------------------
Option Release RelWithDebInfo Debug
----------------------------------------------------------------------------
wchar_t /Zc:wchar_t- " "
RTL type /MD /MD /MDd
FLoating Point /fp:fast " "
Debug Info /Zi (app/dll), /Z7 (lib) " "
Optimizer /O2 /Ob2 /GR /Od /Ob0 /GR /Od /GR
Incr. Link /INCREMENTAL:NO /INCREMENTAL /INCREMENTAL:NO
Debug /DEBUG /DEBUG /DEBUG
/OPT:REF
Ignore Libs LIBCMT LIBCMT LIBCMT;LIBCMTD;MSVCRT
Alignment Default " "
Defines WIN32 " "
_WINDOWS " "
LL_RELEASE=1 LL_RELEASE=1 n/a
LL_RELEASE_FOR_DOWNLOAD=1 n/a n/a
NDEBUG NDEBUG _DEBUG
n/a n/a LL_DEBUG=1
n/a LL_RELEASE_WITH_\ n/a
DEBUG_INFO=1
n/a n/a _SCL_SECURE_NO_WARNINGS=1
_SECURE_STL=0 _SECURE_STL=0 _SECURE_STL=0
_HAS_ITERATOR_DEBUGGING=0 n/a n/a
LL_WINDOWS=1 " "
UNICODE " "
_UNICODE " "
WINVER=0x0600 " "
_WIN32_WINNT=0x0600 " "
LL_OS_DRAGDROP_ENABLED=1 " "
LIB_NDOF=1 " "
----------------------------------------------------------------------------
Notes:
1. /Zc:wchar_t-. Not certain where this comes from. It may be
due to a default set of compilation flags in Qt 4.X that then
propagates outward. In Qt 5.X, this setting is flipped back to
default (wchar_t is a built-in). Other options for dealing with
this include:
http://msdn.microsoft.com/en-us/library/dh8che7s%28v=vs.110%29.aspx
Recommend trying to stay with /Zc:wchar_t (the default) when
adding libraries. If incompatible, you'll typically get some
missing ostream '<<' operators or something similar in the stream
headers.
2. /Z7 (VC 7.0 compatibility symbols) gives us debug information
in the static libraries we build. Otherwise builds generate
vc100.pdb files all over the place which generally aren't useful.
DLL's and .EXEs are to get /Zi or /ZI with separate .PDB files.
These .PDB files can then be packaged up in symbol tarballs for
the crash dump analyzer or used in debugging. There are issues here
for VS 2013 (see below).
Mac Targets
Fairly straightforward, optimization level is easily changed (may
be little or negative gain for -O3 and RelWithDebInfo should be
kicked up to 1 or 2. Boost debug symbols to dwarf-2 with a goal
of dwarf-2 in separate dSYM file when building .dylibs and
executables.
----------------------------------------------------------------------------
Option Release RelWithDebInfo Debug
----------------------------------------------------------------------------
Strip Debug Symbols On " "
During Copy
Generate Debug Syms On " "
Level Debug Syms -gdwarf-2 " "
Optimization -O3 -O0 -O0
PIC -fPIC -DPIC " "
Defines LL_RELEASE=1 LL_RELEASE=1 n/a
LL_RELEASE_FOR_DOWNLOAD=1 n/a n/a
NDEBUG NDEBUG _DEBUG
n/a n/a LL_DEBUG=1
n/a LL_RELEASE_WITH_\ n/a
DEBUG_INFO=1
LL_DARWIN=1 " "
LL_OS_DRAGDROP_ENABLED=1 " "
LIB_NDOF=1 " "
----------------------------------------------------------------------------
Notes:
1. We're also building dylibs in a somewhat unusual way. They're
currently being generated with a link path of
'@executable_path/../Resources/<library>'. If we were to follow
the recommendations in dyld's man page, we’d instead reference
'@loader_path/<library>', use -rpath on the executable link
(pointing to the 'Resources' subdir of the main executable), and
be able to avoid some symlinking in the .app tree.
2. Use the -headerpad_max_install_names link option on all .dylibs.
Linux Targets
Not much variety here.
----------------------------------------------------------------------------
Option Release RelWithDebInfo Debug
----------------------------------------------------------------------------
Debug Level -g (-g0/1 better?) -g -g
During Copy
Optimization -O2 -O0 -O0
PIC -fPIC " "
----------------------------------------------------------------------------
Notes:
Linking
The library update work has generally moved in the direction of
preferring static libraries over dynamic (Qt4 being the notable
exception). It also mostly eliminated the extremely bad practice
of having multiple versions of a library built into an image.
How bad was it? Very. Appalling. A nightmare. On Windows, at
least four versions of zlib (1.2.3, 1.2.5, 1.2.6, unknown), three
versions of Boost (1.45, 1.48, 1.52), two versions of OpenSSL
(0.9.8q, 1.0.0g) and three different builds of libexpat
2.0.5/1.5.2 were used. Mac was worse with five builds or versions
of zlib, two of PCRE, two of c-ares, and three of OpenSSL. Linux
topped that by adding two builds of libpng.
DO NOT ALLOW THIS TO HAPPEN AGAIN. It isn't enough to update a
library and then stuff a new triplet of S3 URLs into the viewer's
autobuild.xml. If you update a library you MUST:
* Update the autobuild.xml of ALL consumers of the library. In
the case of zlib, that meant updating freetype, libpng, openssl,
libxml2, fontconfig, curl, Boost, SDL, llqtwebkit, google-mock and
colladadom.
* Confirm by test and observation that the consumers actually use
your library rather than 'call home to mother' and find
system-supplied versions of your library. This may consist of
watching configuration scripts, probing with ldd/depends/otool,
pulling text out of binaries with 'strings'. The previously-
mentioned libraries all have a README.Linden file that gives
examples specific to the consumer library.
* DO NOT RE-EXPORT LIBRARIES. Colladadom was the worst offender
of this rule. As a shared library, it was re-exporting part, but
not all, of Boost filesystem and system, some zlib, some PCRE,
some libxml2 and minizip. This meant that depending upon link-
time and run-time symbol resolution, data constructed with one
version of a library might be processed by a method built in a
second, incompatible version of the library. Switching colladadom
to a static library ended the re-export problem.
* Preventing re-export is not sufficient. Other libraries will
still be shipped as shared and they can still have Singleton and
Fragile Base Class issues. A DLL may be built with a static
archive of a library that has global data. That same static
archive might be linked into the application proper. An object
created with a method in the DLL may pass into a method in the
application where the archive's global data has a second instance
and no knowledge of the object. This is a failure due to an
assumption of Singleton global data which leads to some kind of
failure. This is the same effect as when, in Windows, both MSVCRT
and MSVCRTD get activated in a program. If you're lucky, some
asserts fail in that case having to do with file handle global
data.
Running
Windows Debug Build. Seems to have been rendered nearly useless
by having the LL_CHECK_MEMORY define in llmemory.h calling
_CrtCheckMemory(). Viewer is almost useful disabling this in
llvoavatar code alone but not quite.
Futures
Static Versus Dynamic Libraries
One solution to the above linking problems is the use of static
libraries for everything. Single version, singleton instancing of
data, etc. But it's not the 1950's and we're not running our
applications on bare metal. Every platform comes with 100s of
libraries waiting to interfere with operations by breaking the
single-version and singleton-data assumption.
Additionally, there are libraries that simply expect to be built
into shared libraries. Qt4 is one such. The version we're using
now, 4.7.1, is actually trying to disable both Webkit and plugin
modules because we're building it statically on Mac and Linux.
It's only because of configuration bugs that we're getting the
functionality out of it that we want.
With enough libraries and a single, global namespace, eventually
there will be collisions and there may not be a warning. All it
takes is two programmers who thought that 'FILE * open_file(const
char *);' was a safe signature to use between compilation units in
their libraries and glorious debugging sessions are in your
future. Having debugged it, you will now become the proud owner
of a one-off version of one of those libraries that uses a special
symbol prefix which you will be maintaining forever.
Lastly, we have some binary blobs that we must use as delivered.
Executables can be isolated at run-time if necessary. Shared
libraries are a different problem. They may bring their own
library dependencies that affect link- and run-time symbol
resolution and they'll impose that on us according to platform
rules.
So, what to do? My natural bias for large software is to use
shared libraries for everything. It's a path to single-version
and singleton data and isolates namespaces and prevents
interactions. It also has some field serviceability benefits if
you need to debug some bizarre problem a user has.
But there's a local preference for static. Here, my
rules-of-thumb are:
* Static library used by default.
* Shared library where the library must be built shared.
* Shared library if that is the only means to enforce the
single-version and singleton-data requirements.
* Shared library *on a case-by-case basis* if the library is also
provided by the platform and some benefit is plausible. (An
example of this is freetype/fontconfig on Linux. The .so
versions we build with, and incompletely ship, are inferior in
behavior to the platform libraries. By being shared libraries,
the platform-supplied option is available to all Linux users.)
In all cases, beware of cmake which appears to collapse and move
library references in links. This can drastically affect symbol
resolution when there are multiple sources for a symbol.
General
VS 2013. The /Z7 flag is rumored to be somewhat broken in 2013.
But it also sounds like there are explicit controls to name .PDB
files associated with static archives. That would make this an
ideal time to switch to /Zi or /ZI everywhere with explicit naming
and bring all the .PDBs together.
The embedded browser technology (e.g. Qt4 with Webkit) is the
800-pound gorilla in the viewer. When starting any major work,
decide what changes you need here as those changes will propagate
outwards forcing many other decisions (cf: /Zc:wchar_t- flag).
The current package structure (./include, ./lib/release,
./lib/debug, etc.) really works against the conventions used by
configure-like programs. I wasted a lot of time getting each
library to work with our structure without having to go back to
automake/autoconf. For Linux and Mac (and even for Windows), a
structure like the following where each grouping is optional would
probably save some work:
./debug/bin
/include
/lib
./debug/shared/bin
/include
/lib
./debug/static/bin
/include
/lib
./release/bin
/include
/lib
...
In zlib and openssl and in a few of the libraries that consume
them, I experimented with packaging both static and shared
libraries and then having the consumer library move the unwanted
pieces out of the way to use the library type of choice (see
restore_dylibs() and restore_sos() functions). It was a bit fussy
and simplicity and clarity are the keys to maintaining libraries
in the future.
But it did suggest another approach. The idea is that every build
pre-stages inputs. Before anything is built, package pieces are
copied or symlinked from the 'stage/packages' area to the
'stage/input' area. Builds then proceed with a single set of
-I/-L options for the dependencies. And products are built and
installed in a similar output staging structure for the next
consumer:
stage/packages/<package>/[above structure]
stage/input/{bin,include,lib}
stage/<package>/[above structure]
Next library project. I'd recommend working on the related set of
libexpat, apr, aprutil, xmlrpc-epi. We know libexpat has some
updates that should improve stability. Libapr consumes it and it
could use some /Z7 flag work to get rid of some 1000's of PDB
warnings and improve our debug symbols.
Miscellany to be sorted out:
* The packaging of libfreetype and libfontconfig on Linux.
Determine what the right thing is, do it.
* Maybe do something with ICU4C. Qt5 will require it and a number
of our packages can consume it typically replacing iconv or some
other library. But it is a huge bolus of static data. It can be
trimmed, but still.
* Revisit openssl. Package as a shared library? Replace with
LibreSSL when available? Start using platform-supplied crypto?
|