Using CMAKE_ROLE to detect CMake run mode

The operating mode of CMake can be detected via CMAKE_ROLE. This variable must be read with:

get_property(cmake_role GLOBAL PROPERTY CMAKE_ROLE)

Here, the variables cmake_role is created and can then be used in if() statements as needed.

Detect WSL from Python in Windows

It’s trivial to detect if one is running inside WSL. It’s also straightforward to detect if WSL is available and working on a Windows PC. These functions are using only Python standard library modules.

Detect if inside WSL

This function detects if Python is running in WSL.

import uname


def in_wsl() -> bool:
    """
    WSL is thought to be the only common Linux kernel with Microsoft in the name.
    """

    return 'Microsoft' in uname().release

Detect WSL from Windows

import os
import shutil


def wsl_available() -> bool:
    """
    heuristic to detect if Windows Subsystem for Linux is available.

    Uses presence of /etc/os-release in the WSL image to say Linux is there.
    This is a de facto file standard across Linux distros.
    """
    if os.name == "nt":
        wsl = shutil.which("wsl")
        if not wsl:
            return False
        # can't read this file or test with
        # pathlib.Path('//wsl$/Ubuntu/etc/os-release').
        # A Python limitation?
        ret = subprocess.run(["wsl", "test", "-f", "/etc/os-release"])
        return ret.returncode == 0

    return False

Ignore Meson subprojects with .gitignore

Meson projects using Meson subprojects have a project directory structure like:

meson.build
main.c
subprojects/
  lapack/
  lapack.wrap

