XIOS on an Apple Silicon Mac

From Planets
Revision as of 12:27, 16 October 2026 by Mwolff (talk | contribs) (→‎Compiling XIOS on macOS (Apple Silicon) with Open MPI and Clang)

Jump to: navigation, search

Compiling XIOS on macOS (Apple Silicon) with Open MPI and Clang

This page provides the three XIOS arch files needed to build XIOS on macOS with Homebrew gfortran and Homebrew Open MPI. In this setup the Open MPI C++ wrapper calls Apple clang, not GCC. Because of that, two settings differ from the stock arch-gfortran files. They are explained in the background section below.

Tested environment

  • macOS 26 (Darwin 25.6.0), Apple Silicon (arm64)
  • GNU Fortran 16.2.0 (Homebrew), installed as gfortran
  • Apple clang 21, which provides /usr/bin/cpp and is called by mpiCC
  • Open MPI 5.0.11 (Homebrew)
  • netCDF-C 4.10.1 and netCDF-Fortran 4.6.4, compiled from source against Open MPI(which is where the CLANG dependence originates) and installed in /opt/parallel-netcdf

Arch files

Create these three files in the arch/ directory of your XIOS source tree.

arch/arch-CLANG_MACOSX.env

Change NETCDF_DIR to match your netCDF installation.

# Environment to compile XIOS on Mac OS X with NETCDF compiled with CLANG (through openmpi)
#
# Tested successfully with :
#   Mac OS X 25.6.0
#   Prerequisite softwares installed with Homebrew
#     gfortran       : 16.2.0
#     openmpi        : 5.0.11
#
#   Compiled from source with open-mpi
#     netcdf         : netCDF 4.10.1 netCDF-Fortran 4.6.4
#
#
export NETCDF_DIR=/opt/parallel-netcdf
export NETCDF_INC_DIR=${NETCDF_DIR}/include
export NETCDF_LIB_DIR=${NETCDF_DIR}/lib

arch/arch-CLANG_MACOSX.fcm

################################################################################
###################                Projet XIOS               ###################
################################################################################
#Build environment (2026-10-13):
#
#- macOS 26 (Darwin 25.6.0), arm64
#- GNU Fortran 16.2.0 (Homebrew), installed as `gfortran`
#- Apple clang 21, which provides `/usr/bin/cpp` and is wrapped by `mpiCC`
#- Open MPI 5.0.11 (Homebrew)
## Summary

%CCOMPILER      mpiCC
%FCOMPILER      mpif90
%LINKER         mpif90

%BASE_CFLAGS    -w -std=gnu++11
%PROD_CFLAGS    -O3 -D BOOST_DISABLE_ASSERTS
%DEV_CFLAGS     -g
%DEBUG_CFLAGS   -g -O2

%BASE_FFLAGS    -D__NONE__ -ffree-line-length-none
%PROD_FFLAGS    -O3
%DEV_FFLAGS     -g -O2
%DEBUG_FFLAGS   -g

%BASE_INC       -D__NONE__
%BASE_LD        -lc++

%CPP             cpp -P -undef
%FPP             cpp-16 -P
%MAKE           make

arch/arch-CLANG_MACOSX.path

This file gets the netCDF flags from nc-config and nf-config, so both must be on your PATH.

NETCDF_INCDIR="$(nc-config --cflags) $(nf-config --fflags)"
NETCDF_LIBDIR="$(nc-config --libs) $(nf-config --flibs)"
NETCDF_LIB=""

Building

From the top of the XIOS source tree:

./make_xios --arch CLANG_MACOSX --job 8 --full > make_xios.out 2>&1

Use --full to rebuild from scratch.

A successful build produces:

  • lib/libxios.a

Background: Open MPI uses Clang, not GCC

On Linux, mpif90, mpicc and mpicxx usually all call the GNU compilers. With Homebrew on macOS, Open MPI is built with Apple's toolchain. Only the Fortran wrapper calls GCC:

