-
Notifications
You must be signed in to change notification settings - Fork 101
Expand file tree
/
Copy pathci_build_and_test_in_container.sh
More file actions
executable file
·510 lines (440 loc) · 17.7 KB
/
ci_build_and_test_in_container.sh
File metadata and controls
executable file
·510 lines (440 loc) · 17.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
#!/bin/bash
set -o pipefail
export PYTHONDONTWRITEBYTECODE=1
SCRIPT_NAME=$0
echo "Running CLI ${SCRIPT_NAME} $@"
# docs.docker.com/config/containers/resource_constraints
# Inside the container, tools like free report the host's available swap, not what's available inside the container.
# Don't rely on the output of free or similar tools to determine whether swap is present.
echo "running free -g"
free -g
# The or_die function run the passed command line and
# exits the program in case of non zero error code
function or_die () {
"$@"
local status=$?
if [[ $status != 0 ]] ; then
if [[ -n "${CURRENT_PHASE_LABEL:-}" ]]; then
phase_finish "${status}"
fi
echo ERROR $status command: $@
exit $status
fi
}
PHASE_TIMINGS=()
CURRENT_PHASE_LABEL=""
CURRENT_PHASE_START=""
tempdir=""
function now_epoch () {
date +%s
}
function format_duration () {
local total_seconds=$1
local hours=$(( total_seconds / 3600 ))
local minutes=$(( (total_seconds % 3600) / 60 ))
local seconds=$(( total_seconds % 60 ))
if (( hours > 0 )); then
printf '%dh %02dm %02ds' "${hours}" "${minutes}" "${seconds}"
elif (( minutes > 0 )); then
printf '%dm %02ds' "${minutes}" "${seconds}"
else
printf '%ds' "${seconds}"
fi
}
function phase_start () {
CURRENT_PHASE_LABEL="$1"
CURRENT_PHASE_START="$(now_epoch)"
echo ">>> ${CURRENT_PHASE_LABEL} started at $(date -u +"%Y-%m-%dT%H:%M:%SZ")"
}
function phase_finish () {
local status="${1:-0}"
local label="${CURRENT_PHASE_LABEL:-}"
local start="${CURRENT_PHASE_START:-}"
if [[ -z "${label}" || -z "${start}" ]]; then
return 0
fi
local duration=$(( $(now_epoch) - start ))
PHASE_TIMINGS+=("${label}|${duration}|${status}")
if [[ "${status}" -eq 0 ]]; then
echo ">>> ${label} completed in $(format_duration "${duration}")"
else
echo ">>> ${label} failed after $(format_duration "${duration}") (exit ${status})"
fi
CURRENT_PHASE_LABEL=""
CURRENT_PHASE_START=""
}
function print_phase_summary () {
local entry label duration status status_text
if [[ ${#PHASE_TIMINGS[@]} -eq 0 ]]; then
return 0
fi
echo "Phase timing summary:"
for entry in "${PHASE_TIMINGS[@]}"; do
IFS='|' read -r label duration status <<< "${entry}"
if [[ "${status}" -eq 0 ]]; then
status_text="ok"
else
status_text="exit ${status}"
fi
printf ' - %s: %s (%s)\n' "${label}" "$(format_duration "${duration}")" "${status_text}"
done
}
function usage () {
>&2 cat << EOF
Usage: $0
--build-exe-only
Request for the build of geos only.
--cmake-build-type ...
One of Debug, Release, RelWithDebInfo and MinSizeRel. Forwarded to CMAKE_BUILD_TYPE.
--code-coverage
run a code build and test.
--data-basename output.tar.gz
If some data needs to be extracted from the build, the argument will define the tarball. Has to be a `tar.gz`.
--geos-enable-bounds-check
Either ON or OFF (default is ON). Build geos with bounds check.
--enable-hypre
One of ON or OFF (default is ON). Build geos with hypre.
--enable-hypre-device
One of CPU, CUDA, or HIP (default is CPU). Build geos with hypre GPU support.
--enable-trilinos
One of ON or OFF (default is OFF). Build geos with trilinos.
--exchange-dir /path/to/exchange
Folder to share data with outside of the container.
--host-config host-config/my_config.cmake
The host-config. Path is relative to the root of the repository.
--install-dir-basename GEOS-e42ffc1
GEOS installation basename.
--makefile
Use "Unix Makefiles" as build system generator.
--ninja
Use "Ninja" as build system generator.
--no-install-schema
Do not install the xsd schema.
--no-run-unit-tests
Do not run the unit tests (but they will be built).
--nproc N
Number of cores to use for the build.
--repository /path/to/repository
Internal mountpoint where the geos repository will be available.
--run-integrated-tests
Run the integrated tests. Then bundle and send the results to the cloud.
--use-sccache
Enable sccache as compiler launcher.
--sccache-config config.toml
Relative path to an sccache config file to use inside the container.
--test-code-style
--test-documentation
-h | --help
EOF
exit 1
}
# First working in the root of the cloned repository.
# Then we'll move to the build dir.
or_die cd $(dirname $0)/..
args=$(or_die getopt -a -o h --long build-exe-only,cmake-build-type:,code-coverage,data-basename:,geos-enable-bounds-check:,enable-hypre:,enable-hypre-device:,enable-trilinos:,exchange-dir:,host-config:,install-dir-basename:,makefile,ninja,no-install-schema,no-run-unit-tests,nproc:,repository:,run-integrated-tests,sccache-config:,test-code-style,test-documentation,use-sccache,help -- "$@")
# Variables with default values
BUILD_EXE_ONLY=false
BUILD_GENERATOR=""
GEOS_INSTALL_SCHEMA=true
HOST_CONFIG="host-configs/environment.cmake"
ENABLE_HYPRE=ON
ENABLE_HYPRE_DEVICE=CPU
GEOS_LA_INTERFACE=Hypre
RUN_UNIT_TESTS=true
RUN_INTEGRATED_TESTS=false
UPLOAD_TEST_BASELINES=false
TEST_CODE_STYLE=false
TEST_DOCUMENTATION=false
ENABLE_TRILINOS=OFF
CODE_COVERAGE=false
NPROC="$(nproc)"
GEOS_ENABLE_BOUNDS_CHECK=ON
SCCACHE_BIN=""
USE_SCCACHE=false
eval set -- ${args}
while :
do
case $1 in
--build-exe-only)
BUILD_EXE_ONLY=true
RUN_UNIT_TESTS=false
shift;;
--cmake-build-type) CMAKE_BUILD_TYPE=$2; shift 2;;
--ninja)
BUILD_GENERATOR=$1;
shift;;
--data-basename)
DATA_BASENAME=$2
DATA_BASENAME_WE=${DATA_BASENAME%%.*}
DATA_BASENAME_EXT=${DATA_BASENAME#*.}
if [[ ${DATA_BASENAME_EXT} != "tar.gz" ]] ; then
echo "The script ${SCRIPT_NAME} can only pack data into a '.tar.gz' file."
exit 1
fi
unset DATA_BASENAME DATA_BASENAME_EXT
shift 2;;
--geos-enable-bounds-check) GEOS_ENABLE_BOUNDS_CHECK=$2; shift 2;;
--enable-hypre) ENABLE_HYPRE=$2; shift 2;;
--enable-hypre-device) ENABLE_HYPRE_DEVICE=$2; shift 2;;
--enable-trilinos) ENABLE_TRILINOS=$2; shift 2;;
--exchange-dir) DATA_EXCHANGE_DIR=$2; shift 2;;
--host-config) HOST_CONFIG=$2; shift 2;;
--install-dir-basename) GEOS_DIR=${GEOSX_TPL_DIR}/../$2; shift 2;;
--makefile) BUILD_GENERATOR=""; shift;;
--no-install-schema) GEOS_INSTALL_SCHEMA=false; shift;;
--no-run-unit-tests) RUN_UNIT_TESTS=false; shift;;
--nproc) NPROC=$2; shift 2;;
--repository) GEOS_SRC_DIR=$2; shift 2;;
--run-integrated-tests) RUN_INTEGRATED_TESTS=true; shift;;
--upload-test-baselines) UPLOAD_TEST_BASELINES=true; shift;;
--code-coverage) CODE_COVERAGE=true; shift;;
--sccache-config) SCCACHE_CONFIG_FILE=$2; shift 2;;
--use-sccache) USE_SCCACHE=true; shift;;
--test-code-style) TEST_CODE_STYLE=true; shift;;
--test-documentation) TEST_DOCUMENTATION=true; shift;;
-h | --help) usage; shift;;
# -- means the end of the arguments; drop this, and break out of the while loop
--) shift; break;;
*) >&2 echo Unsupported option: $1
usage;;
esac
done
if [[ -z "${GEOS_SRC_DIR}" ]]; then
echo "Variable GEOS_SRC_DIR is either empty or not defined. Please define it using '--repository'."
exit 1
fi
cleanup() {
if [[ -n "${CURRENT_PHASE_LABEL:-}" ]]; then
phase_finish 1
fi
print_phase_summary
echo "Container cleanup..."
if [[ -n "${tempdir:-}" ]]; then
rm -rf "${tempdir}" || true
fi
rm -rf "${GEOS_SRC_DIR}/src/docs/sphinx/datastructure" || true
if [[ -n "${HOST_UID:-}" && -n "${HOST_GID:-}" ]]; then
chown -R "${HOST_UID}:${HOST_GID}" "${GEOS_SRC_DIR}" || true
fi
}
trap cleanup EXIT
if [[ -z "${GEOS_DIR}" ]]; then
echo "Installation folder undefined. Set to default value '/dev/null'. You can define it using '--install-dir-basename'."
GEOS_DIR=/dev/null
fi
if [[ "${ENABLE_HYPRE}" = ON ]]; then
GEOS_LA_INTERFACE=Hypre
else
GEOS_LA_INTERFACE=Trilinos
fi
if [[ "${USE_SCCACHE}" == true ]]; then
SCCACHE_BIN=${SCCACHE:-$(command -v sccache || true)}
if [[ -z "${SCCACHE_BIN}" ]]; then
echo "sccache was requested, but no sccache binary is available in the container."
exit 1
fi
if [[ -n "${SCCACHE_CONFIG_FILE:-}" ]]; then
if [[ ! -f "${GEOS_SRC_DIR}/${SCCACHE_CONFIG_FILE}" ]]; then
echo "Unable to find requested sccache config file at ${GEOS_SRC_DIR}/${SCCACHE_CONFIG_FILE}."
exit 1
fi
or_die mkdir -p ${HOME}/.config/sccache
or_die cp "${GEOS_SRC_DIR}/${SCCACHE_CONFIG_FILE}" "${HOME}/.config/sccache/config"
fi
# Backend-specific credentials and endpoints are injected through the environment and/or config file.
SCCACHE_CMAKE_ARGS="-DCMAKE_C_COMPILER_LAUNCHER=${SCCACHE_BIN} -DCMAKE_CXX_COMPILER_LAUNCHER=${SCCACHE_BIN} -DCMAKE_CUDA_COMPILER_LAUNCHER=${SCCACHE_BIN}"
if [[ -f /certs/ca-bundle.crt ]]; then
export SSL_CERT_FILE=/certs/ca-bundle.crt
export CURL_CA_BUNDLE=/certs/ca-bundle.crt
export REQUESTS_CA_BUNDLE=/certs/ca-bundle.crt
fi
echo "sccache initial state"
${SCCACHE_BIN} --show-stats || true
fi
if [ -z "${NPROC}" ]; then
NPROC=$(nproc)
echo "NPROC unset, setting to ${NPROC}..."
fi
echo "Using ${NPROC} cores."
if [[ "${RUN_INTEGRATED_TESTS}" = true ]]; then
phase_start "Set up integrated test environment"
echo "Running the integrated tests has been requested."
# We install the python environment required by ATS to run the integrated tests.
#or_die apt-get update
or_die apt-get install -y virtualenv python3-dev python-is-python3
ATS_PYTHON_HOME=/tmp/run_integrated_tests_virtualenv
or_die virtualenv ${ATS_PYTHON_HOME}
python3 -m pip cache purge
# Setup a temporary directory to hold tests
tempdir=$(mktemp -d)
echo "Setting up a temporary directory to hold tests and baselines: $tempdir"
ATS_BASELINE_DIR=$tempdir/GEOS_integratedTests_baselines
ATS_WORKING_DIR=$tempdir/GEOS_integratedTests_working
export ATS_FILTER="np<=32"
ATS_CMAKE_ARGS="-DATS_ARGUMENTS=\"--machine openmpi --ats openmpi_mpirun=/usr/bin/mpirun --ats openmpi_args=--allow-run-as-root --ats openmpi_procspernode=32 --ats openmpi_maxprocs=32\" -DPython3_ROOT_DIR=${ATS_PYTHON_HOME} -DPython3_EXECUTABLE=${ATS_PYTHON_HOME}/bin/python3 -DATS_BASELINE_DIR=${ATS_BASELINE_DIR} -DATS_WORKING_DIR=${ATS_WORKING_DIR}"
phase_finish 0
fi
if [[ "${CODE_COVERAGE}" = true ]]; then
or_die apt-get update
or_die apt-get install -y lcov
fi
# The -DBLT_MPI_COMMAND_APPEND="--allow-run-as-root;--oversubscribe" option is added for OpenMPI.
#
# OpenMPI prevents from running as `root` user by default.
# And by default user is `root` in a docker container.
# Using this option therefore offers a minimal and convenient way to run the unit tests.
#
# The option `--oversubscribe` tells OpenMPI to allow more MPI ranks than the node has cores.
# This is needed because our unit test `blt_mpi_smoke` is run in parallel with _hard coded_ 4 ranks.
# While some of our ci nodes may have less cores available.
#
# In case we have more powerful nodes, consider removing `--oversubscribe` and use `--use-hwthread-cpus` instead.
# This will tells OpenMPI to discover the number of hardware threads on the node,
# and use that as the number of slots available. (There is a distinction between threads and cores).
GEOS_BUILD_DIR=/tmp/geos-build
phase_start "Configure"
or_die python3 scripts/config-build.py \
-hc ${HOST_CONFIG} \
-bt ${CMAKE_BUILD_TYPE} \
-bp ${GEOS_BUILD_DIR} \
-ip ${GEOS_DIR} \
${BUILD_GENERATOR} \
-DBLT_MPI_COMMAND_APPEND='"--allow-run-as-root;--oversubscribe"' \
-DGEOS_INSTALL_SCHEMA=${GEOS_INSTALL_SCHEMA} \
-DENABLE_HYPRE=${ENABLE_HYPRE} \
-DENABLE_HYPRE_DEVICE=${ENABLE_HYPRE_DEVICE} \
-DENABLE_TRILINOS=${ENABLE_TRILINOS} \
-DGEOS_LA_INTERFACE:PATH=${GEOS_LA_INTERFACE} \
-DENABLE_COVERAGE=$([[ "${CODE_COVERAGE}" = true ]] && echo 1 || echo 0) \
-DGEOS_ENABLE_BOUNDS_CHECK=${GEOS_ENABLE_BOUNDS_CHECK} \
${SCCACHE_CMAKE_ARGS} \
${ATS_CMAKE_ARGS}
phase_finish 0
# The configuration step is now over, we can now move to the build directory for the build!
or_die cd ${GEOS_BUILD_DIR}
# Code style check
if [[ "${TEST_CODE_STYLE}" = true ]]; then
phase_start "Code style check"
or_die ctest --output-on-failure -R "testUncrustifyCheck"
phase_finish 0
exit 0
fi
# Documentation check
if [[ "${TEST_DOCUMENTATION}" = true ]]; then
phase_start "Documentation check"
or_die ctest --output-on-failure -R "testDoxygenCheck"
phase_finish 0
exit 0
fi
# Performing the requested build.
phase_start "Build"
if [[ "${BUILD_EXE_ONLY}" = true ]]; then
or_die cmake --build . -j $NPROC --target geosx
else
or_die cmake --build . -j $NPROC
#or_die cmake --install .
if [[ ! -z "${DATA_BASENAME_WE}" ]]; then
# Here we pack the installation.
# The `--transform` parameter provides consistency between the tarball name and the unpacked folder.
echo "DATA_EXCHANGE_DIR=${DATA_EXCHANGE_DIR}"
echo "DATA_BASENAME_WE=${DATA_BASENAME_WE}"
echo "GEOS_TPL_DIR=${GEOS_TPL_DIR}"
echo "GEOSX_TPL_DIR=${GEOSX_TPL_DIR}"
GEOS_TPL_DIR=${GEOSX_TPL_DIR}
echo tar czf ${DATA_EXCHANGE_DIR}/${DATA_BASENAME_WE}.tar.gz --directory=${GEOS_TPL_DIR}/.. --transform "s|^./|${DATA_BASENAME_WE}/|" .
or_die tar czf ${DATA_EXCHANGE_DIR}/${DATA_BASENAME_WE}.tar.gz --directory=${GEOS_TPL_DIR}/.. --transform "s|^./|${DATA_BASENAME_WE}/|" .
fi
fi
phase_finish 0
if [[ -n "${SCCACHE_BIN}" ]]; then
echo "sccache post-build state"
or_die ${SCCACHE_BIN} --show-adv-stats
fi
if [[ "${CODE_COVERAGE}" = true ]]; then
phase_start "Generate code coverage"
export OMP_NUM_THREADS=1
or_die cmake --build . --target coreComponents_coverage
or_die cp -r ${GEOS_BUILD_DIR}/coreComponents_coverage.info.cleaned ${GEOS_SRC_DIR}/geos_coverage.info.cleaned
phase_finish 0
fi
# Run the unit tests (excluding previously ran checks).
if [[ "${RUN_UNIT_TESTS}" = true ]]; then
phase_start "Unit tests"
if [ ${HOSTNAME} == 'streak.llnl.gov' ] || [ ${HOSTNAME} == 'streak2.llnl.gov' ]; then
or_die ctest --output-on-failure -E "testUncrustifyCheck|testDoxygenCheck|testExternalSolvers"
else
or_die ctest --output-on-failure -E "testUncrustifyCheck|testDoxygenCheck"
fi
phase_finish 0
fi
if [[ "${RUN_INTEGRATED_TESTS}" = true ]]; then
# fix the setuptools/distutils conflict
export SETUPTOOLS_USE_DISTUTILS=stdlib
# We split the process in two steps. First installing the environment, then running the tests.
phase_start "Build ATS environment"
or_die cmake --build . --target ats_environment
phase_finish 0
# The tests are not run using cmake (`cmake --build . --verbose --target ats_run`)
# because with ninja it swallows the output while all the
# simulations are running.
# We directly use the script instead...
echo "Available baselines:"
ls -lR /tmp/geos/baselines
echo "Running integrated tests..."
phase_start "Integrated tests"
integratedTests/geos_ats.sh --baselineCacheDirectory /tmp/geos/baselines
ATS_RUN_STATUS=$?
phase_finish "${ATS_RUN_STATUS}"
phase_start "Process integrated test logs"
PROCESS_LOGS_STATUS=0
echo "Processing logs..."
bin/geos_ats_process_tests_fails --directory integratedTests/TestResults &> integratedTests/TestResults/processedTestsLogs.txt || PROCESS_LOGS_STATUS=$?
if [[ "${PROCESS_LOGS_STATUS}" -eq 0 ]]; then
echo "Packing logs..."
tar -czf ${DATA_EXCHANGE_DIR}/test_logs_${DATA_BASENAME_WE}.tar.gz integratedTests/TestResults || PROCESS_LOGS_STATUS=$?
fi
phase_finish "${PROCESS_LOGS_STATUS}"
echo "Checking results..."
phase_start "Check integrated test results"
bin/geos_ats_log_check integratedTests/TestResults/test_results.ini -y ${GEOS_SRC_DIR}/.integrated_tests.yaml &> $tempdir/log_check.txt
LOG_CHECK_STATUS=$?
cat $tempdir/log_check.txt
phase_finish "${LOG_CHECK_STATUS}"
if grep -q "Overall status: PASSED" "$tempdir/log_check.txt"; then
echo "IntegratedTests passed. No rebaseline required."
INTEGRATED_TEST_EXIT_STATUS=0
else
echo "IntegratedTests failed. Rebaseline is required."
# Rebaseline and pack into an archive
echo "Rebaselining..."
phase_start "Rebaseline integrated tests"
REBASELINE_STATUS=0
integratedTests/geos_ats.sh -a rebaselinefailed || REBASELINE_STATUS=$?
if [[ "${REBASELINE_STATUS}" -eq 0 ]]; then
echo "Packing baselines..."
integratedTests/geos_ats.sh -a pack_baselines --baselineArchiveName ${DATA_EXCHANGE_DIR}/baseline_${DATA_BASENAME_WE}.tar.gz --baselineCacheDirectory /tmp/geos/baselines || REBASELINE_STATUS=$?
fi
phase_finish "${REBASELINE_STATUS}"
INTEGRATED_TEST_EXIT_STATUS=1
fi
echo "Done!"
# INTEGRATED_TEST_EXIT_STATUS=$?
echo "The return code of the integrated tests is ${INTEGRATED_TEST_EXIT_STATUS}"
fi
# Cleaning the build directory.
phase_start "Clean build directory"
or_die cmake --build . --target clean
phase_finish 0
# Clean the repository
phase_start "Clean repository"
or_die cd ${GEOS_SRC_DIR}/inputFiles
find . -name '*.pyc' -delete
phase_finish 0
# If we're here, either everything went OK or we have to deal with the integrated tests manually.
if [[ ! -z "${INTEGRATED_TEST_EXIT_STATUS+x}" ]]; then
echo "Exiting the build process with exit status ${INTEGRATED_TEST_EXIT_STATUS} from the integrated tests."
exit ${INTEGRATED_TEST_EXIT_STATUS}
else
echo "Exiting the build process with exit status 0."
exit 0
fi