Scientific Computing

CMake Find MPIEXEC without FindMPI

CMake superprojects may have one project that builds binary executables with MPI and another project that runs those targets. To avoid the overhead of FindMPI, one can use the following find_program() command to use “mpiexec”.

find_program(MPIEXEC_EXECUTABLE
NAMES mpiexec
HINTS ${MPI_ROOT} ENV MPI_ROOT ENV I_MPI_ROOT
PATHS /usr/lib64 ENV MINGWROOT ENV MSMPI_BIN
PATH_SUFFIXES bin openmpi/bin mpich/bin
DOC "Runs an MPI program"
REQUIRED
)

CTest update command

To ensure expected behavior in a CTest script for ctest_update() when Git is used, set these variables in the CTest script:

set(CTEST_UPDATE_TYPE git)
set(CTEST_UPDATE_COMMAND git)

CMake TARGET_FILE generator expression

The CMake generator expression TARGET_FILE yields the full path to a binary target file. This is useful with add_custom_command() and add_test() when a script is used as part of those commands. For CMake native commands, it’s usually not necessary to use TARGET_FILE.

Example:

add_executable(my_exe main.c)

find_package(Python COMPONENTS Interpreter REQUIRED)

add_test(NAME scripted
COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example.py $<TARGET_FILE:my_exe>
)

When using scripts it’s a good practice to also specify the full path as above.

Prohibited Git branch names

Git doesn’t have any general letter-specific restrictions on branch names. Organizations can implement push restrictions with services like GitHub to restrict branch names–for example bad words or regex patterns.

The Windows operating system has general filename restrictions that restrict several keywords from Git branch names. A list of case-insensitive prohibited branch names includes Windows devices names.

For example on Windows:

> git switch -c con

fatal: cannot lock ref 'refs/heads/con': Unable to create '.git/refs/heads/con.lock': Invalid argument

Note that one can work with those branch names using Windows Subsystem for Linux (WSL), but even if you create such a branch, it will not work from native Windows:

warning: ignoring broken ref refs/heads/con

Stop GNU Octave autoload all packages

Speed up Octave startup by a factor of 10-100x by not autoloading packages. For each of the Octave config files that exist, comment out the line:

# pkg ("load", "auto");

Verify GNU Octave speedup fix: in Octave type

pkg list

if all packages are starred, that means you’re autoloading, which greatly slows down Octave startup. The factor of 100 improvement in GNU Octave speedup was estimated with the command

time octave --eval 'exit'

Before fix 4.904 seconds, after fix 0.072 seconds to load Octave.

CMake INACTIVITY_TIMEOUT not foolproof

CMake implements a file(DOWNLOAD INACTIVITY_TIMEOUT) option that uses curl CURLOPT_LOW_SPEED_TIME to fail much earlier than a TIMEOUT option would when the connection speed is extremely slow. However, some systems intentionally block downloads, but do so in a way that fools the underlying curl library into not tripping INACTIVITY_TIMEOUT. Rather than try to figure out a direct fix in curl for this apparently longstanding problem, we instead decide to implement a CMake configure-time internet connectivity detection.

When a CTest test is comprised of a CMake script, this test can be setup as a FIXTURES_SETUP to avoid calling it repeatedly for each test, instead just once for a set of tests.

The key technique here is the URL connectivitycheck.gstatic.com/generate_204 that is used by Android devices to check connectivity. Any similar URL with zero or tiny download size would also be suitable.

CMake GCC build

This CMake project builds GCC and high level prerequisites such as GMP. This avoids needing to manually download and configure each project. This assumes fundamental prerequisites such as libc, autotools, make, CMake are present. The 3-stage compiler bootstrap is enabled to help ensure a working, performant GCC.

Obscuring Matlab code for sharing

“Security through obscurity” alone does not actually confer security. Obscuring code simply increases effort for someone who wishes to use code for unauthorized purposes. For Matlab “.m” code, two methods to partially obscure the underlying “.m” code for locally-run Matlab-based algorithms are:

  • compile Matlab code to executable (OS-specific and Matlab version-specific)
  • convert Matlab code to pcode (OS-agnostic and Matlab version-agnostic)

In general, when critical IP needs to be made available for user data while keeping IP non-public, this is done by providing web services. For example, the IP of Google, Bing, Office 365 is usable via web services, but generally the core code remains non-public.

Matlab pcode() can obscure directories or lists of files to “.p” code that runs on Matlab on any supported OS. Newer versions of Matlab use a more powerful obscuration algorithm that is not backward compatible.

conda install tar.bz2 low bandwidth Internet

For sites with poor Internet connection, conda install mypkg will fail and not resume a partially downloaded package. Workaround: use URL from the error message with curl to download the package and install directly.

curl --location --remote-name --continue-at https://conda.anaconda.org/conda-forge/win-64/<package file>

Resume download interrupted by reissuing the command to resume download. Install from the tar.bz2 file by

conda install <package stem>.tar.bz2

Using Python in CMake script

CMake FindPython prioritizes location over version number. Prior to CMake 3.15, even specifying Python_ROOT could be overridden if the other Python was a higher version.

Using the Python interpreter in CMake should generally be via ${Python_EXECUTABLE} instead of Python::Interpreter. CMake provides the imported target Python::Interpreter only when the CMAKE_ROLE is PROJECT. This means that Python::Interpreter is not available when using CTest, which is often when using the Python interpreter is desired. Normally, to use Python interpreter from a CMake script, including in execute_process or add_test, use Python_EXECUTABLE.

Example:

find_package(Python COMPONENTS Interpreter REQUIRED)

add_test(NAME Foo COMMAND ${Python_EXECUTABLE} myscript.py -arg1 value)