OILS / build / ninja-rules-cpp.sh View on Github | oils.pub

449 lines, 262 significant
1#!/usr/bin/env bash
2#
3# Actions invoked by build.ninja, which is generated by ./NINJA-config.sh.
4#
5# It's distributed with the tarball and invoked by _build/oils.sh, so
6# it's written in a /bin/sh style. But we're not using /bin/sh yet.
7#
8# And some non-Ninja wrappers.
9#
10# Usage:
11# build/ninja-rules-cpp.sh <function name>
12#
13# Env variables:
14# BASE_CXXFLAGS= default flags passed to all compiler invocations
15# CXXFLAGS= additional flags
16# OILS_CXX_VERBOSE=1 show compiler command lines
17# TIME_TSV_OUT=file compile_one and link output rows to this TSV file
18
19set -o nounset
20set -o errexit
21# For /bin/sh portability
22#eval 'set -o pipefail'
23
24REPO_ROOT=$(cd "$(dirname $0)/.."; pwd)
25
26. build/common.sh # for $BASE_CXXFLAGS
27. build/dev-shell.sh # python2 in $PATH
28
29# for HAVE_READLINE, READLINE_DIR, and STRIP_FLAGS
30if ! . _build/detected-config.sh; then
31 die "Can't find _build/detected-config.sh. Run './configure'"
32fi
33
34line_count() {
35 local out=$1
36 shift # rest are inputs
37 wc -l "$@" | sort -n | tee $out
38}
39
40#
41# Mutable GLOBALS
42#
43
44cxx='' # compiler
45flags='' # compile flags
46link_flags='' # link flags
47
48#
49# Functions to set them
50#
51
52setglobal_cxx() {
53 local compiler=$1
54
55 case $compiler in
56 (clang) cxx=$CLANGXX ;;
57 # Note: we could get rid of this "alias", and use 'c++' everywhere
58 (cxx) cxx='c++' ;;
59
60 # e.g. could be cosmoc++
61 (*) cxx=$compiler ;;
62 esac
63}
64
65setglobal_compile_flags() {
66 ### Set flags based on $variant $more_cxx_flags and $dotd
67
68 local variant=$1
69 local more_cxx_flags=$2 # e.g. NINJA_subgraph.py sets -D CPP_UNIT_TEST
70 local dotd=${3:-}
71
72 # flags from Ninja/shell respected
73 flags="$BASE_CXXFLAGS -I $REPO_ROOT $more_cxx_flags"
74
75 # flags needed to strip unused symbols
76 # TODO: should these go in BASE_CXXFLAGS?
77 #
78 # https://stackoverflow.com/questions/6687630/how-to-remove-unused-c-c-symbols-with-gcc-and-ld
79 flags="$flags -fdata-sections -ffunction-sections"
80 # Note: -ftlo doesn't do anything for size?
81
82 case $variant in
83 *+bumpleak|*+bumproot)
84 ;;
85 *+bigint)
86 flags="$flags -D MARK_SWEEP -D BIGINT"
87 ;;
88 *)
89 flags="$flags -D MARK_SWEEP"
90 ;;
91 esac
92
93 # First half of variant: what affects ALL translation units
94
95 case $variant in
96 dbg*)
97 flags="$flags -O0 -g"
98 ;;
99
100 asan*)
101 # CLEAN_PROCESS_EXIT avoids spurious memory leaks
102 flags="$flags -O0 -g -fsanitize=address -D CLEAN_PROCESS_EXIT"
103 ;;
104
105 tsan*)
106 flags="$flags -O0 -g -fsanitize=thread"
107 ;;
108
109 ubsan*)
110 # Extra flag to make it fatal
111 # https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
112
113 flags="$flags -O0 -g -fsanitize=undefined -fno-sanitize-recover=null"
114 ;;
115
116 opt*)
117 flags="$flags -O2 -g -D OPTIMIZED"
118 ;;
119
120 coverage*)
121 # source-based coverage is more precise than say sanitizer-based
122 # https://clang.llvm.org/docs/SourceBasedCodeCoverage.html
123 flags="$flags -O0 -g -fprofile-instr-generate -fcoverage-mapping"
124 ;;
125
126 uftrace*)
127 # -O0 creates a A LOT more data. But sometimes we want to see the
128 # structure of the code.
129 # NewStr(), OverAllocatedStr(), StrFromC() etc. are not inlined
130 # Ditto vector::size(), std::forward, len(), etc.
131
132 local opt='-O0'
133 #local opt='-O2'
134 flags="$flags $opt -g -pg"
135 ;;
136
137 (*)
138 die "Invalid variant $variant"
139 ;;
140 esac
141
142 # for cxx-dbg32, cxx-opt32+bumpleak, etc.
143 case $variant in
144 *32*)
145 flags="$flags -m32"
146 ;;
147 esac
148
149 # OPTIONAL second half of variant: for the application
150
151 case $variant in
152 *+gcalways)
153 flags="$flags -D GC_ALWAYS"
154 ;;
155
156 *+tcmalloc)
157 flags="$flags -D TCMALLOC"
158 ;;
159
160 *+bumpleak)
161 flags="$flags -D BUMP_LEAK"
162 ;;
163 *+bumproot)
164 flags="$flags -D BUMP_LEAK -D BUMP_ROOT"
165 ;;
166
167 *+bumpsmall)
168 # the pool allocator should approximate opt+bumpsmall (which doesn't support GC)
169 flags="$flags -D BUMP_ROOT -D BUMP_SMALL -D NO_POOL_ALLOC"
170 ;;
171
172 *+nopool)
173 flags="$flags -D NO_POOL_ALLOC"
174 ;;
175 esac
176
177 # Detected by ./configure
178 if test -n "$READLINE_DIR"; then
179 flags="$flags -I${READLINE_DIR}/include"
180 fi
181
182 # Flags from env
183 # Similar to
184 # - GNU make - https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html
185 # CXXFLAGS "Extra flags to give to the C++ compiler"
186 # - CMake - https://cmake.org/cmake/help/latest/envvar/CXXFLAGS.html
187 # "Add default compilation flags to be used when compiling CXX (C++) files."
188
189 local env_flags=${CXXFLAGS:-}
190 if test -n "$env_flags"; then
191 flags="$flags $env_flags"
192 fi
193
194 # https://ninja-build.org/manual.html#ref_headers
195 if test -n "$dotd"; then
196 flags="$flags -MD -MF $dotd"
197 fi
198}
199
200setglobal_link_flags() {
201 local variant=$1
202 local more_link_flags=${2:-} # from NINJA_subgraph.py, e.g. Souffle datalog
203
204 link_flags="$more_link_flags" # initialize
205
206 # Linker flags based on build variant
207 local variant_flags=''
208 case $variant in
209 # Must REPEAT these flags, otherwise we lose sanitizers / coverage
210 asan*)
211 variant_flags='-fsanitize=address'
212 ;;
213
214 tcmalloc)
215 # Need to tell the dynamic loader where to find tcmalloc
216 variant_flags='-ltcmalloc -Wl,-rpath,/usr/local/lib'
217 ;;
218
219 tsan)
220 variant_flags='-fsanitize=thread'
221 ;;
222 ubsan*)
223 variant_flags='-fsanitize=undefined'
224 ;;
225 coverage*)
226 variant_flags='-fprofile-instr-generate -fcoverage-mapping'
227 ;;
228 esac
229
230 if test -n "$variant_flags"; then
231 link_flags="$link_flags $variant_flags"
232 fi
233
234 # More build variant flags, and GNU readline flags
235 case $variant in
236 # TODO: 32-bit variants can't handle -l readline right now.
237 *32*)
238 link_flags="$link_flags -m32"
239 ;;
240
241 *)
242 if test "$HAVE_READLINE" = 1; then
243 link_flags="$link_flags -lreadline"
244 fi
245 if test -n "$READLINE_DIR"; then
246 link_flags="$link_flags -L${READLINE_DIR}/lib"
247 fi
248 ;;
249 esac
250
251 # Detected by ./configure
252 if test -n "${STRIP_FLAGS:-}"; then
253 link_flags="$link_flags -Wl,$STRIP_FLAGS"
254 fi
255
256 # Set by user/packager
257 local env_flags=${LDFLAGS:-}
258 if test -n "$env_flags"; then
259 link_flags="$link_flags $env_flags"
260 fi
261}
262
263compile_one() {
264 ### Compile one translation unit. Invoked by build.ninja
265
266 local compiler=$1
267 local variant=$2
268 local more_cxx_flags=$3
269 local in=$4
270 local out=$5
271 local dotd=${6:-} # optional .d file
272
273 setglobal_compile_flags "$variant" "$more_cxx_flags" "$dotd"
274
275 case $out in
276 _build/preprocessed/*)
277 flags="$flags -E"
278 ;;
279
280 # DISABLE spew for mycpp-generated code. mycpp/pea could flag this at the
281 # PYTHON level, rather than doing it at the C++ level.
282 _build/obj/*/_gen/bin/oils_for_unix.mycpp.o)
283 flags="$flags -Wno-unused-variable -Wno-unused-but-set-variable"
284 ;;
285 esac
286
287 if test "$compiler" = 'clang'; then
288 # 2024-08 - Clang needs -stdlib=libc++ for some reason
289 # https://stackoverflow.com/questions/26333823/clang-doesnt-see-basic-headers
290 # https://stackoverflow.com/questions/19774778/when-is-it-necessary-to-use-the-flag-stdlib-libstdc
291
292 # But don't do it for clang-coverage* builds, because the CI machine
293 # doesn't like it? This makes it fail on the release machine - sigh
294 case $variant in
295 coverage*) ;; # include coverage+bumpleak
296 *) flags="$flags -stdlib=libc++" ;;
297 esac
298
299 # TODO: exactly when is -fPIC needed? Clang needs it sometimes?
300 if test $variant != 'opt'; then
301 flags="$flags -fPIC"
302 fi
303 fi
304
305 # this flag is only valid in Clang, doesn't work in continuous build
306 if test "$compiler" = 'clang'; then
307 flags="$flags -ferror-limit=10"
308 fi
309
310 setglobal_cxx $compiler
311
312 # The command we want to run. Not using arrays because this is POSIX shell
313 set -- "$cxx" $flags -o "$out" -c "$in"
314
315 if test -n "${OILS_CXX_VERBOSE:-}"; then
316 echo '__' "$@" >&2
317 fi
318
319 # Maybe add timing prefix
320 if test -n "${TIME_TSV_OUT:-}"; then
321 set -- \
322 benchmarks/time_.py --tsv \
323 --out "$TIME_TSV_OUT" --append \
324 --rusage --field compile_one --field $out -- \
325 "$@"
326 else
327 # Show timing info on the most expensive translation unit
328 case $in in
329 */oils_for_unix.*)
330 # Must have external 'time', and it must have -f
331 # Notes: OS X has no time -f
332 # busybox time supports -f but not --format.
333 case ${BASH_VERSION:-} in
334 3*)
335 # bash 3.2.x on macos has a bizarre bug that leads to weird
336 # interactions between errexit and `command time -f` when this
337 # function gets run as a background job (like it does in the
338 # tarball build script). Don't bother timing in this case.
339 ;;
340 *)
341 if command time -f '%e %M' true >/dev/null; then
342 set -- \
343 time -f "$out { elapsed: %e, max_RSS: %M }" -- \
344 "$@"
345 fi
346 ;;
347 esac
348 ;;
349 esac
350 fi
351
352 # Run the command
353 "$@"
354}
355
356link() {
357 ### Link a binary. Invoked by build.ninja
358
359 local compiler=$1
360 local variant=$2
361 local more_link_flags=$3 # Used by Souffle datalog rules
362 local out=$4
363 shift 4
364 # rest are inputs
365
366 setglobal_link_flags "$variant" "$more_link_flags"
367
368 setglobal_cxx "$compiler"
369
370 if test "$compiler" = 'clang'; then
371 case $variant in
372 coverage*) ;; # include coverage+bumpleak
373 *) link_flags="$link_flags -stdlib=libc++"
374 esac
375 fi
376
377 # The command we want to run. Not using arrays because this is POSIX shell
378 #
379 # IMPORTANT: Flags like -ltcmalloc have to come AFTER objects! Weird but
380 # true.
381 set -- "$cxx" -o "$out" "$@" $link_flags
382
383 if test -n "${OILS_CXX_VERBOSE:-}"; then
384 echo '__' "$@" >&2
385 fi
386
387 # Maybe add timing prefix
388 if test -n "${TIME_TSV_OUT:-}"; then
389 set -- \
390 benchmarks/time_.py --tsv \
391 --out "$TIME_TSV_OUT" --append \
392 --rusage --field link --field $out -- \
393 "$@"
394 fi
395
396 # Run the command
397 "$@"
398}
399
400compile_and_link() {
401 ### This function is no longer used; use 'compile_one' and 'link'
402
403 local compiler=$1
404 local variant=$2
405 local more_cxx_flags=$3
406 local out=$4
407 shift 4
408
409 setglobal_compile_flags "$variant" "$more_cxx_flags" "" # no dotd
410
411 setglobal_link_flags $variant
412
413 setglobal_cxx $compiler
414
415 if test -n "${OILS_CXX_VERBOSE:-}"; then
416 echo "__ $cxx -o $out $flags $@ $link_flags" >&2
417 fi
418
419 "$cxx" -o "$out" $flags "$@" $link_flags
420}
421
422strip_() {
423 ### Invoked by ninja
424
425 local in=$1
426 local stripped=$2
427 local symbols=${3:-}
428
429 strip -o $stripped $in
430
431 if test -n "$symbols"; then
432 objcopy --only-keep-debug $in $symbols
433 objcopy --add-gnu-debuglink=$symbols $stripped
434 fi
435}
436
437symlink() {
438 local dir=$1
439 local in=$2
440 local out=$3
441
442 cd $dir
443 ln -s -f -v $in $out
444}
445
446# test/cpp-unit.sh sources this
447if test $(basename $0) = 'ninja-rules-cpp.sh'; then
448 "$@"
449fi