Repository navigation
GitHub Actions CI: CPU build tests for GNU, Intel OneAPI, and NVHPC #1524
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
cenamiller
wants to merge
5
commits into
MPAS-Dev:develop
Choose a base branch
from
NCAR:feature/ci-cpu-testing
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
96b9518
Add GitHub Actions build tests for GNU, Intel OneAPI, and NVHPC (MPICH)
cenamiller 917b431
Use the container's PIO setting and drop USE_PIO2
cenamiller bc87306
Remove the make-flags input
cenamiller fd116a0
Raise the default build timeout to 30 minutes
cenamiller 755d8a4
Build in single precision by default
cenamiller File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,207 @@ | ||
| name: 'Build MPAS' | ||
| description: 'Build MPAS-Atmosphere with specified compiler configuration' | ||
|
|
||
| inputs: | ||
| compiler: | ||
| description: 'Compiler family (gcc, nvhpc, oneapi, etc.)' | ||
| required: true | ||
| core: | ||
| description: 'MPAS core to build (atmosphere, init_atmosphere)' | ||
| required: false | ||
| default: 'atmosphere' | ||
| use-pio: | ||
| description: 'Use PIO library (true/false)' | ||
| required: false | ||
| default: 'false' | ||
| openacc: | ||
| description: 'Enable OpenACC (true/false)' | ||
| required: false | ||
| default: 'false' | ||
| precision: | ||
| description: 'Floating-point precision (single or double)' | ||
| required: false | ||
| default: 'single' | ||
| build-timeout: | ||
| description: 'Build timeout in minutes' | ||
| required: false | ||
| default: '30' | ||
| make-target: | ||
| description: 'Make target to build. Empty = look up MAKE_TARGET_<compiler> in ci-config.env' | ||
| required: false | ||
| default: '' | ||
| use-target-arch: | ||
| description: 'NVHPC only: add NVHPC_TARGET_ARCH from ci-config.env to the build flags (true/false). Use false for performance runs.' | ||
| required: false | ||
| default: 'true' | ||
| clean-core: | ||
| description: 'Run "make clean" for this core before building (use when a different core was built earlier in the same source tree; empty = no clean)' | ||
| required: false | ||
| default: '' | ||
| log-file: | ||
| description: 'Write the build output to this file as well as the job log (empty = no file)' | ||
| required: false | ||
| default: '' | ||
|
|
||
| outputs: | ||
| executable: | ||
| description: 'Path to built executable (atmosphere_model or init_atmosphere_model)' | ||
| value: ${{ steps.build.outputs.executable }} | ||
|
|
||
| runs: | ||
| using: 'composite' | ||
| steps: | ||
| - name: Build MPAS-A | ||
| id: build | ||
| shell: bash | ||
| env: | ||
| INPUT_MAKE_TARGET: ${{ inputs.make-target }} | ||
| run: | | ||
| # Source container environment if available | ||
| # This sets NETCDF, PNETCDF, PIO, and other library paths | ||
| if [ -f /container/config_env.sh ]; then | ||
| echo "Sourcing container environment from /container/config_env.sh" | ||
| source /container/config_env.sh | ||
| fi | ||
|
|
||
| # MPAS Makefile uses `cpp` for Registry preprocessing. | ||
| # Some containers (e.g. openSUSE Leap) don't ship it separately. | ||
| if ! command -v cpp &>/dev/null; then | ||
| echo "cpp not found, installing..." | ||
| if command -v zypper &>/dev/null; then | ||
| zypper install -y --no-recommends cpp | ||
| elif command -v dnf &>/dev/null; then | ||
| dnf install -y cpp | ||
| elif command -v apt-get &>/dev/null; then | ||
| apt-get update && apt-get install -y cpp | ||
| fi | ||
| fi | ||
|
|
||
| # Map compiler input to COMPILER_FAMILY if not already set | ||
| # The container should set this, but we provide a fallback | ||
| if [ -z "${COMPILER_FAMILY}" ]; then | ||
| case "${{ inputs.compiler }}" in | ||
| gcc|gfortran|gnu) export COMPILER_FAMILY="gcc" ;; | ||
| nvhpc|nvfortran) export COMPILER_FAMILY="nvhpc" ;; | ||
| oneapi|intel|ifx) export COMPILER_FAMILY="oneapi" ;; | ||
| llvm|flang|clang) export COMPILER_FAMILY="clang" ;; | ||
| *) export COMPILER_FAMILY="${{ inputs.compiler }}" ;; | ||
| esac | ||
| fi | ||
|
|
||
| # Set up I/O configuration. The container sets PIO; the MPAS Makefile | ||
| # builds with PIO when it is set and with SMIOL when it is not. | ||
| if [ "${{ inputs.use-pio }}" = "true" ]; then | ||
| if [ -z "${PIO}" ]; then | ||
| echo "::error::use-pio is true but PIO is not set by the container environment" | ||
| exit 1 | ||
| fi | ||
| else | ||
| unset PIO | ||
| fi | ||
|
|
||
| # Set up accelerator configuration | ||
| if [ "${{ inputs.openacc }}" = "true" ]; then | ||
| export OPENACC=true | ||
| fi | ||
|
|
||
| echo "Build configuration:" | ||
| echo " Core: ${{ inputs.core }}" | ||
| echo " Compiler input: ${{ inputs.compiler }}" | ||
| echo " COMPILER_FAMILY: ${COMPILER_FAMILY}" | ||
| echo " NETCDF: ${NETCDF:-not set}" | ||
| echo " PNETCDF: ${PNETCDF:-not set}" | ||
| echo " PIO: ${PIO:-not set}" | ||
| echo " OPENACC: ${OPENACC:-false}" | ||
| echo " PRECISION: ${{ inputs.precision }}" | ||
|
|
||
| # Settings from .github/ci-config.env — edit that file to change targets/workarounds | ||
| CI_CONFIG="${GITHUB_WORKSPACE}/.github/ci-config.env" | ||
| if [ -f "${CI_CONFIG}" ]; then | ||
| source "${CI_CONFIG}" | ||
| fi | ||
|
|
||
| # Make target: the make-target input, else MAKE_TARGET_<compiler> from ci-config.env | ||
| MAKE_TARGET="${INPUT_MAKE_TARGET}" | ||
| if [ -z "${MAKE_TARGET}" ]; then | ||
| VARNAME="MAKE_TARGET_${COMPILER_FAMILY}" | ||
| MAKE_TARGET="${!VARNAME}" | ||
| fi | ||
| if [ -z "${MAKE_TARGET}" ]; then | ||
| echo "::error::No make target for COMPILER_FAMILY=${COMPILER_FAMILY}. Add MAKE_TARGET_${COMPILER_FAMILY}= to .github/ci-config.env" | ||
| exit 1 | ||
| fi | ||
|
|
||
| case "${{ inputs.core }}" in | ||
| atmosphere) EXE="atmosphere_model" ;; | ||
| init_atmosphere) EXE="init_atmosphere_model" ;; | ||
| *) echo "::error::Unsupported core '${{ inputs.core }}' (atmosphere, init_atmosphere)"; exit 1 ;; | ||
| esac | ||
|
|
||
| if [ "${COMPILER_FAMILY}" = "nvhpc" ]; then | ||
| ARCH="" | ||
| if [ "${{ inputs.use-target-arch }}" = "true" ]; then | ||
| ARCH="${NVHPC_TARGET_ARCH}" | ||
| fi | ||
| if [ -n "${ARCH}" ]; then | ||
| for FLAG in FFLAGS_OPT CFLAGS_OPT CXXFLAGS_OPT LDFLAGS_OPT; do | ||
| if ! sed -n "/^${MAKE_TARGET}:/,/^[a-z]/p" Makefile | grep -qF "\"${FLAG} = "; then | ||
| echo "::error::Could not locate ${FLAG} in the '${MAKE_TARGET}' Makefile target" | ||
| exit 1 | ||
| fi | ||
| # Skip if already applied (the action can run twice in one source tree) | ||
| if sed -n "/^${MAKE_TARGET}:/,/^[a-z]/p" Makefile | grep -qF "\"${FLAG} = ${ARCH} "; then | ||
| continue | ||
| fi | ||
| sed -i "/^${MAKE_TARGET}:/,/^[a-z]/ s/\"${FLAG} = /\"${FLAG} = ${ARCH} /" Makefile | ||
| done | ||
| fi | ||
| fi | ||
|
|
||
| # Compiler defaults from ci-config.env (<FAMILY>_EXTRA_MAKE_FLAGS, family in | ||
| # upper case) | ||
| FLAGS_VAR="${COMPILER_FAMILY^^}_EXTRA_MAKE_FLAGS" | ||
| EXTRA_MAKE_FLAGS="${!FLAGS_VAR}" | ||
|
|
||
| echo " Make target: ${MAKE_TARGET}" | ||
| echo " Extra make flags: ${EXTRA_MAKE_FLAGS:-<none>}" | ||
| echo " Parallel jobs: ${MAKE_J_PROCS:-$(nproc)}" | ||
|
|
||
| PRECISION_FLAG="" | ||
| if [ "${{ inputs.precision }}" = "double" ]; then | ||
| PRECISION_FLAG="PRECISION=double" | ||
| fi | ||
|
|
||
| CLEAN_CMD="" | ||
| if [ -n "${{ inputs.clean-core }}" ]; then | ||
| CLEAN_CMD="make clean CORE=${{ inputs.clean-core }}" | ||
| fi | ||
| MAKE_CMD="timeout ${{ inputs.build-timeout }}m make ${MAKE_TARGET} CORE=${{ inputs.core }} ${EXTRA_MAKE_FLAGS} ${PRECISION_FLAG} --jobs ${MAKE_J_PROCS:-$(nproc)}" | ||
|
|
||
| LOG_FILE="${{ inputs.log-file }}" | ||
| set -o pipefail | ||
| # Tool versions and the exact commands (job log + log file) | ||
| PRINT_ENV="${GITHUB_WORKSPACE}/.github/actions/build-mpas/print-build-env.sh" | ||
| if [ ! -f "${PRINT_ENV}" ]; then | ||
| echo "::error::${PRINT_ENV} not found" | ||
| exit 1 | ||
| fi | ||
| export COMPILER_FAMILY MAKE_TARGET ARCH CLEAN_CMD MAKE_CMD | ||
| CORE="${{ inputs.core }}" USE_PIO="${{ inputs.use-pio }}" PRECISION="${{ inputs.precision }}" \ | ||
| bash "${PRINT_ENV}" | tee "${LOG_FILE:-/dev/null}" | ||
| if [ -n "${CLEAN_CMD}" ]; then | ||
| ${CLEAN_CMD} 2>&1 | tee -a "${LOG_FILE:-/dev/null}" | ||
| fi | ||
| ${MAKE_CMD} 2>&1 | tee -a "${LOG_FILE:-/dev/null}" | ||
|
|
||
| echo "executable=$(pwd)/${EXE}" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Verify executable | ||
| shell: bash | ||
| run: | | ||
| EXE="${{ steps.build.outputs.executable }}" | ||
| if [ ! -f "${EXE}" ]; then | ||
| echo "::error::${EXE} not found" | ||
| exit 1 | ||
| fi | ||
| ls -la "${EXE}" | ||
| file "${EXE}" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| #!/usr/bin/env bash | ||
| # Print the tool versions and exact commands for an MPAS build. | ||
| # Called by action.yml; the output goes to the job log and the build log. | ||
| # | ||
| # Environment variables set by the action: | ||
| # COMPILER_FAMILY gcc, oneapi, nvhpc, ... | ||
| # MAKE_TARGET make target | ||
| # CORE atmosphere or init_atmosphere | ||
| # USE_PIO true or false | ||
| # PRECISION single or double | ||
| # ARCH NVHPC target arch flag that was applied (empty = none) | ||
| # CLEAN_CMD clean command run before the build (empty = none) | ||
| # MAKE_CMD the build command | ||
| # NETCDF, PNETCDF and PIO come from the container environment. | ||
|
|
||
| # First non-empty line of a command's output, or "not found" | ||
| v() { command -v "$1" >/dev/null 2>&1 || { echo "not found"; return 0; }; { "$@" 2>&1 || true; } | grep -m1 . || true; } | ||
| case "${COMPILER_FAMILY}" in | ||
| gcc) FC_SER=gfortran; CC_SER=gcc; FC_MPI=mpif90 ;; | ||
| oneapi) FC_SER=ifx; CC_SER=icx; FC_MPI=mpifort ;; | ||
| nvhpc) FC_SER=nvfortran; CC_SER=nvc; FC_MPI=mpifort ;; | ||
| *) FC_SER=mpifort; CC_SER=mpicc; FC_MPI=mpifort ;; | ||
| esac | ||
| PATH="${NETCDF:+${NETCDF}/bin:}${PNETCDF:+${PNETCDF}/bin:}${PATH}" | ||
| # MPI implementation: MPICH or Open MPI | ||
| if command -v mpichversion >/dev/null 2>&1; then | ||
| MPI_VERSION="$(v mpichversion)"; MPI_SHOW="-show" | ||
| elif command -v ompi_info >/dev/null 2>&1; then | ||
| MPI_VERSION="$(v ompi_info --version)"; MPI_SHOW="--showme" | ||
| else | ||
| MPI_VERSION="not found"; MPI_SHOW="-show" | ||
| fi | ||
| echo "==== Build environment ====" | ||
| echo "Core: ${CORE} (${COMPILER_FAMILY}, make target ${MAKE_TARGET})" | ||
| echo "Fortran: $(v ${FC_SER} --version)" | ||
| echo "C: $(v ${CC_SER} --version)" | ||
| echo "MPI: ${MPI_VERSION}" | ||
| echo "MPI wrapper: $(v ${FC_MPI} ${MPI_SHOW} | cut -c1-160)" | ||
| echo "NetCDF-C: $(v nc-config --version)" | ||
| echo "NetCDF-Fortran: $(v nf-config --version)" | ||
| echo "PnetCDF: $(v pnetcdf-config --version)" | ||
| if [ "${USE_PIO}" = "true" ]; then | ||
| echo "I/O layer: PIO (${PIO})" | ||
| else | ||
| echo "I/O layer: SMIOL (in-tree)" | ||
| fi | ||
| echo "Precision: ${PRECISION}" | ||
| echo "Container OS: $( (. /etc/os-release && echo "${PRETTY_NAME}") 2>/dev/null || echo unknown)" | ||
| if [ "${COMPILER_FAMILY}" = "nvhpc" ]; then | ||
| if [ -n "${ARCH}" ]; then | ||
| echo "Makefile edit: added ${ARCH} to FFLAGS_OPT, CFLAGS_OPT, CXXFLAGS_OPT, LDFLAGS_OPT" | ||
| else | ||
| echo "Makefile edit: none (use-target-arch is false)" | ||
| fi | ||
| fi | ||
| if [ -n "${CLEAN_CMD}" ]; then | ||
| echo "Clean command: ${CLEAN_CMD}" | ||
| fi | ||
| echo "Build command: ${MAKE_CMD}" | ||
| echo "==== End build environment ====" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| name: 'Resolve Container Image' | ||
| description: 'Assemble a container image name from ci-config.env templates' | ||
|
|
||
| inputs: | ||
| compiler: | ||
| description: 'Compiler family (gcc, nvhpc, oneapi)' | ||
| required: true | ||
| mpi: | ||
| description: 'MPI implementation (openmpi, mpich)' | ||
| required: true | ||
| gpu: | ||
| description: 'GPU variant (empty string, or "cuda")' | ||
| required: false | ||
| default: '' | ||
|
|
||
| outputs: | ||
| image: | ||
| description: 'Fully qualified container image name' | ||
| value: ${{ steps.resolve.outputs.image }} | ||
|
|
||
| runs: | ||
| using: 'composite' | ||
| steps: | ||
| - name: Resolve container image | ||
| id: resolve | ||
| shell: bash | ||
| run: | | ||
| CI_CONFIG="${GITHUB_WORKSPACE}/.github/ci-config.env" | ||
| if [ ! -f "${CI_CONFIG}" ]; then | ||
| echo "::error::ci-config.env not found at ${CI_CONFIG}" | ||
| exit 1 | ||
| fi | ||
| source "${CI_CONFIG}" | ||
|
|
||
| COMPILER="${{ inputs.compiler }}" | ||
| MPI="${{ inputs.mpi }}" | ||
| GPU="${{ inputs.gpu }}" | ||
|
|
||
| # Resolve compiler name (fallback to raw value if no mapping) | ||
| comp_var="CONTAINER_COMPILER_${COMPILER}" | ||
| COMPILER_NAME="${!comp_var:-$COMPILER}" | ||
|
|
||
| # Resolve MPI name (fallback to raw value if no mapping) | ||
| mpi_var="CONTAINER_MPI_${MPI}" | ||
| MPI_NAME="${!mpi_var:-$MPI}" | ||
|
|
||
| # Pick template: GPU vs CPU, then check for per-compiler override | ||
| if [ -n "${GPU}" ]; then | ||
| gpu_override="CONTAINER_IMAGE_GPU_${COMPILER}" | ||
| TEMPLATE="${!gpu_override:-$CONTAINER_IMAGE_GPU}" | ||
| else | ||
| cpu_override="CONTAINER_IMAGE_${COMPILER}" | ||
| TEMPLATE="${!cpu_override:-$CONTAINER_IMAGE}" | ||
| fi | ||
|
|
||
| # Substitute placeholders | ||
| IMAGE="${TEMPLATE//\{compiler\}/$COMPILER_NAME}" | ||
| IMAGE="${IMAGE//\{mpi\}/$MPI_NAME}" | ||
|
|
||
| echo "Resolved container image: ${IMAGE}" | ||
| echo " Compiler: ${COMPILER} → ${COMPILER_NAME}" | ||
| echo " MPI: ${MPI} → ${MPI_NAME}" | ||
| echo " GPU: ${GPU:-none}" | ||
|
|
||
| echo "image=${IMAGE}" >> $GITHUB_OUTPUT |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| # .github/ci-config.env — Central CI configuration | ||
| # | ||
| # Sourced by workflows and composite actions. Edit here to change | ||
| # containers, compilers, or build workarounds. | ||
|
|
||
|
|
||
| # ── Container images ────────────────────────────── | ||
| # {compiler} and {mpi} are replaced by resolve-container. | ||
| # Tags: https://hub.docker.com/r/ncarcisl/hpcdev-x86_64/tags | ||
|
|
||
| CONTAINER_IMAGE="docker.io/ncarcisl/hpcdev-x86_64:almalinux9-{compiler}-{mpi}-26.02" | ||
|
|
||
| # Per-compiler override: CONTAINER_IMAGE_{compiler}="template" | ||
|
|
||
|
|
||
| # ── Container name mappings ─────────────────────── | ||
| # Map CI compiler/MPI names to Docker Hub tag strings when they differ. | ||
|
|
||
| CONTAINER_COMPILER_gcc=gcc14 | ||
| # CONTAINER_MPI_mpich=mpich3 | ||
|
|
||
|
|
||
| # ── Makefile target mapping ─────────────────────── | ||
| # MAKE_TARGET_{compiler} → MPAS Makefile target name. | ||
|
|
||
| MAKE_TARGET_gcc=gfortran | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's change |
||
| MAKE_TARGET_nvhpc=nvhpc | ||
| MAKE_TARGET_oneapi=intel | ||
| MAKE_TARGET_clang=llvm | ||
|
|
||
|
|
||
| # ── Compiler workarounds ───────────────────────── | ||
|
|
||
| # Target arch flag added to the NVHPC build flags. -tp=px is portable | ||
| # (CI workflows can sometimes build and run on different hosts). A build | ||
| # turns it off with use-target-arch: 'false' on the build-mpas action. | ||
| NVHPC_TARGET_ARCH=-tp=px | ||
|
|
||
| # Extra make arguments for every build with a compiler: | ||
| # <FAMILY>_EXTRA_MAKE_FLAGS (space-separated), with the family in upper case | ||
| # (GCC, NVHPC, ONEAPI, CLANG). | ||
| # | ||
| # Disable MPI Fortran 2008 bindings (not supported by MPICH | ||
| # builds in current containers). | ||
| NVHPC_EXTRA_MAKE_FLAGS="MPAS_MPI_F08=0" | ||
| ONEAPI_EXTRA_MAKE_FLAGS="MPAS_MPI_F08=0" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be worth checking whether these are actually required. |
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Claude suggested this issue. I haven't double checked it myself.
make-flags gets split on spaces. ${MAKE_CMD} and ${CLEAN_CMD} run unquoted. A value like FFLAGS_OPT="-O2 -g" reaches make as two broken words, and any * or ? gets expanded as a filename pattern. Building the command as a bash array would fix it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, agreed. I've removed the make-flags input from this PR for now, because we aren't really using it yet. The only extra flags left are the compiler defaults in ci-config.env. I’ll add the make-flags back with proper quoting when a build needs it. Removed in bc87306.