Skip to content

Commit 4bf9299

Browse files
committed
ci(refactor): extract setup logic into setup-build
Create a new composite action "setup-build" by extracting common setup logic from the test/deploy workflows. This action sets up the build environment and runs the configure script.
1 parent f2df9bf commit 4bf9299

File tree

3 files changed

+441
-231
lines changed

3 files changed

+441
-231
lines changed
Lines changed: 381 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,381 @@
1+
name: setup-build
2+
description: >-
3+
Set up the build environment by installing necessary tools and dependencies.
4+
It sets the CONFIG_ARGS environment variable. If necessary, it also sets
5+
CPATH, LIBRARY_PATH, FORMPATH and DISTNAME, and runs autoreconf or extracts
6+
a tar.gz archive, and lastly invokes ./configure.
7+
inputs:
8+
features:
9+
description: A list of features to enable, separated by spaces.
10+
required: false
11+
default: ''
12+
13+
runs:
14+
using: composite
15+
steps:
16+
- name: Initialize setup
17+
id: setup
18+
env:
19+
available_features: >-
20+
form=false tform=false parform=false vorm=false tvorm=false parvorm=false
21+
gmp=true mpfr=true zlib=true zstd=true
22+
mpich=false openmpi=false
23+
bash=false msys2=false debian=false container=false
24+
untar=false autoreconf=false configure=true deploy=false
25+
valgrind=false coverage=false
26+
latex=false latex2html=false
27+
formlib=false forcer=false color=false
28+
shell: bash
29+
run: |
30+
### Initialize setup ###
31+
32+
# First, parse the input.
33+
34+
available_features=$(echo "$available_features" | awk '{$1=$1; print}')
35+
36+
feature_names=''
37+
for entry in $available_features; do
38+
# Get "name=value".
39+
feat_name=$(echo "$entry" | cut -d '=' -f 1)
40+
feat_value=$(echo "$entry" | cut -d '=' -f 2)
41+
feature_names="$feature_names $feat_name"
42+
# Initialize the feature variable.
43+
eval "feat_${feat_name}=${feat_value}"
44+
done
45+
feature_names=$(echo "$feature_names" | awk '{$1=$1; print}')
46+
47+
input_features=$(echo "${{ inputs.features }}" | awk '{$1=$1; print}')
48+
feature_pattern='^('$(echo "$feature_names" | tr ' ' '|')')$'
49+
no_feature_pattern='^('$(echo "no$feature_names" | sed 's/ /|no/g')')$'
50+
51+
for feat in $input_features; do
52+
if [[ $feat =~ $feature_pattern ]]; then
53+
eval "feat_${feat}=true"
54+
elif [[ $feat =~ $no_feature_pattern ]]; then
55+
feat=${feat#no}
56+
eval "feat_${feat}=false"
57+
else
58+
echo "Error: unknown feature: $feat"
59+
exit 1
60+
fi
61+
done
62+
63+
# Some features are enabled automatically,
64+
# while others may require extra setup.
65+
66+
has_scalar=false
67+
has_threaded=false
68+
has_mpi=false
69+
has_bin=false
70+
if $feat_form || $feat_vorm; then
71+
has_scalar=true
72+
fi
73+
if $feat_tform || $feat_tvorm; then
74+
has_threaded=true
75+
fi
76+
if $feat_parform || $feat_parvorm; then
77+
has_mpi=true
78+
fi
79+
if $has_scalar || $has_threaded || $has_mpi; then
80+
has_bin=true
81+
fi
82+
83+
if ! $has_bin; then
84+
feat_gmp=false
85+
feat_mpfr=false
86+
feat_zlib=false
87+
feat_zstd=false
88+
fi
89+
90+
if $has_mpi && ! $feat_mpich && ! $feat_openmpi; then
91+
# Use OpenMPI by default because MPICH is broken on Ubuntu 24.04.
92+
# See: https://bugs.launchpad.net/ubuntu/+source/mpich/+bug/2072338
93+
feat_openmpi=true
94+
fi
95+
96+
if [ -f /etc/os-release ] && grep -qi '^ID=debian' /etc/os-release; then
97+
feat_debian=true
98+
fi
99+
100+
if [ -f /.dockerenv ]; then
101+
feat_container=true
102+
fi
103+
104+
if [ ! -f configure.ac ] && ls form-*.tar.gz 1>/dev/null 2>&1; then
105+
feat_untar=true
106+
fi
107+
108+
if [ ! -f configure ] && [ -f configure.ac ]; then
109+
feat_autoreconf=true
110+
fi
111+
112+
if $feat_color || $feat_forcer; then
113+
feat_formlib=true
114+
items=''
115+
$feat_color && items="$items-color"
116+
$feat_forcer && items="$items-forcer"
117+
echo "formlib-key=formlib$items" >>"$GITHUB_OUTPUT"
118+
echo "formlib-restore-keys=formlib-" >>"$GITHUB_OUTPUT"
119+
echo "FORMPATH=${{ github.workspace }}/formlib" >>"$GITHUB_ENV"
120+
fi
121+
122+
# Output the feature variables.
123+
124+
for feat in $feature_names; do
125+
value=$(eval "echo \$feat_${feat}")
126+
echo "$feat=$value"
127+
echo "$feat=$value" >>"$GITHUB_OUTPUT"
128+
done
129+
130+
# Make CONFIG_ARGS.
131+
132+
args='--disable-dependency-tracking'
133+
134+
if $has_scalar; then
135+
args="$args --enable-scalar"
136+
else
137+
args="$args --disable-scalar"
138+
fi
139+
if $has_threaded; then
140+
args="$args --enable-threaded"
141+
else
142+
args="$args --disable-threaded"
143+
fi
144+
if $has_mpi; then
145+
args="$args --enable-parform"
146+
else
147+
args="$args --disable-parform"
148+
fi
149+
150+
if $feat_vorm || $feat_tvorm || $feat_parvorm; then
151+
args="$args --enable-debug"
152+
fi
153+
154+
if $feat_coverage; then
155+
args="$args --enable-coverage"
156+
fi
157+
158+
if $has_bin; then
159+
if $feat_deploy; then
160+
# Avoid compiler optimizations specific to the build host.
161+
args="$args --disable-native"
162+
# Enable static linking whenever possible.
163+
if ${{ runner.os != 'macOS' }}; then
164+
args="$args --enable-static-link"
165+
fi
166+
fi
167+
168+
if ${{ runner.os == 'Windows' }}; then
169+
args="$args --with-api=windows"
170+
else
171+
args="$args --with-api=posix"
172+
fi
173+
else
174+
# This avoids checking optimization flags.
175+
args="$args --disable-native"
176+
fi
177+
178+
if $feat_gmp; then
179+
args="$args --with-gmp"
180+
else
181+
args="$args --without-gmp"
182+
fi
183+
184+
if $feat_mpfr; then
185+
args="$args --with-mpfr"
186+
else
187+
args="$args --without-mpfr"
188+
fi
189+
190+
if $feat_zlib; then
191+
args="$args --with-zlib"
192+
else
193+
args="$args --without-zlib"
194+
fi
195+
196+
if $feat_zstd; then
197+
args="$args --with-zstd"
198+
else
199+
args="$args --without-zstd"
200+
fi
201+
202+
echo "CONFIG_ARGS=$args" >>"$GITHUB_ENV"
203+
204+
# Determine DISTNAME if possible.
205+
206+
if [ -f ./scripts/git-version-gen.sh ] && command -v git >/dev/null 2>&1; then
207+
echo "DISTNAME=form-$(./scripts/git-version-gen.sh -r | sed '2q;d' | sed 's/^v//')" >>"$GITHUB_ENV"
208+
fi
209+
210+
# awalsh128/cache-apt-pkgs-action does not allow empty packages argument.
211+
# See: https://github.com/awalsh128/cache-apt-pkgs-action/issues/149
212+
# As a workaround, we need to check if any packages need to be installed.
213+
214+
# We also handle another bug that may cause cache key conflicts.
215+
# See: https://github.com/awalsh128/cache-apt-pkgs-action/pull/150
216+
- name: Install dependencies (Ubuntu)
217+
if: >-
218+
runner.os == 'Linux' && steps.setup.outputs.container == 'false' && (
219+
steps.setup.outputs.coverage == 'true' ||
220+
steps.setup.outputs.mpfr == 'true' ||
221+
steps.setup.outputs.zstd == 'true' ||
222+
steps.setup.outputs.valgrind == 'true'
223+
)
224+
uses: awalsh128/cache-apt-pkgs-action@v1
225+
with:
226+
packages: >-
227+
${{ steps.setup.outputs.coverage == 'true' && 'lcov' || '' }}
228+
${{ steps.setup.outputs.mpfr == 'true' && 'libmpfr-dev' || '' }}
229+
${{ steps.setup.outputs.zstd == 'true' && 'libzstd-dev' || '' }}
230+
${{ steps.setup.outputs.valgrind == 'true' && 'valgrind' || '' }}
231+
version: ${{ runner.arch }}-1.0
232+
233+
# Because we use i386/debian containers, here we do not assume that
234+
# awalsh128/cache-apt-pkgs-action works properly.
235+
# See: https://github.com/actions/cache/issues/675
236+
- name: Install dependencies (Debian)
237+
if: steps.setup.outputs.debian == 'true' && steps.setup.outputs.container == 'true'
238+
shell: bash
239+
run: |
240+
### Install dependencies ###
241+
242+
apt-get update
243+
apt-get install -y -q automake g++ git make ruby \
244+
${{ steps.setup.outputs.coverage == 'true' && 'lcov' || '' }} \
245+
${{ steps.setup.outputs.gmp == 'true' && 'libgmp-dev' || '' }} \
246+
${{ steps.setup.outputs.mpfr == 'true' && 'libmpfr-dev' || '' }} \
247+
${{ steps.setup.outputs.zstd == 'true' && 'libzstd-dev' || '' }} \
248+
${{ steps.setup.outputs.valgrind == 'true' && 'valgrind' || '' }} \
249+
${{ steps.setup.outputs.zlib == 'true' && 'zlib1g-dev' || '' }}
250+
251+
- name: Install dependencies (Windows)
252+
if: runner.os == 'Windows' && steps.setup.outputs.msys2 == 'true'
253+
uses: msys2/setup-msys2@v2
254+
with:
255+
update: true
256+
install: >-
257+
make
258+
mingw-w64-x86_64-gcc
259+
mingw-w64-x86_64-ruby
260+
${{ steps.setup.outputs.gmp == 'true' && 'mingw-w64-x86_64-gmp' || '' }}
261+
${{ steps.setup.outputs.mpfr == 'true' && 'mingw-w64-x86_64-mpfr' || '' }}
262+
${{ steps.setup.outputs.zlib == 'true' && 'mingw-w64-x86_64-zlib' || '' }}
263+
${{ steps.setup.outputs.zstd == 'true' && 'mingw-w64-x86_64-zstd' || '' }}
264+
265+
# awalsh128/cache-apt-pkgs-action does not work for MPI.
266+
# See: https://github.com/awalsh128/cache-apt-pkgs-action/issues/57#issuecomment-1304062077
267+
- name: Install MPI if necessary
268+
if: runner.os == 'Linux' && (steps.setup.outputs.mpich == 'true' || steps.setup.outputs.openmpi == 'true')
269+
shell: bash
270+
run: |
271+
### Install MPI ###
272+
273+
sudo apt-get update
274+
sudo apt-get install -y -q \
275+
${{ steps.setup.outputs.mpich == 'true' && 'libmpich-dev' || '' }} \
276+
${{ steps.setup.outputs.openmpi == 'true' && 'libopenmpi-dev' || '' }}
277+
278+
# Currently, cache-apt-pkgs-action doesn't work for LaTeX.
279+
# See: https://github.com/awalsh128/cache-apt-pkgs-action/issues/57
280+
- name: Install LaTeX.
281+
if: runner.os == 'Linux' && (steps.setup.outputs.latex == 'true' || steps.setup.outputs.latex2html == 'true')
282+
shell: bash
283+
run: |
284+
### Install LaTeX ###
285+
286+
sudo apt-get update
287+
sudo apt-get install -y -q \
288+
${{ steps.setup.outputs.latex == 'true' && 'texlive-latex-extra' || '' }} \
289+
${{ steps.setup.outputs.latex2html == 'true' && 'latex2html' || '' }}
290+
291+
# --static fails on macOS but we want to statically link
292+
# the brewed gmp. The linker supports neither -Wl,-static nor
293+
# -l:libgmp.a to make partial static links possible.
294+
# As a workaround, we make a library directory with libgmp.a
295+
# but without libgmp.dylib so that the linker has to link libgmp.a.
296+
# Note that the Homebrew installation path for Apple Silicon (arm64)
297+
# differs from the one on macOS Intel (x86-64).
298+
- name: Set up statically linked libraries (macOS)
299+
if: runner.os == 'macOS' && steps.setup.outputs.deploy
300+
shell: bash
301+
run: |
302+
### Set up statically linked libraries ###
303+
304+
mkdir static-lib
305+
if [ "$RUNNER_ARCH" == "ARM64" ]; then
306+
brew_dir=/opt/homebrew
307+
# Include directories, not located in the usual places,
308+
# must be explicitly appended to the include paths.
309+
export CPATH="$brew_dir/opt/gmp/include:$brew_dir/opt/mpfr/include:/opt/homebrew/opt/zstd/include:${CPATH:-}"
310+
echo "CPATH=$CPATH" >>"$GITHUB_ENV"
311+
else
312+
brew_dir=/usr/local
313+
fi
314+
ln -s $brew_dir/opt/gmp/lib/libgmp.a static-lib/libgmp.a
315+
ln -s $brew_dir/opt/mpfr/lib/libmpfr.a static-lib/libmpfr.a
316+
ln -s $brew_dir/opt/zstd/lib/libzstd.a static-lib/libzstd.a
317+
export LIBRARY_PATH="$(pwd)/static-lib:${LIBRARY_PATH:-}"
318+
echo "LIBRARY_PATH=$LIBRARY_PATH" >>"$GITHUB_ENV"
319+
320+
- name: Cache FORM library
321+
id: cache-formlib
322+
if: steps.setup.outputs.formlib == 'true'
323+
uses: actions/cache@v4
324+
with:
325+
path: formlib
326+
key: ${{ steps.setup.outputs.formlib-key }}
327+
restore-keys: ${{ steps.setup.outputs.formlib-restore-keys }}
328+
329+
- name: Install FORM libraries if necessary
330+
if: steps.setup.outputs.formlib == 'true' && steps.cache-formlib.outputs.cache-hit != 'true'
331+
shell: bash
332+
run: |
333+
### Install FORM libraries ###
334+
335+
mkdir -p formlib
336+
if ${{ steps.setup.outputs.forcer }} && [ ! -f formlib/forcer.h ]; then
337+
wget https://github.com/benruijl/forcer/archive/v1.0.0.tar.gz -O - | tar -x --gzip
338+
mv forcer-1.0.0/forcer.h formlib
339+
mv forcer-1.0.0/forcer formlib
340+
rm -rf forcer-1.0.0
341+
fi
342+
if ${{ steps.setup.outputs.color }} && [ ! -f formlib/color.h ]; then
343+
wget https://www.nikhef.nl/~form/maindir/packages/color/color.h -P formlib
344+
fi
345+
346+
# Fix dubious ownership in containers for Git operations.
347+
# See: https://github.com/actions/runner/issues/2033#issuecomment-1204205989
348+
- name: Fix dubious ownership
349+
if: steps.setup.outputs.container == 'true'
350+
shell: bash
351+
run: |
352+
### Fix dubious ownership
353+
354+
chown -R $(id -u):$(id -g) $PWD
355+
356+
- name: Uncompress tarball
357+
if: steps.setup.outputs.untar == 'true'
358+
shell: bash
359+
run: |
360+
### Uncompress tarball ###
361+
362+
echo "DISTNAME=$(basename form-*.tar.gz .tar.gz)" >>"$GITHUB_ENV"
363+
364+
tar -xf form-*.tar.gz --strip-components 1
365+
rm *.tar.gz
366+
367+
- name: Run Autoreconf
368+
if: steps.setup.outputs.autoreconf == 'true'
369+
shell: bash
370+
run: |
371+
### Run Autoreconf ###
372+
373+
autoreconf -i
374+
375+
- name: Configure
376+
if: steps.setup.outputs.configure == 'true'
377+
shell: ${{ steps.setup.outputs.msys2 == 'true' && 'msys2' || 'bash' }} {0}
378+
run: |
379+
### Configure ###
380+
381+
./configure ${{ env.CONFIG_ARGS }}

0 commit comments

Comments
 (0)