Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 103 additions & 18 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-13, windows-latest]
os: [ubuntu-latest, macos-14, windows-latest]
toolchain:
- {compiler: gcc, version: 10}
- {compiler: gcc, version: 11}
Expand All @@ -31,8 +31,10 @@ jobs:
- {compiler: gcc, version: 15}
- {compiler: intel, version: 2025.1}
exclude:
- os: macos-13 # No Intel on MacOS anymore since 2024
- os: macos-14 # No Intel on MacOS anymore since 2024
toolchain: {compiler: intel, version: '2025.1'}
- os: macos-14 # gcc@10 not available on macos-14 (ARM64)
toolchain: {compiler: gcc, version: 10}
- os: windows-latest # Doesn't pass build and tests yet
toolchain: {compiler: intel, version: '2025.1'}
- os: windows-latest # gcc 14 not available on Windows yet
Expand All @@ -45,14 +47,14 @@ jobs:
- os: ubuntu-latest
os-arch: linux-x86_64
release-flags: --flag '--static -g -fbacktrace -O3'
- os: macos-13
- os: macos-14
os-arch: macos-x86_64
release-flags: --flag '-g -fbacktrace -O3'
- os: windows-latest
os-arch: windows-x86_64
release-flags: --flag '--static -g -fbacktrace -O3'
exe: .exe
- os: macos-13
- os: macos-14
toolchain: {compiler: gcc, version: 15}
release-flags: --flag '-g -fbacktrace -Og -fcheck=all,no-recursion -Wno-external-argument-mismatch'

Expand All @@ -69,25 +71,89 @@ jobs:

# Phase 1: Bootstrap fpm with existing version
- name: Install fpm
uses: fortran-lang/setup-fpm@v7
uses: fortran-lang/setup-fpm@v8
with:
fpm-version: 'v0.8.0'
fpm-version: 'v0.12.0'

# Backport gfortran shared libraries to version 10 folder. This is necessary because the macOS release of fpm
# 0.10.0 used for bootstrapping has these paths hardcoded in the executable.
# Backport gfortran shared libraries to the paths expected by the bootstrap fpm binary.
# The macOS x86_64 release used for bootstrapping has hardcoded library paths from the gcc version
# it was built with. If the versions match, skip this step. Otherwise, create symlinks.
# On ARM64 it runs under Rosetta 2 and needs /usr/local paths.
- name: MacOS patch libgfortran
if: contains(matrix.os, 'macos') && !contains(matrix.toolchain.version, '10')
if: contains(matrix.os, 'macos')
run: |
which gfortran-${{ matrix.toolchain.version }}
which gfortran
mkdir /usr/local/opt/gcc@10
mkdir /usr/local/opt/gcc@10/lib
mkdir /usr/local/opt/gcc@10/lib/gcc
mkdir /usr/local/opt/gcc@10/lib/gcc/10
mkdir /usr/local/lib/gcc/10
ln -fs /usr/local/opt/gcc@${{ matrix.toolchain.version }}/lib/gcc/${{ matrix.toolchain.version }}/libquadmath.0.dylib /usr/local/opt/gcc@10/lib/gcc/10/libquadmath.0.dylib
ln -fs /usr/local/opt/gcc@${{ matrix.toolchain.version }}/lib/gcc/${{ matrix.toolchain.version }}/libgfortran.5.dylib /usr/local/opt/gcc@10/lib/gcc/10/libgfortran.5.dylib
ln -fs /usr/local/lib/gcc/${{ matrix.toolchain.version }}/libgcc_s.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib
BREW_PREFIX=$(brew --prefix)

# Query the actual library paths that fpm expects
echo "Checking library dependencies of fpm bootstrap binary..."
otool -L $(which fpm)

# Extract the gcc version from the bootstrap binary's library paths
BOOTSTRAP_GCC_VERSION=$(otool -L $(which fpm) | grep -o 'gcc@[0-9]\+' | head -n 1 | cut -d@ -f2)
if [ -z "$BOOTSTRAP_GCC_VERSION" ]; then
# Try alternative pattern: /lib/gcc/13/ -> extract 13
BOOTSTRAP_GCC_VERSION=$(otool -L $(which fpm) | grep libgfortran | grep -o '/lib/gcc/[0-9]\+/' | grep -o '[0-9]\+' | head -n 1)
fi

