Scientific Computing

C feature macros that stdlib.h may need

When reading the documentation for a C library function, you may see a note that the function requires a feature test macro to be defined. This is common for functions that are not part of the C standard library but are part of POSIX or another standard. The feature test macro tells the compiler to include the necessary headers and definitions for the function.

Often the “_DEFAULT_SOURCE” macro is sufficient. Try doing in C like:

#if defined(__linux__) && !defined(_DEFAULT_SOURCE)
#define _DEFAULT_SOURCE
#endif

Switch CMake Generator to Ninja

The default CMake build generator is operating system dependent. In general many projects can benefit from increased build speed and especially rebuild speed of Ninja. Switch the default build generator on any platform by setting environment variable CMAKE_GENERATOR.

CMAKE_GENERATOR=Ninja

Ninja executable can be installed by any one of:

  • download
  • WinGet: winget install Ninja-build.Ninja
  • Python: pip install ninja
  • Homebrew: brew install ninja
  • Alpine Linux: apk add ninja-build

Add the location of the Ninja executable to environment variable $PATH or CMAKE_PROGRAM_PATH.


Note that Ninja work-alikes like Samurai “samu” might get installed by default by some package managers. These work-alikes might not have a new-enough Ninja API equivalent, so in those cases install the actual Ninja program.


If using Conan Package Manager, tell Conan to use Ninja by setting environment variable

CONAN_CMAKE_GENERATOR=Ninja

Check C++ random is working with CMake

The C++ STL <random> header may not be implemented correctly by some C++ compiler / CPU arch platforms. If a function using <random> is not essential to the program, avoid build errors by checking that <random> is working at configure time. This example checks that <random> is working with CMake.

include(CheckSourceCompiles)

check_source_compiles(CXX
"#include <random>
int main(){
  std::mt19937 mt_rand(0);
  return 0;
}"
HAVE_MERSENNE_TWISTER
)

add_executable(main main.cpp)
target_compile_definitions(main PRIVATE $<$<BOOL:${HAVE_MERSENNE_TWISTER}>:HAVE_MERSENNE_TWISTER>)

The “main.cpp” can have #ifdef HAVE_MERSENNE_TWISTER to conditionally compile code that uses <random>. This allows the rest of the program that doesn’t use this function to work without <random> or to fallback to a simpler RNG if suitable.

#ifdef HAVE_MERSENNE_TWISTER
#include <random>
#endif

#include <cstdlib>
#include <iostream>

void init_rand(){
#ifdef HAVE_MERSENNE_TWISTER
  std::mt19937 mt_rand(0);
#else
  std::cerr << "Warning: mt19937 not available\n";
#endif
}

UTM serial device in case no display on boot

For VM server images in particular, the main display of the VM may never activate. The UTM message is like:

display output is not active

This is solved by powering off the VM, and adding a Serial Device under the UTM VM settings. Rebooting will automatically popup a separate windows for the Serial terminal, where boot messages and login can be accomplished. Often the main display window will then start working.

Cross-compiler Raspberry Pi Pico

Microcontrollers generally use a different CPU architecture than the developer’s laptop. This implies a cross compiler is needed to build code for the microcontroller on the laptop. It’s straightforward to install the Raspberry Pi Pico ARM C / C++ cross-compiler on macOS, Windows, and Linux.

Please follow the section relevant to the laptop operating system. About 2..5 gigabytes of hard drive space is required by the Raspberry Pi Pico SDK and associated tools.

Git, CMake and a cross compiler are used by almost all full projects as well. This script will install the cross-compiler and other tools needed for the Raspberry Pi Pico SDK, or follow the manual process below.

#!/usr/bin/env bash

set -e

# determine OS and arch
case "$OSTYPE" in
linux*)
sudo apt update
sudo apt install git cmake g++ gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib;;
darwin*)
brew install git cmake
brew install --cask gcc-arm-embedded;;
*)
echo "$OSTYPE not supported"
exit 1
esac

macOS

Homebrew install Git, CMake build system, and ARM cross-compiler by:

brew install cmake git

brew install --cask gcc-arm-embedded

The cross-compiler executables have a prefix “arm-none-eabi-” that are linked into $(brew --prefix)/bin for easy use from the command line.

It’s likely the macOS laptop has an Apple Silicon CPU. If so, it’s necessary to enable Rosetta. Rosetta enables most x86 apps on Apple Silicon at nearly full performance.