Component Wrapper Underlying compiler C++ standard library
XIOS (C++ core) mpiCC Apple clang libc++ (LLVM), linked with -lc++
XIOS Fortran interface, and the model (e.g. LMDZ / PCM) mpif90 Homebrew gfortran none (GCC Fortran runtime)

This mix causes two problems: one with the preprocessor and one at link time.

Why mpiCC is really clang

macOS filesystems are case-insensitive by default, so the files mpiCC and mpicc cannot both exist. Homebrew's Open MPI makes /opt/homebrew/bin/mpiCC a symlink to the C wrapper mpicc, which calls Apple clang:

$ mpiCC --showme
clang -I/opt/homebrew/Cellar/open-mpi/5.0.11/include -L/opt/homebrew/Cellar/open-mpi/5.0.11/lib -lmpi

clang still compiles the XIOS .cpp files as C++, because it picks the language from the file extension. But it compiles them against libc++, LLVM's C++ standard library, not GCC's libstdc++.

A side effect: OMPI_CXX has no effect on mpiCC, because mpiCC is really mpicc, and mpicc reads OMPI_CC.

1. Link library: -lc++ instead of -lstdc++

The stock arch-gfortran.fcm sets %BASE_LD -lstdc++. With clang-compiled C++ objects, the link of xios_server.exe fails with errors like:

Undefined symbols for architecture arm64:
  "std::__1::basic_string<...>::copy(...) const", referenced from: ...
  "std::__1::regex_error::regex_error(...)", referenced from: ...
ld: symbol(s) not found for architecture arm64

The std::__1:: prefix is libc++'s inline namespace. libstdc++ uses different names, such as std::__cxx11::. The two libraries are not interchangeable at link time: an object compiled against libc++ can only be linked with libc++.

The final link is done by mpif90 (gfortran), and gfortran does not add any C++ library on its own. The arch file must name the right one, so %BASE_LD is set to -lc++.

This also applies to every program that links against this libxios.a. For example, the LMDZ / PCM arch .path file must use:

XIOS_LIB="-lxios -lc++"

and not -lstdc++. Otherwise the model build compiles all its sources and then fails at the final link with hundreds of undefined std::__1:: symbols coming from libxios.a.

To check which C++ library an existing libxios.a needs:

nm -C lib/libxios.a | grep -m1 'std::__1::'      # match -> libc++    (-lc++)
nm -C lib/libxios.a | grep -m1 'std::__cxx11::'  # match -> libstdc++ (-lstdc++)

The rule: the C++ library named at link time must be the same one that compiled XIOS. If you rebuild XIOS with GCC's g++ instead (for example %CCOMPILER mpicxx with export OMPI_CXX=g++-16), switch back to -lstdc++ in both the XIOS and the model arch files.

2. Fortran preprocessor: cpp-16 instead of cpp

The XIOS Fortran interface builds its names with a token-pasting macro, defined in src/interface/fortran/xios_fortran_prefix.hpp:

#define xios(arg)  xios_##arg
#define txios(arg) xios_##arg

On macOS, /usr/bin/cpp is Apple clang. It does not expand ## in these files, so txios(duration) comes out as xios_##duration instead of xios_duration. Compilation then fails, for example in duration_interface.f90:

IMPORT :: xios_##duration
Error: Syntax error in IMPORT statement at (1)
TYPE(xios_##duration), VALUE :: dur
Error: Malformed type-spec at (1)

The fix is to use the GNU C preprocessor that comes with Homebrew GCC for Fortran files: %FPP cpp-16 -P. The -16 suffix follows the major version of Homebrew GCC. If GCC is upgraded, update this line to match. Check the installed version with:

ls /opt/homebrew/bin/cpp-*

gfortran -E -P -cpp may also work as the Fortran preprocessor, but it has not been tested.

Harmless warning

The XIOS build and the model builds may print:

ld: warning: ignoring duplicate libraries: '-lnetcdf'

It appears because nc-config --libs and nf-config --flibs both list -lnetcdf. You can ignore it.