-
Notifications
You must be signed in to change notification settings - Fork 108
466 lines (425 loc) · 19.4 KB
/
Copy pathbuild-and-test-linux.yml
File metadata and controls
466 lines (425 loc) · 19.4 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
name: Build and Test (Linux)
permissions:
contents: write
on:
push:
branches:
- main
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# Filter to allow skipping the test if no relevant changes occurred.
filter:
runs-on: ubuntu-latest
outputs:
test: ${{ steps.filter.outputs.test }}
steps:
- uses: actions/checkout@v6
- uses: dorny/paths-filter@v4
id: filter
with:
filters: |
test:
- 'palace/**'
- 'cmake/**'
- 'extern/**'
- 'examples/**'
- 'test/unit/**'
- 'test/data/**'
- '.github/workflows/build-and-test-linux.yml'
build-and-test-linux:
needs: filter
strategy:
fail-fast: false
matrix:
include:
# x86 Intel stack.
- arch: x86
compiler: intel
mpi: intelmpi
math-libs: intelmkl
build-shared: shared
with-64bit-int: int64
with-openmp: openmp
with-eigensolver: slepc
with-solver: superlu
# x86 GCC / OpenBLAS open stack.
- arch: x86
compiler: gcc
mpi: openmpi
math-libs: openblas
build-shared: shared
with-64bit-int: int64
with-openmp: openmp
with-eigensolver: slepc
with-solver: strumpack
# arm GCC / OpenBLAS and ARPACK.
- arch: arm
compiler: gcc
mpi: openmpi
math-libs: openblas
build-shared: static
with-64bit-int: int64
with-openmp: serial
with-eigensolver: arpack
with-solver: mumps
# Sanitizer build (ASan/UBSan), x86 Clang.
- arch: x86
compiler: clang
mpi: openmpi
math-libs: openblas
build-shared: shared
with-64bit-int: int32
with-openmp: serial
with-eigensolver: slepc
with-solver: superlu
with-sanitizers: true
# Coverage build, x86 GCC.
- arch: x86
compiler: gcc
mpi: openmpi
math-libs: openblas
build-shared: shared
with-64bit-int: int64
with-openmp: openmp
with-eigensolver: slepc
with-solver: strumpack
with-coverage: true
# Flaky arm builds with ubuntu 24 means we pin to ubuntu 22
# (See comment above matrix)
runs-on: ${{ matrix.arch == 'x86' && 'palace_ubuntu-latest_16-core' || 'ubuntu-22.04-arm' }}
steps:
- uses: actions/checkout@v6
if: needs.filter.outputs.test == 'true'
with:
submodules: 'recursive'
- name: Update package lists
if: needs.filter.outputs.test == 'true'
run: |
sudo apt-get update
- name: Configure GNU compilers
if: needs.filter.outputs.test == 'true'
run: |
# Find latest gnu compiler versions
LATEST_GFORTRAN=$(apt-cache search '^gfortran-[0-9]+$' | sort -V | tail -n1 | cut -d' ' -f1)
LATEST_GCC=$(apt-cache search '^gcc-[0-9]+$' | sort -V | tail -n1 | cut -d' ' -f1)
LATEST_GPP=$(apt-cache search '^g\+\+-[0-9]+$' | sort -V | tail -n1 | cut -d' ' -f1)
GFORT_VERSION=$(echo $LATEST_GFORTRAN | cut -d'-' -f2)
GCC_VERSION=$(echo $LATEST_GCC | cut -d'-' -f2)
sudo apt-get install -y $LATEST_GFORTRAN $LATEST_GCC $LATEST_GPP
sudo update-alternatives --install $(which gfortran) gfortran $(which gfortran-$GFORT_VERSION) ${GFORT_VERSION}0
sudo update-alternatives --set gfortran $(which gfortran-$GFORT_VERSION)
sudo update-alternatives --install $(which gcc) gcc $(which gcc-$GCC_VERSION) ${GCC_VERSION}0 \
--slave $(which g++) g++ $(which g++-$GCC_VERSION) \
--slave $(which gcov) gcov $(which gcov-$GCC_VERSION) \
--slave $(which gcov-tool) gcov-tool $(which gcov-tool-$GCC_VERSION)
sudo update-alternatives --set gcc $(which gcc-$GCC_VERSION)
gfortran --version
gcc --version
g++ --version
- name: Configure LCOV
if: needs.filter.outputs.test == 'true' && matrix.with-coverage
run: |
sudo apt-get install -y lcov
- name: Configure Open MPI
if: needs.filter.outputs.test == 'true' && matrix.mpi == 'openmpi'
run: |
sudo apt-get install -y openmpi-bin libopenmpi-dev
- name: Configure MPICH
if: needs.filter.outputs.test == 'true' && matrix.mpi == 'mpich'
run: |
sudo apt-get install -y mpich libmpich-dev
- name: Configure Intel MPI
if: needs.filter.outputs.test == 'true' && matrix.mpi == 'intelmpi'
uses: mpi4py/setup-mpi@v1
with:
mpi: ${{ matrix.mpi }}
- name: Configure Clang compiler
if: needs.filter.outputs.test == 'true' && matrix.compiler == 'clang'
run: |
sudo apt-get install -y clang lld
if [[ "${{ matrix.with-openmp }}" == 'openmp' ]]; then
sudo apt-get install -y libomp-dev
fi
- name: Configure Intel oneAPI compiler
if: needs.filter.outputs.test == 'true' && matrix.compiler == 'intel'
run: |
sudo apt-get install -y intel-oneapi-compiler-dpcpp-cpp \
intel-oneapi-compiler-fortran
- name: Install math libraries (OpenBLAS)
if: needs.filter.outputs.test == 'true' && matrix.math-libs == 'openblas'
run: |
if [[ "${{ matrix.with-openmp }}" == 'openmp' ]]; then
sudo apt-get install -y libopenblas-openmp-dev
else
sudo apt-get install -y libopenblas-serial-dev
fi
- name: Install GCC OpenMP runtime
if: needs.filter.outputs.test == 'true' && matrix.with-openmp == 'openmp'
run: |
sudo apt-get install -y libgomp1
- name: Install math libraries (Intel oneAPI MKL)
if: needs.filter.outputs.test == 'true' && matrix.math-libs == 'intelmkl'
run: |
sudo apt-get install -y intel-oneapi-mkl intel-oneapi-mkl-devel
- name: Install math libraries (AOCL)
if: needs.filter.outputs.test == 'true' && matrix.math-libs == 'aocl'
run: |
wget -q https://download.amd.com/developer/eula/aocl/aocl-4-1/aocl-linux-gcc-4.1.0_1_amd64.deb
sudo apt-get install -y ./aocl-linux-gcc-4.1.0_1_amd64.deb
rm aocl-linux-gcc-*.deb
echo "AOCLROOT=/opt/AMD/aocl/aocl-linux-gcc-4.1.0/gcc" >> $GITHUB_ENV
- name: Install math libraries (ARMPL)
if: needs.filter.outputs.test == 'true' && matrix.math-libs == 'armpl'
run: |
wget -q https://developer.arm.com/-/cdn-downloads/permalink/Arm-Performance-Libraries/Version_24.10/arm-performance-libraries_24.10_deb_gcc.tar
tar -xf arm-performance-libraries* && rm -rf arm-performance-libraries*.tar
sudo ./arm-performance-libraries*/arm-performance-libraries*.sh -a -f -i /opt/arm
echo "ARMPL_DIR=/opt/arm/armpl_24.10_gcc" >> $GITHUB_ENV
- name: Test MPI installation
if: needs.filter.outputs.test == 'true'
run: |
# Create test program
cat > mpi_test.c << 'EOF'
#include <stdio.h>
#include <mpi.h>
int main(int argc, char *argv[]) {
int rank, size;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
printf("Process %d of %d\n", rank, size);
if (rank == 0) {
if (size == 1) {
printf("ERROR: MPI_Comm_size reports only 1 process\n");
return 1;
} else {
printf("SUCCESS: MPI running with %d processes\n", size);
}
}
MPI_Finalize();
return 0;
}
EOF
# Select correct compiler wrapper
if [[ "${{ matrix.mpi }}" == "mpich" ]]; then
MPICC="mpicc"
elif [[ "${{ matrix.mpi }}" == "openmpi" ]]; then
MPICC="mpicc"
elif [[ "${{ matrix.mpi }}" == "intelmpi" ]]; then
source /opt/intel/oneapi/setvars.sh
MPICC="mpiicx"
fi
$MPICC mpi_test.c -o mpi_test
mpirun -np 2 ./mpi_test
# Julia is needed for the antenna postprocessing example below; the
# regression sweep itself uses the Catch2 binary and doesn't need it.
- uses: julia-actions/setup-julia@v2
if: needs.filter.outputs.test == 'true'
with:
version: '1'
- uses: julia-actions/cache@v3
if: needs.filter.outputs.test == 'true'
- name: Build Palace
if: needs.filter.outputs.test == 'true'
env:
CMAKE_BUILD_TYPE: Release
NUM_PROC_BUILD_MAX: '32'
# Catch2 runs the binary as part of the cmake test discovery step. So,
# we want to avoid reporting leaks.
ASAN_OPTIONS: detect_leaks=0
run: |
# Configure environment
if [[ "${{ matrix.compiler }}" == 'intel' ]] || \
[[ "${{ matrix.mpi }}" == 'intelmpi' ]] || \
[[ "${{ matrix.math-libs }}" == 'intelmkl' ]]; then
source /opt/intel/oneapi/setvars.sh # Sets PATH, MKLROOT
if [[ "${{ matrix.compiler }}" == 'intel' ]]; then
export CC=icx
export CXX=icpx
export FC=ifx
fi
elif [[ "${{ matrix.compiler }}" == 'clang' ]]; then
export CC=clang
export CXX=clang++
export FC=gfortran
export LDFLAGS='-fuse-ld=lld'
elif [[ "${{ matrix.compiler }}" == 'gcc' ]]; then
export CC=gcc
export CXX=g++
export FC=gfortran
fi
export NUM_PROC_BUILD=$(nproc 2> /dev/null || sysctl -n hw.ncpu)
if [[ "$NUM_PROC_BUILD" -gt "$NUM_PROC_BUILD_MAX" ]]; then
NUM_PROC_BUILD=$NUM_PROC_BUILD_MAX
fi
if [[ "${{ matrix.math-libs }}" == 'aocl' ]]; then
export LD_LIBRARY_PATH="$AOCLROOT/lib:$LD_LIBRARY_PATH"
fi
if [[ "${{ matrix.math-libs }}" == 'armpl' ]]; then
export LD_LIBRARY_PATH="$ARMPL_DIR/lib:$LD_LIBRARY_PATH"
fi
[[ "${{ matrix.build-shared }}" == 'shared' ]] && BUILD_SHARED='ON' || BUILD_SHARED='OFF'
[[ "${{ matrix.with-64bit-int }}" == 'int64' ]] && WITH_INT64='ON' || WITH_INT64='OFF'
[[ "${{ matrix.with-openmp }}" == 'openmp' ]] && WITH_OPENMP='ON' || WITH_OPENMP='OFF'
[[ "${{ matrix.with-solver }}" == 'superlu' ]] && WITH_SUPERLU='ON' || WITH_SUPERLU='OFF'
[[ "${{ matrix.with-solver }}" == 'strumpack' ]] && WITH_STRUMPACK='ON' || WITH_STRUMPACK='OFF'
[[ "${{ matrix.with-solver }}" == 'mumps' ]] && WITH_MUMPS='ON' || WITH_MUMPS='OFF'
[[ "${{ matrix.with-eigensolver }}" == 'slepc' ]] && WITH_SLEPC='ON' || WITH_SLEPC='OFF'
[[ "${{ matrix.with-eigensolver }}" == 'arpack' ]] && WITH_ARPACK='ON' || WITH_ARPACK='OFF'
[[ "${{ matrix.with-sanitizers }}" == 'true' ]] && WITH_SANITIZERS='ON' || WITH_SANITIZERS='OFF'
[[ "${{ matrix.with-coverage }}" == 'true' ]] && WITH_COVERAGE='ON' || WITH_COVERAGE='OFF'
[[ "${{ matrix.with-butterflypack }}" == 'true' ]] && WITH_BUTTERFLYPACK='ON' || WITH_BUTTERFLYPACK='OFF'
# Build and install (with unit tests)
# Compile so that MFEM throws exceptions rather then aborts for unit-tests
# Unit [Parallel] cases run at PALACE_TESTS_NUMPROC ranks (small, fast);
# regression cases run at PALACE_REGRESSION_NUMPROC (~8 CPUs/case --
# fewer ranks shift tail eigenvalues past tolerance for some solvers).
# CTest derives PROCESSORS = ranks x threads and pins OMP_NUM_THREADS,
# so -j only bounds concurrency.
TEST_CORES=$(nproc 2> /dev/null || sysctl -n hw.ncpu)
if [[ "$TEST_CORES" -gt 8 ]]; then
TEST_CORES=8
fi
if [[ "${{ matrix.with-openmp }}" == 'openmp' ]]; then
TESTS_OMP_THREADS=2
else
TESTS_OMP_THREADS=1
fi
REGRESSION_NUMPROC=$(( TEST_CORES / TESTS_OMP_THREADS ))
if [[ "$REGRESSION_NUMPROC" -lt 1 ]]; then
REGRESSION_NUMPROC=1
fi
mkdir palace-build && cd palace-build
cmake .. \
-DCMAKE_INSTALL_PREFIX=$(pwd)/../palace-install \
-DPALACE_TESTS_NUMPROC=2 \
-DPALACE_REGRESSION_NUMPROC=$REGRESSION_NUMPROC \
-DPALACE_TESTS_OMP_THREADS=$TESTS_OMP_THREADS \
-DBUILD_SHARED_LIBS=$BUILD_SHARED \
-DPALACE_WITH_64BIT_INT=$WITH_INT64 \
-DPALACE_WITH_OPENMP=$WITH_OPENMP \
-DPALACE_WITH_SUPERLU=$WITH_SUPERLU \
-DPALACE_WITH_STRUMPACK=$WITH_STRUMPACK \
-DPALACE_WITH_STRUMPACK_BUTTERFLYPACK=$WITH_BUTTERFLYPACK \
-DPALACE_WITH_MUMPS=$WITH_MUMPS \
-DPALACE_WITH_SLEPC=$WITH_SLEPC \
-DPALACE_WITH_ARPACK=$WITH_ARPACK \
-DPALACE_MFEM_USE_EXCEPTIONS=ON \
-DPALACE_BUILD_WITH_SANITIZERS=$WITH_SANITIZERS \
-DPALACE_BUILD_WITH_COVERAGE=$WITH_COVERAGE \
-DPALACE_MFEM_USE_EXCEPTIONS=ON
make -j$NUM_PROC_BUILD palace-tests
- name: Run unit tests
if: needs.filter.outputs.test == 'true'
timeout-minutes: 10
run: |
# Configure environment
if [[ "${{ matrix.compiler }}" == 'intel' ]] || \
[[ "${{ matrix.mpi }}" == 'intelmpi' ]] || \
[[ "${{ matrix.math-libs }}" == 'intelmkl' ]]; then
source /opt/intel/oneapi/setvars.sh # Sets PATH, MKLROOT
fi
export LD_LIBRARY_PATH=$(pwd)/palace-install/lib:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$(pwd)/palace-install/lib64:$LD_LIBRARY_PATH
if [[ "${{ matrix.math-libs }}" == 'aocl' ]]; then
export LD_LIBRARY_PATH="$AOCLROOT/lib:$LD_LIBRARY_PATH"
fi
if [[ "${{ matrix.math-libs }}" == 'armpl' ]]; then
export LD_LIBRARY_PATH="$ARMPL_DIR/lib:$LD_LIBRARY_PATH"
fi
# Configure sanitizer options
if [[ "${{ matrix.with-sanitizers }}" == 'true' ]]; then
export ASAN_OPTIONS=detect_leaks=1
export UBSAN_OPTIONS=print_stacktrace=1
# Suppress known leaks in third-party MPI/hwloc/libCEED libraries.
# See test/unit/lsan_suppressions.txt for the suppression list.
export LSAN_OPTIONS="suppressions=$GITHUB_WORKSPACE/test/unit/lsan_suppressions.txt"
fi
# We don't compute coverage for the entire matrix because it slows
# everything down.
if [[ "${{ matrix.with-coverage }}" == 'true' ]]; then
scripts/measure-test-coverage report $(pwd)/palace-build
else
cd $(pwd)/palace-build/palace-build/test/unit
# Skip [Regression] and [Long] cases here; they have their
# own slower steps further down. CTest pins OMP_NUM_THREADS and
# reserves PROCESSORS per case, so -j just bounds concurrency.
ctest -j "$(nproc 2> /dev/null || sysctl -n hw.ncpu)" --output-on-failure -LE "^(regression|long)$"
fi
# NOTE: Fork PRs get a read-only GITHUB_TOKEN and cannot push to gh-pages;
# for those the coverage report is still available via the artifact below.
- name: Deploy coverage to gh-pages
if: |
needs.filter.outputs.test == 'true' && matrix.with-coverage &&
(github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: palace-build/coverage_html
destination_dir: ${{ github.event_name == 'pull_request' && format('previews/PR{0}/coverage', github.event.pull_request.number) || format('coverage/{0}', github.sha) }}
keep_files: true
- name: Add coverage link to summary
if: |
needs.filter.outputs.test == 'true' && matrix.with-coverage &&
(github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
env:
EVENT_NAME: ${{ github.event_name }}
PR_NUMBER: ${{ github.event.pull_request.number }}
SHA: ${{ github.sha }}
run: |
if [[ "$EVENT_NAME" == "pull_request" ]]; then
COVERAGE_URL="https://awslabs.github.io/palace/previews/PR${PR_NUMBER}/coverage/"
else
COVERAGE_URL="https://awslabs.github.io/palace/coverage/${SHA}/"
fi
echo "### Coverage Report" >> "$GITHUB_STEP_SUMMARY"
echo "View the coverage report at: [Coverage Report](${COVERAGE_URL})" >> "$GITHUB_STEP_SUMMARY"
- name: Upload coverage report
if: needs.filter.outputs.test == 'true' && matrix.with-coverage
uses: actions/upload-artifact@v7
with:
name: coverage-${{ matrix.arch }}-${{ matrix.compiler }}-${{ matrix.mpi }}
path: palace-build/coverage_html/
- name: Run regression tests
if: needs.filter.outputs.test == 'true' && !matrix.with-sanitizers
# CTest owns per-case scheduling for the [Regression] cases (label
# "regression"). The configured MPI rank count and PROCESSORS property
# reserve matching ctest slots so -j can fan out safely.
timeout-minutes: 60
run: |
# Configure environment
if [[ "${{ matrix.compiler }}" == 'intel' ]] || \
[[ "${{ matrix.mpi }}" == 'intelmpi' ]] || \
[[ "${{ matrix.math-libs }}" == 'intelmkl' ]]; then
source /opt/intel/oneapi/setvars.sh # Sets PATH, MKLROOT
fi
if [[ "${{ matrix.math-libs }}" == 'aocl' ]]; then
export LD_LIBRARY_PATH="$AOCLROOT/lib:$LD_LIBRARY_PATH"
fi
if [[ "${{ matrix.math-libs }}" == 'armpl' ]]; then
export LD_LIBRARY_PATH="$ARMPL_DIR/lib:$LD_LIBRARY_PATH"
fi
export PATH=$(pwd)/palace-install/bin:$PATH
cd palace-build/palace-build/test/unit
# CTest pins OMP_NUM_THREADS and reserves PROCESSORS per case, so -j is
# just an upper bound on concurrency and can safely be all cores.
ctest -j "$(nproc 2> /dev/null || sysctl -n hw.ncpu)" --output-on-failure -L "^regression$"
- name: Check antenna postprocessing example
# The Julia plot script that ships with the antenna example is
# exercised here so it doesn't bit-rot. Gated to one matrix entry
# to avoid paying the ~7 min Julia precompile per job. Reads the
# farfield CSV the regression step left in the staging tree.
if: needs.filter.outputs.test == 'true' && matrix.arch == 'x86' && matrix.compiler == 'gcc' && matrix.math-libs == 'openblas' && !matrix.with-coverage
run: |
STAGE_ROOT="${TMPDIR:-/tmp}/palace-regression"
FARFIELD="$STAGE_ROOT/antenna/antenna_short_dipole/postpro/antenna_short_dipole/farfield-rE.csv"
if [[ ! -f "$FARFIELD" ]]; then
echo "Expected regression output missing: $FARFIELD" >&2
exit 1
fi
julia --project=examples -e 'using Pkg; Pkg.instantiate()'
julia --project=examples examples/antenna/plot_farfield.jl --model short_dipole --file "$FARFIELD"