diff options
Diffstat (limited to 'build')
| -rwxr-xr-x | build | 341 |
1 files changed, 341 insertions, 0 deletions
| @@ -0,0 +1,341 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | # Build system for nissy, run './build help' for info on how to use this. | ||
| 4 | |||
| 5 | # The variables below can be used to personalize the build system. For example, | ||
| 6 | # to compile with clang instead of the default cc one can use: | ||
| 7 | # | ||
| 8 | # CC=clang ./build | ||
| 9 | # | ||
| 10 | # Each variable also has a counterpart that starts with NISSY_BUILD_*. These | ||
| 11 | # other variables can be set, for example, in your shell configuration file | ||
| 12 | # to change the default build options. They are still overwritten by the values | ||
| 13 | # of the non-NISSY_BUILD_* variables, if set. | ||
| 14 | # For example, on a system that supports the addressed and undefined behavior | ||
| 15 | # sanitizers, one can set | ||
| 16 | # | ||
| 17 | # export NISSY_BUILD_SANITIZE="address,undefined" | ||
| 18 | # | ||
| 19 | # so that these sanitizers will be enabled when building nissy in debug mode. | ||
| 20 | # If later the same user wants to build a debug version without sanitizers, | ||
| 21 | # they may use | ||
| 22 | # | ||
| 23 | # SANITIZE="" ./build debug | ||
| 24 | # | ||
| 25 | # And the empty string value will take precedence. | ||
| 26 | |||
| 27 | # Specify the compiler to use. | ||
| 28 | # The default value is "cc". | ||
| 29 | CC=${CC-${NISSY_BUILD_CC}} | ||
| 30 | |||
| 31 | # Specify the compiler to use when building WASM target. | ||
| 32 | # The default value is "emcc". | ||
| 33 | EMCC=${EMCC-${NISSY_BUILD_EMCC}} | ||
| 34 | |||
| 35 | # The maximum number of threads to use for multi-threaded operations. | ||
| 36 | # This is also used as default value in case an operation allows | ||
| 37 | # specifying how many threads to use. | ||
| 38 | # The number n must be between 1 and 128. | ||
| 39 | # The default value is 16. | ||
| 40 | # (TODO: in the future this will be determined base on the system). | ||
| 41 | THREADS=${THREADS-${NISSY_BUILD_THREADS}} | ||
| 42 | |||
| 43 | # You can use this variable to build for a different architecture, for example | ||
| 44 | # if you want to cross-compile or to use the portable version. | ||
| 45 | # The value must be one of "AVX2", "NEON", "PORTABLE". If the value is not | ||
| 46 | # specified, the script will automatically detect the best architecture for the | ||
| 47 | # system. The architecture "PORTABLE" will work on any system. | ||
| 48 | ARCH=${ARCH-${NISSY_BUILD_ARCH}} | ||
| 49 | |||
| 50 | # SANITIZE="option1,option2,..." adds the options "-fsanitize=option1", | ||
| 51 | # "-fsanitize=option2", ... to the C compiler command in debug mode. | ||
| 52 | # By default none is used because these options are not available on all | ||
| 53 | # systems. It is advisable to set the NISSY_BUILD_SANITIZE options in your | ||
| 54 | # shell's configuration file if your system does support them. | ||
| 55 | SANITIZE=${SANITIZE-${NISSY_BUILD_SANITIZE}} | ||
| 56 | |||
| 57 | # Optimization flags. By default, -O3 is used. | ||
| 58 | OPTIMIZE=${OPTIMIZE-${NISSY_BUILD_OPTIMIZE}} | ||
| 59 | |||
| 60 | detectthreads() { | ||
| 61 | # TODO: detect based on system | ||
| 62 | # Is 'getconf NPROCESSORD_ONLN' portable? Is it threads or cores? | ||
| 63 | echo "16" | ||
| 64 | } | ||
| 65 | |||
| 66 | greparch() { | ||
| 67 | $CC -march=native -dM -E - </dev/null 2>/dev/null | grep "$1" | ||
| 68 | } | ||
| 69 | |||
| 70 | detectarch() { | ||
| 71 | [ -n "$(greparch __AVX2__)" ] && detected="AVX2" | ||
| 72 | [ -n "$(greparch __ARM_NEON)" ] && detected="NEON" | ||
| 73 | [ -z "$detected" ] && detected="PORTABLE" | ||
| 74 | |||
| 75 | echo "$detected" | ||
| 76 | } | ||
| 77 | |||
| 78 | validatecc() { | ||
| 79 | if ! (command -v "$CC" >/dev/null 2>&1) ; then | ||
| 80 | echo "Error: compiler '$CC' not found" | ||
| 81 | exit 1 | ||
| 82 | fi | ||
| 83 | } | ||
| 84 | |||
| 85 | validatethreads() { | ||
| 86 | if [ "$THREADS" -le 0 ] || [ "$THREADS" -gt 128 ]; then | ||
| 87 | echo "Error: number of threads must be between 1 and 128" | ||
| 88 | exit 1 | ||
| 89 | fi | ||
| 90 | } | ||
| 91 | |||
| 92 | validatearch() { | ||
| 93 | case "$ARCH" in | ||
| 94 | AVX2|NEON|PORTABLE) | ||
| 95 | ;; | ||
| 96 | "") | ||
| 97 | ARCH=$(detectarch) | ||
| 98 | echo "Selected architecture '$ARCH'" | ||
| 99 | ;; | ||
| 100 | *) | ||
| 101 | echo "Error: architecture '$ARCH' not supported" | ||
| 102 | echo "Supported architectures: AVX2, NEON, PORTABLE" | ||
| 103 | exit 1 | ||
| 104 | ;; | ||
| 105 | esac | ||
| 106 | } | ||
| 107 | |||
| 108 | parsesanitize() { | ||
| 109 | # Use the user-specified comma-separated sanitizers | ||
| 110 | for opt in $(echo "$1" | tr ',' '\n'); do | ||
| 111 | printf -- '-fsanitize=%s ' "$opt" | ||
| 112 | done | ||
| 113 | } | ||
| 114 | |||
| 115 | # Set variables to default values if unset | ||
| 116 | CC=${CC:-"cc"} | ||
| 117 | EMCC=${EMCC:-"emcc"} | ||
| 118 | THREADS=${THREADS:-"$(detectthreads)"} | ||
| 119 | ARCH=${ARCH:-"$(detectarch)"} | ||
| 120 | OPTIMIZE=${OPTIMIZE:-"-O3"} | ||
| 121 | |||
| 122 | # Validate variales | ||
| 123 | validatecc | ||
| 124 | validatethreads | ||
| 125 | validatearch | ||
| 126 | |||
| 127 | CFLAGS="-std=c11 -fPIC -D_POSIX_C_SOURCE=199309L -lpthread" | ||
| 128 | [ "$ARCH" = "AVX2" ] && CFLAGS="$CFLAGS -mavx2" | ||
| 129 | WFLAGS="-pedantic -Wall -Wextra -Wno-unused-parameter -Wno-unused-function" | ||
| 130 | OFLAGS="$OPTIMIZE" | ||
| 131 | DFLAGS="-DDEBUG -g3 $(parsesanitize "$SANITIZE")" | ||
| 132 | MFLAGS="-DTHREADS=$THREADS -D$ARCH" | ||
| 133 | WASMCFLAGS="-std=c11 -fPIC -D_POSIX_C_SOURCE=199309L" | ||
| 134 | WASMMFLAGS="-DTHREADS=$THREADS -DWASMSIMD" | ||
| 135 | |||
| 136 | if (command -v "python3-config" >/dev/null 2>&1) ; then | ||
| 137 | PYTHON3_INCLUDES="$(python3-config --includes)" | ||
| 138 | PYTHON3="version $(echo "$PYTHON3_INCLUDES" | sed 's/.*3\./3./')" | ||
| 139 | else | ||
| 140 | PYTHON3_INCLUDES="" | ||
| 141 | PYTHON3="Not found, Python shell won't be available" | ||
| 142 | fi | ||
| 143 | |||
| 144 | build_help() { | ||
| 145 | echo "Build system for nissy" | ||
| 146 | echo "" | ||
| 147 | echo "usage:" | ||
| 148 | echo "$0 TARGET # build the given TARGET" | ||
| 149 | echo "$0 # same as '$0 nissy'" | ||
| 150 | echo "$0 test [PATTERN] # run unit tests (optionally matching PATTERN) | ||
| 151 | echo "$0 tool PATTERN # run the tool matching PATTERN | ||
| 152 | echo "" | ||
| 153 | echo "targets:" | ||
| 154 | echo "" | ||
| 155 | echo "nissy Build the nissy.o object file" | ||
| 156 | echo "lib Build the static library libnissy.a" | ||
| 157 | echo "sharedlib Build the shared library libnissy.so" | ||
| 158 | echo "python Build the Python module for nissy" | ||
| 159 | echo "shell Build a basic nissy shell (./run)" | ||
| 160 | echo "wasm Build the WebAssembly module for nissy" | ||
| 161 | echo "" | ||
| 162 | echo "help Show this help message" | ||
| 163 | echo "config Show build configuration and exit" | ||
| 164 | echo "clean Remove all build files" | ||
| 165 | } | ||
| 166 | |||
| 167 | build_config() { | ||
| 168 | echo "Compiler: $CC" | ||
| 169 | echo "Architecture: $ARCH" | ||
| 170 | echo "Max threads: $THREADS" | ||
| 171 | echo "Optimization options: $OFLAGS" | ||
| 172 | echo "Debug flags: $DFLAGS" | ||
| 173 | echo "WASM compiler: $EMCC" | ||
| 174 | echo "Python bindings: $PYTHON3" | ||
| 175 | } | ||
| 176 | |||
| 177 | run() { | ||
| 178 | echo "$@" | ||
| 179 | $@ | ||
| 180 | } | ||
| 181 | |||
| 182 | odflags() { | ||
| 183 | if [ "$debug" = "yes" ]; then | ||
| 184 | echo "$DFLAGS" | ||
| 185 | else | ||
| 186 | echo "$OFLAGS" | ||
| 187 | fi | ||
| 188 | } | ||
| 189 | |||
| 190 | build_clean() { | ||
| 191 | run rm -rf -- *.o *.so *.a run runtest runtool | ||
| 192 | } | ||
| 193 | |||
| 194 | build_nissy() { | ||
| 195 | run $CC $CFLAGS $WFLAGS $MFLAGS $(odflags) -c -o nissy.o src/nissy.c | ||
| 196 | } | ||
| 197 | |||
| 198 | build_lib() { | ||
| 199 | build_nissy | ||
| 200 | run ar rcs libnissy.a nissy.o | ||
| 201 | } | ||
| 202 | |||
| 203 | build_sharedlib() { | ||
| 204 | run $CC $CFLAGS $WFLAGS $MFLAGS $(odflags) -shared -c -o nissy.o \ | ||
| 205 | src/nissy.c | ||
| 206 | } | ||
| 207 | |||
| 208 | build_shell() { | ||
| 209 | build_nissy | ||
| 210 | run $CC $CFLAGS $WFLAGS $(odflags) -o run nissy.o shell/shell.c | ||
| 211 | } | ||
| 212 | |||
| 213 | build_python() { | ||
| 214 | if [ -z "$PYTHON3_INCLUDES" ]; then | ||
| 215 | echo "Python3 development headers could not be located" | ||
| 216 | echo "Cannot build python module" | ||
| 217 | exit 1 | ||
| 218 | fi | ||
| 219 | build_nissy | ||
| 220 | run $CC $CFLAGS $WFLAGS $PYTHON3_INCLUDES $(odflags) -shared \ | ||
| 221 | -o nissy_pthon_module.so nissy.o python/nissy_module.c | ||
| 222 | } | ||
| 223 | |||
| 224 | build_wasm() { | ||
| 225 | run $EMCC $WASMCFLAGS $WFLAGS $WASMMFLAGS $(odflags) -c \ | ||
| 226 | -o wasmnissy.o src/nissy.c | ||
| 227 | } | ||
| 228 | |||
| 229 | dotest() { | ||
| 230 | testout="test/last.out" | ||
| 231 | testerr="test/last.err" | ||
| 232 | |||
| 233 | # Verify that $t is a directory and it starts with three digits | ||
| 234 | if [ ! -d "$t" ] || ! (basename "$t" | grep -Eq '^[0-9]{3}'); then | ||
| 235 | return 0 | ||
| 236 | fi | ||
| 237 | |||
| 238 | # Verify that the test is selected by the given pattern | ||
| 239 | if [ -n "$pattern" ] && ! (echo "$t" | grep -q "$pattern"); then | ||
| 240 | return 0 | ||
| 241 | fi | ||
| 242 | |||
| 243 | $CC $CFLAGS $WFLAGS $DFLAGS $MFLAGS -o runtest "$t"/*.c nissy.o \ | ||
| 244 | || exit 1 | ||
| 245 | for cin in "$t"/*.in; do | ||
| 246 | c="$(echo "$cin" | sed 's/\.in//')" | ||
| 247 | cout="$c.out" | ||
| 248 | printf '%s: ' "$c" | ||
| 249 | ./runtest < "$cin" > "$testout" 2> "$testerr" | ||
| 250 | if diff "$cout" "$testout"; then | ||
| 251 | printf "OK\n" | ||
| 252 | else | ||
| 253 | printf "Test failed! stderr:\n" | ||
| 254 | cat "$testerr" | ||
| 255 | exit 1 | ||
| 256 | fi | ||
| 257 | done | ||
| 258 | } | ||
| 259 | |||
| 260 | build_test() { | ||
| 261 | pattern="$1" | ||
| 262 | shift | ||
| 263 | debug="yes" | ||
| 264 | build_nissy | ||
| 265 | for t in test/*; do | ||
| 266 | dotest || exit 1 | ||
| 267 | done | ||
| 268 | echo "All tests passed!" | ||
| 269 | } | ||
| 270 | |||
| 271 | tool_usage() { | ||
| 272 | echo "usage: ./build tool PATTERN" | ||
| 273 | } | ||
| 274 | |||
| 275 | build_tool() { | ||
| 276 | pattern="$1" | ||
| 277 | shift | ||
| 278 | if [ -z "$pattern" ]; then | ||
| 279 | tool_usage | ||
| 280 | exit 1 | ||
| 281 | fi | ||
| 282 | |||
| 283 | build_nissy | ||
| 284 | |||
| 285 | # Select a single tool matched by the given pattern | ||
| 286 | for t in tools/*; do | ||
| 287 | if [ -d "$t" ] && (echo "$t" | grep -q "$pattern"); then | ||
| 288 | toolname="$(basename "$t" .c)" | ||
| 289 | break | ||
| 290 | fi | ||
| 291 | done | ||
| 292 | if [ -z "$toolname" ]; then | ||
| 293 | tool_usage | ||
| 294 | exit 1 | ||
| 295 | fi | ||
| 296 | |||
| 297 | results="tools/results" | ||
| 298 | last="$results/last.out" | ||
| 299 | date="$(date + '%Y-%m-%d-%H-%M-%S')" | ||
| 300 | file="$results/$toolname-$date.txt" | ||
| 301 | |||
| 302 | $CC $CFLAGS $WFLAGS $MFLAGS $(odflags) -o runtool "$t"/*.c nissy.o \ | ||
| 303 | || exit 1 | ||
| 304 | |||
| 305 | ( | ||
| 306 | date +'%Y-%m-%d %H:%M' | ||
| 307 | echo "" | ||
| 308 | echo "=========== Running tool ===========" | ||
| 309 | echo "tool name: $toolname" | ||
| 310 | echo "" | ||
| 311 | echo "======== nissy build command ========" | ||
| 312 | echo "$CC $CFLAGS $WFLAGS $MFLAGS $(odflags)" | ||
| 313 | echo "" | ||
| 314 | echo "======== tool build command ========" | ||
| 315 | echo "$CC $CFLAGS $WFLAGS $MFLAGS $(odflags)" | ||
| 316 | echo "" | ||
| 317 | echo "============ tool output ============" | ||
| 318 | ./runtool $@ | ||
| 319 | ) | tee "$file" "$last" | ||
| 320 | } | ||
| 321 | |||
| 322 | if [ "$1" = "-d" ]; then | ||
| 323 | debug="yes" | ||
| 324 | shift | ||
| 325 | fi | ||
| 326 | |||
| 327 | target=${1:-"nissy"} | ||
| 328 | shift | ||
| 329 | |||
| 330 | case "$target" in | ||
| 331 | help|config|clean|\ | ||
| 332 | nissy|lib|sharedlib|shell|python|wasm|test|tool) | ||
| 333 | mkdir -p tables tools/results | ||
| 334 | (build_"$target" $@) || exit 1 | ||
| 335 | exit 0 | ||
| 336 | ;; | ||
| 337 | *) | ||
| 338 | echo "Target '$target' unavailable, run '$0 help' for info" | ||
| 339 | exit 1 | ||
| 340 | ;; | ||
| 341 | esac | ||