echo "Bootstrap fpm built with gcc@$BOOTSTRAP_GCC_VERSION"
echo "Current toolchain: gcc@${{ matrix.toolchain.version }}"

if [ "$BOOTSTRAP_GCC_VERSION" == "${{ matrix.toolchain.version }}" ]; then
echo "✓ Bootstrap gcc version matches current toolchain - no patching needed"
exit 0
fi

echo "⚠ Version mismatch detected - creating compatibility symlinks..."

# Find the actual Cellar path by following the symlink
if [ -L "${BREW_PREFIX}/opt/gcc@${{ matrix.toolchain.version }}" ]; then
GCC_CELLAR_PATH=$(cd -P "${BREW_PREFIX}/opt/gcc@${{ matrix.toolchain.version }}" && pwd)
else
GCC_CELLAR_PATH="${BREW_PREFIX}/Cellar/gcc@${{ matrix.toolchain.version }}"/*
fi
echo "GCC Cellar path: $GCC_CELLAR_PATH"

# Find the actual library files
CURRENT_GFORTRAN=$(find "$GCC_CELLAR_PATH"/lib/gcc/${{ matrix.toolchain.version }} -name "libgfortran.*.dylib" 2>/dev/null | head -n 1)
CURRENT_QUADMATH=$(find "$GCC_CELLAR_PATH"/lib/gcc/${{ matrix.toolchain.version }} -name "libquadmath.*.dylib" 2>/dev/null | head -n 1)
CURRENT_GCC_S=$(find "$GCC_CELLAR_PATH"/lib/gcc/${{ matrix.toolchain.version }} -name "libgcc_s.*.dylib" 2>/dev/null | head -n 1)

echo "Current gcc@${{ matrix.toolchain.version }} libraries:"
echo " libgfortran: $CURRENT_GFORTRAN"
echo " libquadmath: $CURRENT_QUADMATH"
echo " libgcc_s: $CURRENT_GCC_S"

# Extract the expected libgfortran path and create symlink
LIBGFORTRAN_PATH=$(otool -L $(which fpm) | grep libgfortran | awk '{print $1}' | head -n 1)
if [ -n "$LIBGFORTRAN_PATH" ] && [ -n "$CURRENT_GFORTRAN" ]; then
TARGET_DIR=$(dirname "$LIBGFORTRAN_PATH")
echo "Creating directory: $TARGET_DIR"
sudo mkdir -p "$TARGET_DIR"
echo "Linking $CURRENT_GFORTRAN to $LIBGFORTRAN_PATH"
sudo ln -fs "$CURRENT_GFORTRAN" "$LIBGFORTRAN_PATH"
fi

# Extract the expected libquadmath path and create symlink
LIBQUADMATH_PATH=$(otool -L $(which fpm) | grep libquadmath | awk '{print $1}' | head -n 1)
if [ -n "$LIBQUADMATH_PATH" ] && [ -n "$CURRENT_QUADMATH" ]; then
TARGET_DIR=$(dirname "$LIBQUADMATH_PATH")
echo "Creating directory: $TARGET_DIR"
sudo mkdir -p "$TARGET_DIR"
echo "Linking $CURRENT_QUADMATH to $LIBQUADMATH_PATH"
sudo ln -fs "$CURRENT_QUADMATH" "$LIBQUADMATH_PATH"
fi

# Extract the expected libgcc_s path and create symlink
LIBGCC_PATH=$(otool -L $(which fpm) | grep libgcc_s | awk '{print $1}' | head -n 1)
if [ -n "$LIBGCC_PATH" ] && [ -n "$CURRENT_GCC_S" ]; then
TARGET_DIR=$(dirname "$LIBGCC_PATH")
echo "Creating directory: $TARGET_DIR"
sudo mkdir -p "$TARGET_DIR"
echo "Linking $CURRENT_GCC_S to $LIBGCC_PATH"
sudo ln -fs "$CURRENT_GCC_S" "$LIBGCC_PATH"
fi

# gcc and g++ will point to clang/clang++: use versioned alias for fpm
- name: MacOS patch C and C++ compilers
Expand Down Expand Up @@ -210,6 +276,7 @@ jobs:
- uses: actions/checkout@v5

- name: Download Artifacts
id: download_windows_artifacts
uses: actions/download-artifact@v5
with:
path: ${{ github.workspace }}
Expand Down Expand Up @@ -243,7 +310,25 @@ jobs:
- name: Fetch Windows executable
shell: msys2 {0}
run: |
cp fpm-*/fpm-*-windows-*-gcc-${{ matrix.gcc_v }}.exe ./ci/fpm.exe
set -e
download_path='${{ steps.download_windows_artifacts.outputs.download-path }}'
download_path=$(printf '%s\n' "$download_path" | head -n1)
if [ -z "$download_path" ]; then
echo "download-artifact did not report a download path" >&2
exit 1
fi
if command -v cygpath >/dev/null 2>&1; then
posix_path=$(cygpath -u "$download_path")
else
posix_path="$download_path"
fi
exe_path=$(find "$posix_path" -maxdepth 2 -type f -name "fpm-*-windows-*-gcc-${{ matrix.gcc_v }}.exe" | head -n 1)
if [ -z "$exe_path" ]; then
echo "Windows executable not found under $posix_path" >&2
ls -al "$posix_path" || true
exit 1
fi
cp "$exe_path" ./ci/fpm.exe

