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