Scientific Computing

CMake default generator introspection

CMake can tell the default generator for the current platform without configuring a project. This is useful for build scripts that want to detect the generator default for CMake without having to configure a project first.

On Unix-like platforms, the default generator is typically “Unix Makefiles”. On Windows, the default generator is the latest Visual Studio version installed on the system and supported by CMake. With MSYS2 on Windows, the default generator is “MinGW Makefiles”.

Determine the default CMake generator on Unix-like platforms:

cmake --system-information | awk '/^CMAKE_GENERATOR / {print substr($0, index($0,$2))}'

Determine the default CMake generator with PowerShell:

cmake --system-information | Select-String '^CMAKE_GENERATOR ' | % { ($_ -split '^CMAKE_GENERATOR\s+', 2)[1] }

If the environment doesn’t have a C or C++ compiler available, CMake might fail to determine the default generator. A missing compiler would likely only be an issue on Windows or minimal Linux / macOS / Unix environments.

On Unix-like platforms, there may be a working directory named “__cmake_systeminformation” created by CMake when running the above command. This directory can be deleted. To avoid using the current working directory on Unix-like platforms, run the above command in a temporary directory:

( cd "$(mktemp -d)" && cmake --system-information | awk '/^CMAKE_GENERATOR / {print substr($0, index($0,$2))}' )

A platform-independent, self-cleaning temporary directory can be used with Python:

import subprocess
import tempfile

with tempfile.TemporaryDirectory(ignore_cleanup_errors=True, delete=True) as temp_dir:
    result = subprocess.check_output(
        ['cmake', '--system-information'],
        cwd=temp_dir,
        text=True
    )
for line in result.splitlines():
    if line.startswith('CMAKE_GENERATOR '):
        default_generator = line.split(' ', 1)[1].strip().strip('"')
        print(default_generator)
        break

Related: detect CMake cached generator

FITSIO CMake build

From Cygwin, in Octave prompt:

pkg install -verbose cfitsio

This should build and install FITS from C source without errors.


Load the fits package by:

pkg load fits

This enables Octave FITS read/write functions.

Building CFITSIO from source with CMake

Building CFITSIO manually is not necessary; this is just for reference:

curl -O https://heasarc.gsfc.nasa.gov/FTP/software/fitsio/c/cfitsio_latest.tar.gz

tar -xf cfitsio*.tar.gz

cmake -Bbuild

cmake --build build

ctest --test-dir build

Interposer to block exec / fork / spawn

While developing a software project, it may be useful to verify the stability of the program or library by denying the ability to execute child processes. This helps ensure the top program is stable in such cases by using an interposer. This technique is demonstrated for Linux, macOS, and Windows on compilers including GCC, Clang, MSVC, NVHPC, and oneAPI. This is not a cybersecurity sandbox, but rather a limited development tool to test stability of the top program when child process launches fails.

Use this interposer demo by:

git clone https://github.com/scivision/interposer-nochild

cd interposer-nochild

cmake --workflow --preset default

Run the interposer with most other programs by:

  • Windows: build/no-children.exe myprogram.exe arg1 args ...
  • macOS: DYLD_INSERT_LIBRARIES=build/libnochild.dylib myprogram arg1 args ...
  • Linux: LD_PRELOAD=build/libnochild.so myprogram arg1 args ...

Actual sandboxing tools can additionally deny access to resources including filesystem, network, and/or child processes such as:

CMake with stdin or pseudo-file

In Unix-like shells, a pseudo-file can be used as input of script for CMake. CMake can read from a pseudo-file or stdin on the command line, allowing one to run CMake script cmake -P ... commands without creating temporary files. This method by design is only for CMake script role; it does not work with other CMake roles like PROJECT.

Pseudo-file example for Unix-like shells and CMake ≥ 4.2:

cmake -P <(printf '%s\n' 'message("${CMAKE_VERSION}")')

stdin standard input example for Unix-like shells across all versions of CMake:

cmake -P /dev/stdin <<< "message('\${CMAKE_VERSION}')"

That’s useful to print the CMake version without parsing the output of cmake --version or creating a temporary file with the CMake script.

