| 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
|