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 |
|
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 | 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 |
|
203 | setglobal_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 |
|
267 | compile_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 |
|
362 | link() {
|
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 |
|
406 | strip_() {
|
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 |
|
421 | symlink() {
|
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
|
429 | if test $(basename $0) = 'ninja-rules-cpp.sh'; then
|
430 | "$@"
|
431 | fi
|