On Windows in PowerShell or Windows ComSpec, there isn’t a direct equivalent method to use stdin / CONIN$ or a pseudo-file. Use a temporary file instead, or use WSL to run the above Unix-like shell examples. There was a previous attempt to add cmake -P - support for stdin but as of this writing it was closed without merging.

Wilson 1000 magnet mount antenna repair

The Wilson 1000 is a popular high-performance magnet mount antenna for CB radio and 10 meter amateur radio. The screw-on loading coil design may lead to the magnetic base becoming loose from the antenna mount, which can cause high SWR spikes that could damage the transmitter. Eventually, the antenna could detach from the magnetic base and fall off the vehicle, which is a safety hazard. The symptom of this is when the magnetic base is on a metallic surface, gentle twisting of the plastic shroud allows rotation of the shroud over the magnetic base - this is a problem, the shroud should not be able to rotate over the magnetic base when the antenna is properly tightened. This procedure works for similar antennas with a screw-on coil including the Wilson 5000, Stryker SR-A10MM, Radio Shack 21-940A, and others.

The tools required to fix this are a 9/16" socket, a 9/16" wrench, and a 11/16" wrench. An adjustable wrench may be used, but a 9/16" socket is strongly recommended along with whatever wrench is used. Gently peel off the vinyl sheet covering the magnetic base and set it in a clean space as it will be reused. Gently lift off the plastic shroud and slide it down the cable several inches to expose the nut and coax connection to the antenna base. Use the 9/16" socket under the magnetic base and the 9/16" wrench on the nut above the magnetic base to tighten the nut to the magnetic base. Don’t use excessive force as the nut can be stripped or the magnetic base can be damaged. After tightening the nut, slide the plastic shroud back up and tighten the shroud with the 11/16" wrench to secure it to the antenna base. Finally, reapply the vinyl sheet to the magnetic base to protect it from corrosion and to maintain the appearance of the antenna. Be sure not to crinkle the vinyl sheet as this can affect antenna SWR performance and the attachment strength of the magnetic base to the vehicle.

Major changes in GCC Gfortran by version

GCC GFortran, LLVM Flang, and Intel oneAPI are among the most advanced free-to-usemodern Fortran compilers. Currently we recommend writing Fortran code to support:

  • GCC ≥ 12
  • Flang (recent release)
  • oneAPI (currently supported release)

Useful GFortran standard Fortran 2018 enhancements include: select rank assumed array rank, error stop within pure procedures, random_init to initialize random number seed, and implicit none (type, external) to require external procedures to be explicitly declared. GCC 13 is the oldest version currently maintained.

To get recent GCC is usually straightforward. Red Hat should use GCC Toolset. macOS Homebrew quickly adds the latest GCC version. If Ubuntu gfortran repo defaults aren’t adequate, get recent Gfortran via PPA.

Here are some of the major changes in Gfortran by version:

  • Gfortran 16 adds improved Fortran 2023 support including the split intrinsic subroutine, optional lower argument to c_f_pointer, and additional trigonometric functions (sinpi, etc.). It also enhances coarray support with native shared memory multithreading on single-node machines and better Fortran 2018 TEAM handling, improves Fortran 2003 parameterized derived types (LEN parameters), and adds Fortran 2018 extensions to the IMPORT statement, the REDUCE intrinsic, and the new GENERIC statement.
  • Gfortran 15 adds experimental support for unsigned modular integers (-funsigned), Fortran 2018/2023 locality specifiers in do concurrent, and stricter format string parsing (missing commas in I/O descriptors are now rejected by default). The module file format is now incompatible with GCC 8–14 (but older .mod files can still be read). Coarray support has been significantly reworked.
  • Gfortran 14 adds -std=f2023 (prepares for Fortran 2023) with increased free-form line length (10,000 characters) and statement length (up to 1 million characters). It also improves preprocessing output with -save-temps (.fii/.fi files).
  • Gfortran 13 completes full support for finalization and improves OpenMP 5.0 support for Fortran (e.g. some non-rectangular loop nests).
  • Gfortran 12 enhances OpenMP 5 and OpenACC 2.6 support. Numerous bugfixes. bind(C) with character length greater than one. This is a groundbreaking improvement for C interoperability for character and strings, even C++ <string> using ISO_Fortran_binding.h.
  • Gfortran 11 completed OpenMP 4.5 support.
  • Gfortran 10 added select rank.
  • Gfortran 9 added random_init() to initialize the random generator seed.
  • Gfortran 8 added automatic nested loop exchange with do concurrent, actual argument array with too few elements for dummy argument now errors, initial support for parameterized derived types (simply define kind at initialization) and coarray support for teams. Standard flag -std=f2018 added and deprecated -std=f2008ts.
  • Gfortran 7 added derived type IO select type. Complete Fortran 2003 support, Fortran 2018 non-constant stop and error stop codes, and -fdec- options to help compile very old non-standard code.

