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
|
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 |
|
199 | setglobal_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 |
|
245 | compile_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 |
|
307 | link() {
|
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 |
|
341 | compile_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 |
|
363 | strip_() {
|
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 |
|
378 | symlink() {
|
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
|
388 | if test $(basename $0) = 'ninja-rules-cpp.sh'; then
|
389 | "$@"
|
390 | fi
|