aboutsummaryrefslogtreecommitdiff
path: root/build.sh
diff options
context:
space:
mode:
Diffstat (limited to 'build.sh')
-rwxr-xr-xbuild.sh475
1 files changed, 475 insertions, 0 deletions
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..cad6947
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,475 @@
1#!/bin/sh
2
3# Build system for nissy, run './build.sh 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.sh
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.sh debug
24#
25# And the empty string value will take precedence.
26
27# Specify the C compiler to use.
28CC=${CC-${NISSY_BUILD_CC:-"cc"}}
29
30# Specify the C++ compiler for the C++ examples.
31CXX=${CXX-${NISSY_BUILD_CXX:-"c++"}}
32
33# Specify the compiler to use when building for WASM.
34EMCC=${EMCC-${NISSY_BUILD_EMCC:-"emcc"}}
35
36# Specify the nodejs command for running tests for WASM.
37NODE=${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.
44THREADS=${THREADS-${NISSY_BUILD_THREADS}}
45
46detectthreads() {
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.
59ARCH=${ARCH-${NISSY_BUILD_ARCH}}
60
61greparch() {
62 $CC -march=native -dM -E - </dev/null 2>/dev/null | grep "$1"
63}
64
65detectarch() {
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.
79SANITIZE=${SANITIZE-${NISSY_BUILD_SANITIZE:-""}}
80
81# Optimization flags.
82OPTIMIZE=${OPTIMIZE-${NISSY_BUILD_OPTIMIZE:-"-O3"}}
83
84validate_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
92validate_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
100validate_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
113parsesanitize() {
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
120maybe_pthread() {
121 if [ "$THREADS" -gt 1 ]; then
122 echo "-pthread"
123 else
124 echo ""
125 fi
126}
127
128# Build flags
129CFLAGS="-std=c11 -fPIC -D_POSIX_C_SOURCE=199309L $(maybe_pthread)"
130PYCFLAGS="-std=c11 -fPIC $(maybe_pthread)"
131[ "$ARCH" = "AVX2" ] && CFLAGS="$CFLAGS -mavx2"
132WFLAGS="-pedantic -Wall -Wextra -Wno-unused-parameter -Wno-unused-function"
133OFLAGS="$OPTIMIZE"
134DFLAGS="-DDEBUG -g3 $(parsesanitize "$SANITIZE")"
135MFLAGS="-DTHREADS=$THREADS -D$ARCH"
136CPPFLAGS="-std=c++20 $(maybe_pthread)"
137
138# TODO:
139# MEMORY64 is supported on Firefox (from version 134) and Chrome (from 133),
140# but not on Safari (nor on e.g. Firefox 128 ESR, current default on Debian).
141# See also https://webassembly.org/features
142# When it becomes widely available, we can support it by adding -sMEMORY64
143# to the WASMCFLAGS, WASMCPPFLAGS and WASMLINKFLAGS variables below, and
144# -sMAXIMUM_MEMORY=10737418240 to WASMLINKFLAGS. This way we can enable
145# solvers larger than h48h6k2 in the web version.
146
147# TODO:
148# The options below have to be adjusted when native WASM_SIMD is implemented.
149
150# Build flags for emscripten (WASM target)
151WASMCFLAGS="-std=c11 -fPIC -D_POSIX_C_SOURCE=199309L $(maybe_pthread)
152 -mfpu=neon -mrelaxed-simd"
153WASMCPPFLAGS="-std=c++20 $(maybe_pthread)"
154WASMDBGFLAGS="-sASSERTIONS"
155WASMMFLAGS="-DTHREADS=$THREADS -DNEON"
156WASMLINKFLAGS="--no-entry -sEXPORT_NAME='Nissy' -sMODULARIZE
157 -sEXPORTED_RUNTIME_METHODS=addFunction,UTF8ToString
158 -sALLOW_TABLE_GROWTH
159 -sALLOW_MEMORY_GROWTH -sSTACK_SIZE=5MB -sPTHREAD_POOL_SIZE=$THREADS
160 -sFETCH -sASYNCIFY -sLINKABLE -sEXPORT_ALL"
161
162if (command -v "python3-config" >/dev/null 2>&1) ; then
163 PYTHON3_INCLUDES="$(python3-config --includes)"
164 PYTHON3="version $(echo "$PYTHON3_INCLUDES" | sed 's/.*3\./3./')"
165else
166 PYTHON3_INCLUDES=""
167 PYTHON3="Not found, Python shell won't be available"
168fi
169
170build_help() {
171 echo "Build system for nissy. Usage:"
172 echo ""
173 echo "$0 [-d] TARGET # build the given TARGET"
174 echo "$0 [-d] # same as '$0 nissy [-d]'"
175 echo "$0 test [PATTERN] # run unit tests (matching PATTERN)"
176 echo "$0 webtest [PATTERN] # same as test, but for WASM build"
177 echo "$0 [-d] tool PATTERN # run the tool matching PATTERN"
178 echo ""
179 echo "targets:"
180 echo ""
181 echo "nissy Build the nissy.o object file"
182 echo "lib Build the static library libnissy.a"
183 echo "sharedlib Build the shared library libnissy.so"
184 echo "python Build the Python module for nissy"
185 echo "shell Build a basic nissy shell (./run)"
186 echo "web Build the WebAssembly / JavaScript module for nissy"
187 echo "cpp FILES Build and run the given FILES including cpp/nissy.h"
188 echo "solvetest Build nissy and run the tests for solvers (tools)"
189 echo ""
190 echo "help Show this help message"
191 echo "config Show build configuration and exit"
192 echo "clean Remove all build files"
193 echo ""
194 echo "The -d option activates debug mode (slower, used for testing)."
195 echo "Tests are automatically built in debug mode even without -d."
196 echo "For more on build configurations, see the comments in ./build.sh"
197}
198
199build_config() {
200 echo "Compiler: $CC"
201 echo "Architecture: $ARCH"
202 echo "Max threads: $THREADS"
203 echo "Optimization options: $OFLAGS"
204 echo "Debug flags: $DFLAGS"
205 echo ""
206
207 echo "Optional features:"
208
209 printf '%s' "WASM compiler: "
210 if (validate_command "$EMCC" >/dev/null); then
211 echo "$EMCC"
212 else
213 echo "Not found, web target won't be available"
214 fi
215
216 printf '%s' "nodejs executable: "
217 if (validate_command "$NODE" >/dev/null); then
218 echo "$NODE"
219 else
220 echo "Not found, unit tests won't be available for web target"
221 fi
222
223 echo "Python bindings: $PYTHON3"
224}
225
226run() {
227 echo "$@"
228 $@
229}
230
231odflags() {
232 if [ "$debug" = "yes" ]; then
233 echo "$DFLAGS"
234 else
235 echo "$OFLAGS"
236 fi
237}
238
239build_clean() {
240 run rm -rf -- *.o *.so *.a run runtest runtool runcpp \
241 web/nissy_web_module.* web/http/nissy_web_module.*
242}
243
244build_nissy() {
245 validate_command "$CC"
246 validate_threads "$THREADS"
247 validate_arch "$ARCH"
248
249 run $CC $CFLAGS $WFLAGS $MFLAGS $(odflags) -c -o nissy.o src/nissy.c
250}
251
252build_lib() {
253 build_nissy
254 run ar rcs libnissy.a nissy.o
255}
256
257build_sharedlib() {
258 validate_command "$CC"
259 validate_threads "$THREADS"
260 validate_arch "$ARCH"
261
262 run $CC $CFLAGS $WFLAGS $MFLAGS $(odflags) -shared -c -o nissy.o \
263 src/nissy.c
264}
265
266build_shell() {
267 build_nissy || exit 1
268 run $CC $CFLAGS $WFLAGS $(odflags) -o run nissy.o shell/shell.c
269}
270
271build_python() {
272 if [ -z "$PYTHON3_INCLUDES" ]; then
273 echo "Python3 development headers could not be located"
274 echo "Cannot build python module"
275 exit 1
276 fi
277 if [ "$debug" = "yes" ]; then
278 echo "Cannot build Python library in debug mode"
279 exit 1
280 fi
281 build_nissy || exit 1
282 run $CC $PYCFLAGS $WFLAGS $PYTHON3_INCLUDES $OFLAGS -shared \
283 -o python/nissy.so nissy.o python/nissy_module.c
284}
285
286build_cpp() {
287 validate_command "$CXX"
288 if [ -z "$@" ]; then
289 echo "Please provide one or more valid C++ source files"
290 echo "usage: ./build.sh cpp FILES"
291 fi
292
293 build_nissy || exit 1
294 run $CXX $(odflags) -std=c++20 -o runcpp cpp/nissy.cpp nissy.o $@ \
295 || exit 1
296 run ./runcpp
297}
298
299build_web() {
300 validate_command "$EMCC"
301 validate_threads "$THREADS"
302 validate_arch "$ARCH"
303
304 obj="nissy_web_module"
305
306 if [ "$debug" = "yes" ]; then
307 ODFLAGS="$DFLAGS -sASSERTIONS"
308 else
309 ODFLAGS="$OFLAGS"
310 fi
311
312 run $EMCC $WASMCFLAGS $WFLAGS $WASMMFLAGS $ODFLAGS -c \
313 -o nissy.o src/nissy.c || exit 1
314 run $EMCC -lembind -lidbfs.js \
315 $WASMCPPFLAGS $ODFLAGS $WASMLINKFLAGS -o web/"$obj".mjs \
316 cpp/nissy.cpp web/storage.cpp web/adapter.cpp nissy.o || exit 1
317 cp web/"$obj".mjs web/http/
318 cp web/"$obj".wasm web/http/
319}
320
321dotest() {
322 testout="test/last.out"
323 testerr="test/last.err"
324
325 # Verify that $t is a directory and it starts with three digits
326 if [ ! -d "$t" ] || ! (basename "$t" | grep -Eq '^[0-9]{3}'); then
327 return 0
328 fi
329
330 # Verify that the test is selected by the given pattern
331 if [ -n "$pattern" ] && ! (echo "$t" | grep -q "$pattern"); then
332 return 0
333 fi
334
335 $testbuild "$t"/*.c nissy.o || exit 1
336 for cin in "$t"/*.in; do
337 c="$(echo "$cin" | sed 's/\.in//')"
338 cout="$c.out"
339 printf '%s: ' "$c"
340 $testrun < "$cin" > "$testout" 2> "$testerr"
341 if diff "$cout" "$testout"; then
342 printf "OK\n"
343 else
344 printf "Test failed! stderr:\n"
345 cat "$testerr"
346 exit 1
347 fi
348 done
349}
350
351build_test_generic() {
352 if [ -n "$1" ]; then
353 pattern="$1"
354 shift
355 fi
356 debug="yes"
357 build_$obj || exit 1
358 for t in test/*; do
359 dotest || exit 1
360 done
361 echo "All tests passed!"
362}
363
364build_test() {
365 obj="nissy"
366 testobj="runtest"
367 testbuild="$CC $CFLAGS $WFLAGS $DFLAGS $MFLAGS -o $testobj"
368 testrun="./$testobj"
369 build_test_generic $@
370 rm -f runtest
371}
372
373build_webtest() {
374 obj="web"
375 testobj="runtest.js"
376 testbuild="$EMCC $WASMCFLAGS $WFLAGS $WASMMFLAGS $DFLAGS \
377 -sSTACK_SIZE=5MB -o $testobj"
378 testrun="$NODE $testobj"
379 build_test_generic $@
380 rm -f runtest.js runtest.wasm
381}
382
383run_single_tool() {
384 results="tools/results"
385 last="$results/last.out"
386 date="$(date +'%Y-%m-%d-%H-%M-%S')"
387 file="$results/$toolname-$date.txt"
388 failed="tools/failed"
389
390 $CC $CFLAGS $WFLAGS $MFLAGS $(odflags) -o runtool "$t"/*.c nissy.o \
391 || exit 1
392
393 (
394 date +'%Y-%m-%d %H:%M'
395 echo ""
396 echo "=========== Running tool ==========="
397 echo "tool name: $toolname"
398 echo ""
399 echo "======== nissy build command ========"
400 echo "$CC $CFLAGS $WFLAGS $MFLAGS $(odflags)"
401 echo ""
402 echo "======== tool build command ========"
403 echo "$CC $CFLAGS $WFLAGS $MFLAGS $(odflags)"
404 echo ""
405 echo "============ tool output ============"
406 ./runtool $@ || touch "$failed"
407 ) | tee "$file" "$last"
408
409 rm -f runtool
410 if [ -f "$failed" ]; then
411 rm "$failed"
412 exit 1
413 fi
414}
415
416build_tool() {
417 pattern="$1"
418
419 if [ -z "$pattern" ]; then
420 echo "Please provide a valid PATTERN to select a tool"
421 echo "usage: ./build.sh tool PATTERN"
422 exit 1
423 fi
424 shift
425
426 # Select a single tool matched by the given pattern
427 for t in tools/*; do
428 if [ -d "$t" ] && (echo "$t" | grep -q "$pattern"); then
429 toolname="$(basename "$t" .c)"
430 break
431 fi
432 done
433 if [ -z "$toolname" ]; then
434 echo "pattern '$pattern' does not match any tool"
435 exit 1
436 fi
437
438 build_nissy || exit 1
439 run_single_tool $@
440}
441
442build_solvetest() {
443 build_nissy || exit 1
444 for t in tools/*; do
445 if [ -d "$t" ] && (echo "$t" | grep -q "solvetest"); then
446 toolname="$(basename "$t" .c)"
447 run_single_tool || exit 1
448 fi
449 done
450}
451
452if [ "$1" = "-d" ]; then
453 debug="yes"
454 shift
455fi
456
457target="$1"
458if [ -z "$target" ]; then
459 target="nissy"
460else
461 shift
462fi
463
464case "$target" in
465help|config|clean|\
466nissy|lib|sharedlib|shell|python|cpp|web|test|webtest|tool|solvetest)
467 mkdir -p tables tools/results
468 (build_"$target" $@) || exit 1
469 exit 0
470 ;;
471*)
472 echo "Target '$target' unavailable, run '$0 help' for info"
473 exit 1
474 ;;
475esac

Generated with cgit - Back to sebastiano.tronto.net