Scientific Computing

Homebrew vs. MacPorts package managers

macOS package managers allow easy download, build and install of developer programs and libraries.

Homebrew is by far the most popular macOS package manager. Homebrew has a large number of packages and the ability to create unofficial “taps” to easily distribute software. Homebrew taps allow distributing binaries via Homebrew before going to include in the main homebrew package repo, which takes time and justification. Homebrew distributes per-OS compiled binaries, so package install time is almost instant. It is possible to also download source and build locally with Homebrew if desired.

MacPorts generally distributes source code that is compiled on install, although it can also use precompiled binaries. Macports installs packages under a prefix.

Homebrew is much more popular than MacPorts or Fink.

Package popularity comparison:

CMake find with Homebrew

Anaconda Python puts itself first on PATH when activated. This can become a problem for libraries like HDF5, where “conda install h5py” puts compiler script h5cc on environment variable PATH before the intended script path. For systems where Homebrew is used to provide packages to find from CMake, tell CMake to prefer a package location with CMAKE_PREFIX_PATH.

This can be done a few different ways:

  • Setting CMAKE_PREFIX_PATH in the environment before running cmake:

    export CMAKE_PREFIX_PATH=$HOMEBREW_PREFIX
  • Setting CMAKE_PREFIX_PATH as a command line argument to cmake:

    cmake -DCMAKE_PREFIX_PATH=$HOMEBREW_PREFIX -B build

One can also have CMake ignore conda paths to ensure CMake doesn’t find Anaconda libraries or compilers.

CMake Git inactivity timeout

CMake Git operations such as shallow clone can cause unexpected failures due to too small INACTIVITY_TIMEOUT in ExternalProject or FetchContent. Be sure to set INACTIVITY_TIMEOUT to a large enough value. 15 seconds is too short a timeout for Git shallow clone, for example. Consider 60 seconds or larger INACTIVITY_TIMEOUT.

Check for timeout in:

git config --get http.lowSpeedLimit
git config --get http.lowSpeedTime

lowSpeedLimit might be set to 1000 (bits/second) or as appropriate for the network. If lowSpeedTime is too short, this download failure can also occur. Set to 60 seconds or more.

CMake generator full path

Normally it is not necessary to specify the path to the CMake generator backend, assuming the generator executable is in environment variable $PATH or CMAKE_PROGRAM_PATH. For special use cases such as testing CMake with different versions of a generator the generator executable absolute path may be specified via CMAKE_MAKE_PROGRAM. The absolute path to the generator is necessary or CMake will not find it.

Suppose a GitHub Actions Linux image has ninja-linux.zip containing executable file “ninja”. Get the absolute path using realpath.

    - run: unzip ninja-linux.zip

    - name: CMake configure
      run: cmake -G Ninja -DCMAKE_MAKE_PROGRAM=$(realpath ./ninja) -Bbuild

CMake FindPython hints

CMake Find modules are by their nature a little aggressive about finding libraries and executables. This becomes a factor on Windows in particular when Anaconda Python is not active in the current Terminal. CMake find_package(Python) by default prefers Anaconda Python over system Python unless overridden as below. Anaconda Python won’t operate correctly without conda activate, which presumably the user has either forgotten to do or doesn’t desire at the moment. To decrease the aggressiveness and find Windows Store Python etc. when conda isn’t activated on Windows, add to the project CMakeLists.txt before find_package(Python):

set(Python_FIND_REGISTRY LAST)
# this avoids non-active conda from getting picked anyway on Windows

set(Python_FIND_VIRTUALENV STANDARD)
# Use environment variable PATH to decide preference for Python

find_package(Python)

Disable conda auto activate base

Anaconda Python by default auto-activates the “base” environment each time a new Terminal is opened. This slows opening new Terminal, particularly on systems with slow disks or virtual disks. Particularly if the user isn’t constantly using Python, it can be beneficial to make conda only active when specified. To disable conda auto-activation on new Terminal, type:

conda config --set auto_activate_base false

This setting takes effect in .condarc

CMake detect CPU model

CMake can detect the host CPU arch to use with Intel compilers. We discourage package maintainers from setting flags automatically like “-march=native” (GCC, Clang) and “-xHost” (Intel oneAPI) because they may break on user systems such as ARM or HPC. However, the user can set flags for a project by setting environment variables like CFLAGS before the first project configure in CMake. This allows optimizing for a target compiler while compiling from a different host. Or, the user on an appropriate system may simply set their ~/.profile to have CFLAGS=-march=native or similar.

Identifying Fortran compiler with CMake

Projects use a variety of methods to detect which compiler is being used to thereby set compiler options. Although this discussion focuses on Fortran, it is easily and equally applicable to other languages such as C and C++.

Robustly detect compiler in CMake CMAKE_Fortran_COMPILER_ID. This tells the compiler vendor (GNU, Intel, Clang, etc.) Don’t use CMAKE_Fortran_COMPILER because there are several compiler executables per vendor and this will not be robust over time. CMAKE_Fortran_COMPILER_VERSION allows compiler version comparisons.

Example:

if(CMAKE_Fortran_COMPILER_ID STREQUAL "IntelLLVM")
  add_compile_options("$<$<COMPILE_LANGUAGE:Fortran>:-warn:declarations;-traceback>")
elseif(CMAKE_Fortran_COMPILER_ID MATCHES "GNU|LLVMFlang")
  # Gfortran
  add_compile_options("$<$<COMPILE_LANGUAGE:Fortran>:-fimplicit-none>")
endif()