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 |
|
19 | set -o nounset
|
20 | set -o errexit
|
21 | # For /bin/sh portability
|
22 | #eval 'set -o pipefail'
|
23 |
|
24 | REPO_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
|
30 | if ! . _build/detected-config.sh; then
|
31 | die "Can't find _build/detected-config.sh. Run './configure'"
|
32 | fi
|
33 |
|
34 | line_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 |
|
44 | cxx='' # compiler
|
45 | flags='' # compile flags
|
46 | link_flags='' # link flags
|
47 |
|
48 | #
|
49 | # Functions to set them
|
50 | #
|
51 |
|
52 | setglobal_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 |
|
65 | setglobal_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 |
|
200 | setglobal_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 |
|
263 | compile_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 |
|
356 | link() {
|
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 |
|
400 | compile_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 |
|
422 | strip_() {
|
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 |
|
437 | symlink() {
|
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
|
447 | if test $(basename $0) = 'ninja-rules-cpp.sh'; then
|
448 | "$@"
|
449 | fi
|