Note: if on macOS you get a build error like:

arm-none-eabi-gcc: fatal error: cannot read spec file ’nosys.specs’: No such file or directory

and you’ve installed the GCC cross-compiler via:

brew install --cask gcc-arm-embedded

Then something is wrong with the cross-compiler setup.

Linux

Install Git, CMake build system, and ARM cross-compiler on:

  • laptop/desktop with Ubuntu / Debian-like distros
  • full Raspberry Pi (e.g. Raspberry Pi 4)
apt update

apt install git make cmake g++ gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib

Windows MSYS2

pacman -S mingw-w64-x86_64-arm-none-eabi-gcc

This takes about 2GB of storage.

Windows WSL

Windows Subsystem for Linux (WSL) is useful for many projects including the Raspberry Pi Pico SDK. WSL can be the easiest way to work with non-Windows projects on Windows.

The Ubuntu WSL process takes about 10 minutes depending on download speed. WSL can be installed via the Microsoft Store Ubuntu app. If the Microsoft Store isn’t available on the computer, it is also possible to install Ubuntu WSL manually.

WSL can access the native Windows filesystem. WSL sees the native Windows filesystem “C:” in WSL via “/mnt/c”. Use the native Windows drive under “/mnt/c/pico” from WSL. Then use Windows File Explorer in path “C:/pico” to drag and drop .uf2 file to Pico.

The cross-compiler install on WSL Ubuntu is just like plain Linux in the section above.

To make switching between Windows and WSL easier, optionally use Windows Terminal.

Alternative: Visual Studio

The Visual Studio cross-compiler setup is described in Section 9.2 of the Pico install guide.

Fix Snap too early for operation

When setting up an older Linux operating system on a physical computer or virtual machine, the Snap package manager might not work correctly at first. Snap is handy to get the latest versions of software on older operating systems. The following message might appear on first trying to use Snap to install a package:

too early for operation, device not yet seeded or device model not acknowledged

A fix is to purge the Snap package manager and reinstall it.

apt purge snapd

apt install snapd

CMake C++ standard with fallback

CMake describes language standards supported by the specific C++ compiler being used by the Cmake project in CMAKE_CXX_COMPILE_FEATURES. For example, a project may with to fallback to a lower C++ standard version if the requested standard is not available. This is useful for building projects that need to support older compilers.

Use feature macros and / or CMake source code checks to define symbols within the code to fallback as necessary.

project(my LANGUAGES CXX)

set(cxx_std 17)

if("cxx_std_20" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
  set(cxx_std 20)
elseif(NOT "cxx_std_17" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
  message(WARNING "${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION} may not support at least C++17 standard")
endif()

add_executable(my_exe my.cpp)
target_compile_features(my_exe PRIVATE cxx_std_${cxx_std})

Normally the project wishes to use CMAKE_CXX_COMPILE_FEATURES instead of CMAKE_CXX_KNOWN_FEATURES, as the latter tells all the features CMake knows, rather that what the compiler knows.


The C / C++ code can use macro feature checks that detect compiler C++ / C standard support.

#if __cplusplus >= 201703L
// C++17 features
#endif

For C code likewise:

#if __STDC_VERSION__ >= 201112L
// C11 features
#endif

Related: Meson C++ standard with fallback

Grub force text console boot

When using a virtual machine, and especially if the virtual machine is in emulation mode, which is generally many times slower than virtualization mode, it can greatly help interaction speed to use a console boot rather than graphical desktop. In operating systems like Ubuntu, this can be done by editing /etc/default/grub and setting:

GRUB_CMDLINE_LINUX_DEFAULT="text"
GRUB_TERMINAL=console

Then from Terminal:

update-grub

systemctl set-default multi-user.target

Then the system will boot into a text console on all future boots.


Reference

Select CMake cURL version to build with

CMake uses the bundled cURL library by default. When debugging connectivity issues with CMake, a developer may wish to build CMake with a specific cURL version. To do so, from the cmake/ project source directory, use options like:

cmake -Bbuild -DCURL_ROOT=/path/to/curl -DCMAKE_USE_SYSTEM_LIBRARY_CURL=yes

Then build CMake as usual.

Verify the Curl version used with this script.

macOS software update from Terminal

macOS can update system software from Terminal, even macOS itself. However, to complete the upgrade requires using the graphical desktop, perhaps over VNC.

softwareupdate -l

shows the available updates.

softwareupdate -i -a

installs all available updates.