- name: Fetch Git for Windows
shell: msys2 {0}
Expand Down
106 changes: 86 additions & 20 deletions .github/workflows/meta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ jobs:
- os: ubuntu-latest
mpi: mpich
release-flags: --flag ' -Wno-external-argument-mismatch'
- os: macos-13
- os: macos-14
mpi: openmpi
release-flags: --flag ' -Wno-external-argument-mismatch'
- os: macos-13
- os: macos-14
mpi: mpich
release-flags: --flag ' -Wno-external-argument-mismatch'
- os: ubuntu-latest
Expand Down Expand Up @@ -208,7 +208,8 @@ jobs:
# Only install gcc if not already available
which gfortran-${{ env.GCC_V }} || brew install gcc@${{ env.GCC_V }}
which gfortran-${{ env.GCC_V }}
which gfortran || ln -s /usr/local/bin/gfortran-${{ env.GCC_V }} /usr/local/bin/gfortran
BREW_PREFIX=$(brew --prefix)
which gfortran || ln -s ${BREW_PREFIX}/bin/gfortran-${{ env.GCC_V }} ${BREW_PREFIX}/bin/gfortran

- name: (macOS) Install homebrew MPICH
if: contains(matrix.mpi,'mpich') && contains(matrix.os,'macos')
Expand All @@ -231,26 +232,91 @@ jobs:
brew install netcdf
brew install netcdf-fortran

- name: (macOS) Patch gfortran paths
if: contains(matrix.os,'macos')
run: |
# Backport gfortran shared libraries to version 10 folder. This is necessary because all macOS releases of fpm
# have these paths hardcoded in the executable (no PIC?). Current bootstrap version 0.8.0 has gcc-10
mkdir /usr/local/opt/gcc@10
mkdir /usr/local/opt/gcc@10/lib
mkdir /usr/local/opt/gcc@10/lib/gcc
mkdir /usr/local/opt/gcc@10/lib/gcc/10
mkdir /usr/local/lib/gcc/10
ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libquadmath.0.dylib /usr/local/opt/gcc@10/lib/gcc/10/libquadmath.0.dylib
ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libgfortran.5.dylib /usr/local/opt/gcc@10/lib/gcc/10/libgfortran.5.dylib
# Newer gcc versions use libgcc_s.1.1.dylib
ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib || ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib

# Phase 1: Bootstrap fpm with existing version
- name: Install fpm
uses: fortran-lang/setup-fpm@v7
uses: fortran-lang/setup-fpm@v8
with:
fpm-version: 'v0.8.0'
fpm-version: 'v0.12.0'

# Backport gfortran shared libraries to the paths expected by the bootstrap fpm binary.
# The macOS x86_64 release used for bootstrapping has hardcoded library paths from the gcc version
# it was built with. If the versions match, skip this step. Otherwise, create symlinks.
# On ARM64 it runs under Rosetta 2 and needs /usr/local paths.
- name: MacOS patch libgfortran
if: contains(matrix.os, 'macos')
run: |
which gfortran-${{ env.GCC_V }}
which gfortran || true
BREW_PREFIX=$(brew --prefix)

# Query the actual library paths that fpm expects
echo "Checking library dependencies of fpm bootstrap binary..."
otool -L $(which fpm)

