OILS / build / ninja-rules-cpp.sh View on Github | oilshell.org

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