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

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