# Extract the gcc version from the bootstrap binary's library paths
BOOTSTRAP_GCC_VERSION=$(otool -L $(which fpm) | grep -o 'gcc@[0-9]\+' | head -n 1 | cut -d@ -f2)
if [ -z "$BOOTSTRAP_GCC_VERSION" ]; then
# Try alternative pattern: /lib/gcc/13/ -> extract 13
BOOTSTRAP_GCC_VERSION=$(otool -L $(which fpm) | grep libgfortran | grep -o '/lib/gcc/[0-9]\+/' | grep -o '[0-9]\+' | head -n 1)
fi

echo "Bootstrap fpm built with gcc@$BOOTSTRAP_GCC_VERSION"
echo "Current toolchain: gcc@${{ env.GCC_V }}"

if [ "$BOOTSTRAP_GCC_VERSION" == "${{ env.GCC_V }}" ]; then
echo "✓ Bootstrap gcc version matches current toolchain - no patching needed"
exit 0
fi

echo "⚠ Version mismatch detected - creating compatibility symlinks..."

# Find the actual Cellar path by following the symlink
if [ -L "${BREW_PREFIX}/opt/gcc@${{ env.GCC_V }}" ]; then
GCC_CELLAR_PATH=$(cd -P "${BREW_PREFIX}/opt/gcc@${{ env.GCC_V }}" && pwd)
else
GCC_CELLAR_PATH="${BREW_PREFIX}/Cellar/gcc@${{ env.GCC_V }}"/*
fi
echo "GCC Cellar path: $GCC_CELLAR_PATH"

# Find the actual library files
CURRENT_GFORTRAN=$(find "$GCC_CELLAR_PATH"/lib/gcc/${{ env.GCC_V }} -name "libgfortran.*.dylib" 2>/dev/null | head -n 1)
CURRENT_QUADMATH=$(find "$GCC_CELLAR_PATH"/lib/gcc/${{ env.GCC_V }} -name "libquadmath.*.dylib" 2>/dev/null | head -n 1)
CURRENT_GCC_S=$(find "$GCC_CELLAR_PATH"/lib/gcc/${{ env.GCC_V }} -name "libgcc_s.*.dylib" 2>/dev/null | head -n 1)

echo "Current gcc@${{ env.GCC_V }} libraries:"
echo " libgfortran: $CURRENT_GFORTRAN"
echo " libquadmath: $CURRENT_QUADMATH"
echo " libgcc_s: $CURRENT_GCC_S"

# Extract the expected libgfortran path and create symlink
LIBGFORTRAN_PATH=$(otool -L $(which fpm) | grep libgfortran | awk '{print $1}' | head -n 1)
if [ -n "$LIBGFORTRAN_PATH" ] && [ -n "$CURRENT_GFORTRAN" ]; then
TARGET_DIR=$(dirname "$LIBGFORTRAN_PATH")
echo "Creating directory: $TARGET_DIR"
sudo mkdir -p "$TARGET_DIR"
echo "Linking $CURRENT_GFORTRAN to $LIBGFORTRAN_PATH"
sudo ln -fs "$CURRENT_GFORTRAN" "$LIBGFORTRAN_PATH"
fi

# Extract the expected libquadmath path and create symlink
LIBQUADMATH_PATH=$(otool -L $(which fpm) | grep libquadmath | awk '{print $1}' | head -n 1)
if [ -n "$LIBQUADMATH_PATH" ] && [ -n "$CURRENT_QUADMATH" ]; then
TARGET_DIR=$(dirname "$LIBQUADMATH_PATH")
echo "Creating directory: $TARGET_DIR"
sudo mkdir -p "$TARGET_DIR"
echo "Linking $CURRENT_QUADMATH to $LIBQUADMATH_PATH"
sudo ln -fs "$CURRENT_QUADMATH" "$LIBQUADMATH_PATH"
fi

# Extract the expected libgcc_s path and create symlink
LIBGCC_PATH=$(otool -L $(which fpm) | grep libgcc_s | awk '{print $1}' | head -n 1)
if [ -n "$LIBGCC_PATH" ] && [ -n "$CURRENT_GCC_S" ]; then
TARGET_DIR=$(dirname "$LIBGCC_PATH")
echo "Creating directory: $TARGET_DIR"
sudo mkdir -p "$TARGET_DIR"
echo "Linking $CURRENT_GCC_S to $LIBGCC_PATH"
sudo ln -fs "$CURRENT_GCC_S" "$LIBGCC_PATH"
fi

- name: Remove fpm from path
shell: bash
Expand Down
Loading