nissy-core

The "engine" of nissy, including the H48 optimal solver.
git clone https://git.tronto.net/nissy-core
Download | Log | Files | Refs | README | LICENSE

build (10396B)


      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 C compiler to use.
     28 CC=${CC-${NISSY_BUILD_CC:-"cc"}}
     29 
     30 # Specify the C++ compiler for the C++ examples.
     31 CXX=${CXX-${NISSY_BUILD_CXX:-"c++"}}
     32 
     33 # Specify the compiler to use when building for WASM.
     34 EMCC=${EMCC-${NISSY_BUILD_EMCC:-"emcc"}}
     35 
     36 # Specify the nodejs command for running tests for WASM.
     37 NODE=${NODE-${NISSY_BUILD_NODE:-"node"}}
     38 
     39 # The maximum number of threads to use for multi-threaded operations.
     40 # This is also used as default value in case an operation allows
     41 # specifying how many threads to use.
     42 # The number n must be between 1 and 128.
     43 # The default value is 16.
     44 THREADS=${THREADS-${NISSY_BUILD_THREADS}}
     45 
     46 detectthreads() {
     47 	# TODO: detect based on system
     48 	# Is 'getconf NPROCESSORD_ONLN' portable? Is it threads or cores?
     49 	echo "16"
     50 }
     51 
     52 [ -z "$THREADS" ] && THREADS="$(detectthreads)"
     53 
     54 # You can use this variable to build for a different architecture, for example
     55 # if you want to cross-compile or to use the portable version.
     56 # The value must be one of "AVX2", "NEON", "PORTABLE". If the value is not
     57 # specified, the script will automatically detect the best architecture for the
     58 # system. The architecture "PORTABLE" will work on any system.
     59 ARCH=${ARCH-${NISSY_BUILD_ARCH}}
     60 
     61 greparch() {
     62 	$CC -march=native -dM -E - </dev/null 2>/dev/null | grep "$1"
     63 }
     64 
     65 detectarch() {
     66 	[ -n "$(greparch __AVX2__)" ] && detected="AVX2"
     67 	[ -n "$(greparch __ARM_NEON)" ] && detected="NEON"
     68 	[ -z "$detected" ] && detected="PORTABLE"
     69 	echo "$detected"
     70 }
     71 
     72 [ -z "$ARCH" ] && ARCH="$(detectarch)"
     73 
     74 # SANITIZE="option1,option2,..." adds the options "-fsanitize=option1",
     75 # "-fsanitize=option2", ... to the C compiler command in debug mode.
     76 # By default none is used because these options are not available on all
     77 # systems. It is advisable to set the NISSY_BUILD_SANITIZE options in your
     78 # shell's configuration file if your system does support them.
     79 SANITIZE=${SANITIZE-${NISSY_BUILD_SANITIZE:-""}}
     80 
     81 # Optimization flags.
     82 OPTIMIZE=${OPTIMIZE-${NISSY_BUILD_OPTIMIZE:-"-O3"}}
     83 
     84 validate_command() {
     85 	command="$1"
     86 	if ! (command -v "$command" >/dev/null 2>&1) ; then
     87 		echo "Error: command '$command' not found"
     88 		exit 1
     89 	fi
     90 }
     91 
     92 validate_threads() {
     93 	threads="$1"
     94 	if [ "$threads" -le 0 ] || [ "$threads" -gt 128 ]; then
     95 		echo "Error: number of threads must be between 1 and 128"
     96 		exit 1
     97 	fi
     98 }
     99 
    100 validate_arch() {
    101 	arch="$1"
    102 	case "$arch" in
    103 	AVX2|NEON|PORTABLE)
    104 		;;
    105 	*)
    106 		echo "Error: architecture '$arch' not supported"
    107 		echo "Supported architectures: AVX2, NEON, PORTABLE"
    108 		exit 1
    109 		;;
    110 	esac
    111 }
    112 
    113 parsesanitize() {
    114 	# Use the user-specified comma-separated sanitizers
    115 	for opt in $(echo "$1" | tr ',' '\n'); do
    116 		printf -- '-fsanitize=%s ' "$opt"
    117 	done
    118 }
    119 
    120 # Build flags
    121 CFLAGS="-std=c11 -fPIC -D_POSIX_C_SOURCE=199309L -pthread"
    122 [ "$ARCH" = "AVX2" ] && CFLAGS="$CFLAGS -mavx2"
    123 WFLAGS="-pedantic -Wall -Wextra -Wno-unused-parameter -Wno-unused-function"
    124 OFLAGS="$OPTIMIZE"
    125 DFLAGS="-DDEBUG -g3 $(parsesanitize "$SANITIZE")"
    126 MFLAGS="-DTHREADS=$THREADS -D$ARCH"
    127 CPPFLAGS="-std=c++20 -pthread"
    128 
    129 # Build flags for emscripten (WASM target)
    130 WASMCFLAGS="-std=c11 -fPIC -D_POSIX_C_SOURCE=199309L -pthread"
    131 WASMMFLAGS="-DTHREADS=$THREADS -DWASMSIMD"
    132 WASMLINKFLAGS="--no-entry -sEXPORT_NAME='Nissy' -sMODULARIZE \
    133 	-sALLOW_MEMORY_GROWTH -sSTACK_SIZE=5MB -sPTHREAD_POOL_SIZE=$THREADS \
    134 	-sLINKABLE -sEXPORT_ALL"
    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. Usage:"
    146 	echo ""
    147 	echo "$0 TARGET             # build the given TARGET"
    148 	echo "$0                    # same as '$0 nissy'"
    149 	echo "$0 test [PATTERN]     # run unit tests (matching PATTERN)"
    150 	echo "$0 webtest [PATTERN]  # same as test, but for WASM build"
    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 "web         Build the WebAssembly / JavaScript module for nissy"
    161 	echo "cpp FILES   Build and run the given FILES including cpp/nissy.h"
    162 	echo ""
    163 	echo "help        Show this help message"
    164 	echo "config      Show build configuration and exit"
    165 	echo "clean       Remove all build files"
    166 }
    167 
    168 build_config() {
    169 	echo "Compiler: $CC"
    170 	echo "Architecture: $ARCH"
    171 	echo "Max threads: $THREADS"
    172 	echo "Optimization options: $OFLAGS"
    173 	echo "Debug flags: $DFLAGS"
    174 	echo ""
    175 
    176 	echo "Optional features:"
    177 
    178 	printf '%s' "WASM compiler: "
    179 	if (validate_command "$EMCC" >/dev/null); then
    180 		echo "$EMCC"
    181 	else
    182 		echo "Not found, web target won't be available"
    183 	fi
    184 	
    185 	printf '%s' "nodejs executable: "
    186 	if (validate_command "$NODE" >/dev/null); then
    187 		echo "$NODE"
    188 	else
    189 		echo "Not found, unit tests won't be available for web target"
    190 	fi
    191 
    192 	echo "Python bindings: $PYTHON3"
    193 }
    194 
    195 run() {
    196 	echo "$@"
    197 	$@
    198 }
    199 
    200 odflags() {
    201 	if [ "$debug" = "yes" ]; then
    202 		echo "$DFLAGS"
    203 	else 
    204 		echo "$OFLAGS"
    205 	fi
    206 }
    207 
    208 build_clean() {
    209 	run rm -rf -- *.o *.so *.a run runtest runtool runcpp \
    210 		web/nissy_web_module.*
    211 }
    212 
    213 build_nissy() {
    214 	validate_command "$CC"
    215 	validate_threads "$THREADS"
    216 	validate_arch "$ARCH"
    217 
    218 	run $CC $CFLAGS $WFLAGS $MFLAGS $(odflags) -c -o nissy.o src/nissy.c
    219 }
    220 
    221 build_lib() {
    222 	build_nissy
    223 	run ar rcs libnissy.a nissy.o
    224 }
    225 
    226 build_sharedlib() {
    227 	validate_command "$CC"
    228 	validate_threads "$THREADS"
    229 	validate_arch "$ARCH"
    230 
    231 	run $CC $CFLAGS $WFLAGS $MFLAGS $(odflags) -shared -c -o nissy.o \
    232 		src/nissy.c
    233 }
    234 
    235 build_shell() {
    236 	build_nissy
    237 	run $CC $CFLAGS $WFLAGS $(odflags) -o run nissy.o shell/shell.c
    238 }
    239 
    240 build_python() {
    241 	if [ -z "$PYTHON3_INCLUDES" ]; then
    242 		echo "Python3 development headers could not be located"
    243 		echo "Cannot build python module"
    244 		exit 1
    245 	fi
    246 	build_nissy
    247 	run $CC $CFLAGS $WFLAGS $PYTHON3_INCLUDES $(odflags) -shared \
    248 		-o nissy_python_module.so nissy.o python/nissy_module.c
    249 }
    250 
    251 build_cpp() {
    252 	validate_command "$CXX"
    253 	if [ -z "$@" ]; then
    254 		echo "Please provide one or more valid C++ source files"
    255 		echo "usage: ./build cpp FILES"
    256 	fi
    257 
    258 	build_nissy
    259 	run $CXX -std=c++20 -o runcpp cpp/nissy.cpp nissy.o $@
    260 	run ./runcpp
    261 }
    262 
    263 build_web() {
    264 	validate_command "$EMCC"
    265 	validate_threads "$THREADS"
    266 	validate_arch "$ARCH"
    267 
    268 	run $EMCC $WASMCFLAGS $WFLAGS $WASMMFLAGS $(odflags) -c \
    269 		-o nissy.o src/nissy.c
    270 	run $EMCC -lembind -lidbfs.js -lnodefs.js \
    271 		$CPPFLAGS $(odflags) $WASMLINKFLAGS \
    272 		--js-library web/callback.js \
    273 		-o web/nissy_web_module.mjs \
    274 		cpp/nissy.cpp web/adapter.cpp nissy.o
    275 }
    276 
    277 dotest() {
    278 	testout="test/last.out"
    279 	testerr="test/last.err"
    280 
    281 	# Verify that $t is a directory and it starts with three digits
    282 	if [ ! -d "$t" ] || ! (basename "$t" | grep -Eq '^[0-9]{3}'); then
    283 		return 0
    284 	fi
    285 
    286 	# Verify that the test is selected by the given pattern
    287 	if [ -n "$pattern" ] && ! (echo "$t" | grep -q "$pattern"); then
    288 		return 0
    289 	fi
    290 
    291 	$testbuild "$t"/*.c nissy.o || exit 1
    292 	for cin in "$t"/*.in; do
    293 		c="$(echo "$cin" | sed 's/\.in//')"
    294 		cout="$c.out"
    295 		printf '%s: ' "$c"
    296 		$testrun < "$cin" > "$testout" 2> "$testerr"
    297 		if diff "$cout" "$testout"; then
    298 			printf "OK\n"
    299 		else
    300 			printf "Test failed! stderr:\n"
    301 			cat "$testerr"
    302 			exit 1
    303 		fi
    304 	done
    305 }
    306 
    307 build_test_generic() {
    308 	if [ -n "$1" ]; then
    309 		pattern="$1"
    310 		shift
    311 	fi
    312 	debug="yes"
    313 	build_$obj
    314 	for t in test/*; do
    315 		dotest || exit 1
    316 	done
    317 	echo "All tests passed!"
    318 }
    319 
    320 build_test() {
    321 	obj="nissy"
    322 	testobj="runtest"
    323 	testbuild="$CC $CFLAGS $WFLAGS $DFLAGS $MFLAGS -o $testobj"
    324 	testrun="./$testobj"
    325 	build_test_generic $@
    326 	rm runtest
    327 }
    328 
    329 build_webtest() {
    330 	obj="web"
    331 	testobj="runtest.js"
    332 	testbuild="$EMCC $WASMCFLAGS $WFLAGS $WASMMFLAGS $DFLAGS -o $testobj"
    333 	testrun="$NODE $testobj"
    334 	build_test_generic $@
    335 	rm -f runtest.js runtest.wasm
    336 }
    337 
    338 build_tool() {
    339 	pattern="$1"
    340 
    341 	if [ -z "$pattern" ]; then
    342 		echo "Please provide a valid PATTERN to select a tool"
    343 		echo "usage: ./build tool PATTERN"
    344 		exit 1
    345 	fi
    346 	shift
    347 
    348 	# Select a single tool matched by the given pattern
    349 	for t in tools/*; do
    350 		if [ -d "$t" ] && (echo "$t" | grep -q "$pattern"); then
    351 			toolname="$(basename "$t" .c)"
    352 			break
    353 		fi
    354 	done
    355 	if [ -z "$toolname" ]; then
    356 		echo "pattern '$pattern' does not match any tool"
    357 		exit 1
    358 	fi
    359 
    360 	build_nissy
    361 
    362 	results="tools/results"
    363 	last="$results/last.out"
    364 	date="$(date +'%Y-%m-%d-%H-%M-%S')"
    365 	file="$results/$toolname-$date.txt"
    366 
    367 	$CC $CFLAGS $WFLAGS $MFLAGS $(odflags) -o runtool "$t"/*.c nissy.o \
    368 		|| exit 1
    369 
    370 	(
    371 		date +'%Y-%m-%d %H:%M'
    372 		echo ""
    373 		echo "=========== Running tool  ==========="
    374 		echo "tool name: $toolname"
    375 		echo ""
    376 		echo "======== nissy build command ========"
    377 		echo "$CC $CFLAGS $WFLAGS $MFLAGS $(odflags)"
    378 		echo ""
    379 		echo "======== tool build command  ========"
    380 		echo "$CC $CFLAGS $WFLAGS $MFLAGS $(odflags)"
    381 		echo ""
    382 		echo "============ tool output ============"
    383 		./runtool $@
    384 	) | tee "$file" "$last"
    385 	rm -f runtool
    386 }
    387 
    388 if [ "$1" = "-d" ]; then
    389 	debug="yes"
    390 	shift
    391 fi
    392 
    393 target="$1"
    394 if [ -z "$target" ]; then
    395 	target="nissy"
    396 else
    397 	shift
    398 fi
    399 
    400 case "$target" in
    401 help|config|clean|\
    402 nissy|lib|sharedlib|shell|python|cpp|web|test|webtest|tool)
    403 	mkdir -p tables tools/results
    404 	(build_"$target" $@) || exit 1
    405 	exit 0
    406 	;;
    407 *)
    408 	echo "Target '$target' unavailable, run '$0 help' for info"
    409 	exit 1
    410 	;;
    411 esac