where “subprojects/lapack/” is automatically created as a Meson subproject from the metadata in “subprojects/lapack.wrap”. Using the negate .gitignore syntax Git will ignore subdirectories but track the subprojects/*.wrap files:

include in the project .gitignore:

subprojects/

!subprojects/*.wrap

This technique can be applied to similar non-Meson scenarios.

CMake Ninja Multi-Config generator

We generally recommend using Ninja ≥ 1.10 with CMake ≥ 3.15, especially for large projects (including Fortran) to speed up rebuild times significantly and avoid erratic problems with slower GNU Make on large projects. CMake 3.17 added

cmake -G "Ninja Multi-Config" -B build

which allows building Debug and Release builds or even cross builds without regenerating build*.ninja files for each build type.

Ninja Multi-Config generator options may be used transparently with older CMake and different generators. The default CMake generator can be set with environment variable:

CMAKE_GENERATOR="Ninja Multi-Config"

Here are the options we generally add to CMakeLists.txt:

set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Build type selections" FORCE)
set(CMAKE_NMC_DEFAULT_BUILD_FILE_CONFIG "Release" CACHE STRING "Default Build type" FORCE)

with those settings, one can quickly build Debug and Release and test like:

cd build

# Release default per above settings
cmake --build .

ctest -C Release

Then update and test Debug by:

cmake --build . --config Debug

ctest -C Debug

CMake Ninja generator always preprocesses Fortran

CMake with the Ninja and Ninja Multi-Config generators virtually always work well together, including with Fortran projects. To generate the dependency graph, CMake directs the Fortran compiler to preprocess each Fortran code file when using the Ninja generator. In rare cases, some legacy Fortran large code files can generate unexpected and spurious syntax errors when using CMake + Ninja.

The workaround seems to be to use the GNU Make backend, we have found that works for the rare case this became an issue.

Windows set WiFi preference order

Windows can control WiFi from the Command Prompt, including WiFi AP preference order. Via the Windows WiFi settings GUI page, the latest added WiFi pushes to the top of the preference stack. This is typically not desired, as when setting up a device in general, one usually connects to the most needed WiFi AP first, and over time adds temporary or less desired WiFi, which Windows keeps at the top of the WiFi preference list. This netsh command prompt procedure allows correcting the Windows WiFi preference order.

Show known WiFi AP

This command lists the known (previously connected) WiFi AP, just like shown in the Windows WiFi settings GUI:

netsh wlan show profiles

The WiFi AP are listed in preference order, from most preferred at the top to least preferred at the bottom. A key piece of information needed from this command is:

Profiles on interface “Wi-Fi”

The name might be “Wi-Fi 2” or other if multiple WiFi adapters are in the PC.

Set WiFi preference order

Using the information from:

netsh wlan show profiles

Put the WiFi APs in the desired priority order like:

netsh wlan set profileorder my_work_wifi Wi-Fi 1

netsh wlan set profileorder my_home_wifi Wi-Fi 2

where the trailing integer is the position in the Windows WiFi preference.

Notes

Android 10 restore 3-button navigation

Android 10 defaults to task switching swipe navigation. Some workflows are much easier via the traditional 3-button navigation used by Android since Android 3.x / 4.x. Also when wearing gloves, using 3-button navigation can work better.

Switch an Android 10 device back to traditional 3-button navigation (back, home, switch or triangle, circle square) by going to Settings → System → Gestures → System navigation and select “3-button navigation”.

CMake per-language option flags

CMake can set compiler flags with three broad scope categories. We set global and per-language options near the beginning of the top-level CMakeLists.txt, before any targets are declared, to avoid confusion about scope. COMPILE_DEFINITIONS works in a similar fashion.

Note: where using FetchContent, add_compile_options() can only be overridden for the fetched project by using the “negative” option in the fetched project. While the per-language method can be overridden just by setting it again in the fetched project. For example, the main project may desire -Wall but this may cause megabytes of warnings from a legacy Fetched project. In this case, we suggest using the per-language rather than global method.

Example

This example is for a C and Fortran project, where some flags apply to C and Fortran, and other flags are Fortran-specific.

project(Foo
LANGUAGES C Fortran)

if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU)

  # options applying to any project language for this compiler
  add_compile_options(-march=native)

  # Fortran-specific, note LEADING space
  string(APPEND CMAKE_Fortran_FLAGS " -fimplicit-none")

  # options for Debug build type
  string(APPEND CMAKE_Fortran_FLAGS_DEBUG " -Werror=array-bounds")

  if(CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL 8)
    string(APPEND CMAKE_Fortran_FLAGS " -std=f2018")
  endif()

endif()


add_library(old OBJECT legacy.f old.f)
# these options apply only to target "old"
target_compile_options(old PRIVATE -w -fno-implicit-none)

add_executable(main main.f90)
target_link_libraries(main old)

Writing image stack with HDF5

The HDF5 format can be used for almost anything, including image stacks. To signal graphical interactive HDF5 viewers that a dataset is an image stack, add HDF5 image metadata, as in this Python h5py example:

import numpy as np
import h5py

# images is a numpy ndarray N x X x Y images

with h5py.File("image.h5", "w") as f:
    h = f.create_dataset("/images", data=images)
    h.attrs["CLASS"] = np.string_("IMAGE")
    h.attrs["IMAGE_VERSION"] = np.string_("1.2")
    h.attrs["IMAGE_SUBCLASS"] = np.string_("IMAGE_GRAYSCALE")
    h.attrs["DISPLAY_ORIGIN"] = np.string_("LL")
    h.attrs["IMAGE_WHITE_IS_ZERO"] = np.uint8(0)

HDFview will show an optional video player for this dataset due to the image metadata added.

CMake Fortran design patterns

CMake is excellent for building very complex Fortran projects across operating systems and computing platforms from embedded systems to HPC. Here are a few common Fortran CMake patterns.

Linker options

specify linker options for all targets using add_link_options()

add_link_options(-myflag)

or for a particular target (or static linkers) with

set_target_properties(myexe PROPERTIES STATIC_LIBRARY_OPTIONS -myflag)

Order of libraries

For hand-written Makefile the order of libraries matters completely. CMake attempts to determine the graph for library files, starting from the user specified order. CMake will try a finite multiplicity to resolve the graph, but in tough cases of library interdependency library ordering may need to be influenced by add_dependencies() or even object libraries.

undefined reference to

occurs at linking of main executable: be sure the library is actually linked to the user library with

cmake --build build -v

Fortran module include

be sure the *.mod file directory is included, particularly to the main executable by setting the target properties like:

target_include_directories(mymod INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/include)
set_target_properties(mymod PROPERTIES Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include)

this can be done for numerous targets in a loop instead of repeatedly specifying by a foreach() loop:

foreach(t foo bar biz baz)
  target_include_directories(${t} INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/include)
  set_target_properties(${t} PROPERTIES Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include)
endforeach()

To ensure the build system is completely updated, do

cmake --build build --clean-first