Gfortran 6 added Fortran 2008 submodule support, useful for large projects to save compilation time and allow powerful use scenarios. Fortran 2003 deferred-length character are useful for avoiding bothersome trim() everywhere.

GCC 5 added full support for OpenMP 4.0, Fortran 2003 ieee_ intrinsics, Fortran 2008 error stop in pure procedures with constant error code. GCC 4.9 added Fortran 2003 deferred-length character variables in derived types. GCC 4.8 supported Fortran 2008 polymorphism, including select type, class(*), type(*), and Fortran 2018 assumed rank dimension(..). GCC 4.6 was the first version of Gfortran reaching beyond Fortran 95, with Fortran 2003 deferred-length character variable and Fortran 2008 impure elemental support. GCC 4.5 added Fortran 2008 iso_fortran_env. GCC 4.4 added initial support for polymorphism and OpenMP 3.

CMake allows switching parameters based on compiler version. This is very useful for modern Fortran programs.

Example CMakeLists.txt for Fortran compiler version dependent options.

if(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
  add_compile_options($<$<COMPILE_LANGUAGE:Fortran>:-fimplicit-none>)
  if(CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL "10.0")
    add_compile_options($<$<COMPILE_LANGUAGE:Fortran>:-fallow-argument-mismatch>)
  endif()
endif()

or using generator expressions exclusively:

add_compile_options(
$<$<COMPILE_LANG_AND_ID:Fortran,GNU>:-fimplicit-none>
$<$<AND:$<COMPILE_LANG_AND_ID:Fortran,GNU>,$<VERSION_GREATER_EQUAL:$<CMAKE_Fortran_COMPILER_VERSION>,10.0>>:-fallow-argument-mismatch>
)

Reference: Gfortran changelog

Strip Jupyter notebook outputs from Git

Jupyter notebook outputs can be large (plots, images, etc.), making Git repo history excessively large and making Git operations slower as the Git history grows. Jupyter notebook outputs can reveal personal information with regard to usernames, Python executable, directory layout, and data outputs.

Strip all Jupyter outputs from Git tracking with a client-side Git pre-commit hook by configuring Git pre-commit hooks. We use Git pre-commit hook because Git filters can interfere with other programs such as CMake ExternalProject.

Configure Git user-wide where to use an IPython script to strip Jupyter notebook outputs by:

git config --global hook.lintipython.event pre-commit
git config --global hook.lintipython.command '$HOME/linters/strip-ipython.py'

Use an IPython linter script like ~/linters/strip-ipython.py. On Unix-like systems, the script must have execute permissiong like:

chmod +x ~/linters/strip-ipython.py

fpm build CFLAGS

In some environments, Fortran Package Manager commands like fpm build or fpm test can fail when a dependency (say HDF5) is resolved through pkg-config and the .pc file includes a system include path such as -I/usr/include.

In that case, allow pkg-config to keep system CFLAGS via pkg-config environment variable for FPM to work:

PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1 fpm build
PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1
tells pkg-config not to strip system include paths from pkg-config --cflags output.

The symptom of possibly needing this flag is:

> fpm build
<ERROR> *cmd_build* Model error: Cannot get pkg-config build flags: environment variable error.
STOP 1

CI examples across code languages

Continuous Integration services run user-defined self-checks on code on each “git push”. GitHub Actions is a popular service for CI. CI is one part of the DevOps lifecycle.

CI services generally have quotas and/or concurrency limits but these are currently no-cost for public Git repos:

We have amassed numerous CI examples across programming languages, including