Scientific Computing

Matlab buildfile MEX

Matlab buildtool has become a capable build system for tasks including MEX targets and tests with incremental progress, which avoids the need rerun already completed tasks. A standalone buildtool example illustrates the basic use of Matlab buildtool.

Run the build and test tasks:

buildtool mex

buildtool test

See several additional examples of more advanced Mex and Matlab Engine tasks. Matlab-stdlib is another substantial example of buildfile.m usage.

Quote CMake JSON arguments

The CMake string(JSON) subcommands should have the “json-string” input variable quoted to avoid CMake interpreting any semicolon in the JSON string as a list separator. This avoids CMake string(JSON ...) failures when the JSON string contains semicolons.

string sub-command JSON failed parsing json string:
  Syntax error: value, object or array expected.

Example

Suppose a JSON string contains one or more values with semicolons in any value. In that case, the JSON string should be quoted to avoid the CMake string(JSON ...) failure.

set(json_string "{ \"key1\": 432, \"key2\": \"I like to write; my blog is about tech. \" }")

# Fails with a syntax error
string(JSON a GET ${json_string} key1)

# works as expeccted
string(JSON a GET "${json_string}" key1)

Related: CMake JSON array iteration

GitHub API with CMake

CMake can use the GitHub REST API to fetch the latest release download URL from GitHub in the form of JSON data. The API is useful to download the latest release of a GitHub project using CMake. The API used in this example specifically ignore pre-releases and draft releases. The example CMake code shows how to parse the JSON data to get the download URL.

CMake file(DOWNLOAD …) has HTTPHEADER option that can be repeated to add multiple headers to the HTTP request. In GitHubRelease.cmake linked above, a specific version of the GitHub API and format is used to get the latest release download URL. Authentication parameters can also be passed if API limits are exceeded. Don’t save/commit API credentials to a public repository!

file(DOWNLOAD ...
HTTPHEADER "Accept: application/vnd.github+json"
HTTPHEADER "X-GitHub-Api-Version: 2022-11-28"
)

MATLAB GCC version compatibility fix

Matlab requires compatible compilers to run compiled code and even to run itself. Trying to use Matlab on a non-supported just-released OS version can sometimes encounter difficulty. libc, libstdc++, or libgfortran incompatible with Matlab and cause failure to run MEX code. The workaround below assumes Linux.

Example error messages:

MATLAB/R*/sys/os/glnxa64/libstdc++.so.6: version `GLIBCXX_3.4.29’ not found (myfun.mexa64)

Workaround by having the system libraries take priority by using environment variable LD_PRELOAD.

Find the system libstdc++:

find /usr -name libstdc++.so.6

Suppose “/usr/lib64/libstdc++.so.6”.

LD_PRELOAD=/usr/lib64/libstdc++.so.6 matlab

C / C++ include inline code file

In certain cases, such as defining multiple classes or templates in a single header or source file in C or C++, it may be useful to include inline source code files to reduce code duplication. This can be achieved by using the #include directive with a file containing the inline code. This technique is distinct from the use of header files, which are typically used to declare functions, classes, and other entities that are defined in a separate source file. This technique is also distinct from the use of the inline specifier on functions.

A traditional file suffix for include code files is .inc or .inl, but any suffix can be used. Build systems detect changes to these included inline code files and rebuild the source file if necessary. For example, CMake detects include dependencies (header, inline code) based on its own source parser, or some modern compilers manage dependencies themselves.

Makefiles with CMake uses the compiler itself or depend.make in each target build directory to track dependencies.

Ninja (with CMake or other build system such as Meson) specifies include dependencies via depfiles per source file, which may be observed for debugging with option ninja -d keepdepfile

Example

Here we assume a CMake project with source file main.cpp and inline source file myconst.inl.

main.cpp:

#include <iostream>
#include "myconst.inl"

int main() {
    std::cout << "The value of MY_CONST is: " << MY_CONST << "\n";
    return 0;
}

myconst.inl:

constexpr int MY_CONST 42
cmake_minimum_required(VERSION 3.20)

project(InlineDemo LANGUAGES C CXX)

add_executable(demo main.cpp)

Observe that CMake will rebuild main.cpp if myconst.inl changes.

cmake -Bbuild
cmake --build build

touch myconst.inl

cmake --build build

Fortran include statement

The Fortran include statement inserts source code from the specified file into the Fortran source code file at the location of the include statement. The include file can contain any valid Fortran syntax, including procedures, modules, variable definitions, operations, etc. This concept is similar to the C/C++ #include preprocessor directive that can also be used for inlining code, but Fortran include does not require a preprocessor. include statements are frequently used to reuse code like defining constants or Fortran 77 common blocks. Generated code from build systems like CMake and Meson can be consumed with include statements. The file suffix “.inc” is often used, but is arbitrary.

One example of a Fortran-only project extensively using CMake-generated Fortran source with include is h5fortran to allow polymorphic (type and rank) HDF5 I/O in Fortran. The source code deduplication thus achieved is significant and the code is easier to maintain.

Build systems scan Fortran source files for dependencies to detect the include statements and track the included files. Makefiles with CMake uses the compiler itself or depend.make in each target build directory to track dependencies. Ninja (with CMake or other build system such as Meson) specifies include dependencies via depfiles per source file, which may be observed for debugging with option ninja -d keepdepfile

In the example below, the dependency of main.f90 on const.inc is tracked by:

  • Ninja: depfile “build/CMakeFiles/main.dir/main.f90-pp.f90.d”
  • Make: “build/CMakeFiles/main.dir/depend.make”

A minimal example of using the Fortran include statement is shown below.

main.f90:

program main

include 'const.inc'

print '(A, I0)', 'The value of my_const is: ', my_const

end program

const.inc:

integer, parameter :: my_const = 42

CMakeLists.txt:

cmake_minimum_required(VERSION 3.20)

project(include_example LANGUAGES Fortran)

add_executable(main main.f90)

Observe that CMake will rebuild main.f90 if const.inc changes.

cmake -Bbuild
cmake --build build

touch const.inc

cmake --build build

Python libstdc++ conflict LD_PRELOAD

If importing Python modules or trying to run an Anaconda Python program like Spyder gives CXXABI errors, it can be due to a conflict between the system libstdc++ and the Anaconda libstdc++. Assuming Anaconda / Miniconda Python on Linux, try specifying the libstdc++ library in the conda environment by LD_PRELOAD.

Find the system libstdc++:

find /usr -name libstdc++.so.6

Suppose “/usr/lib64/libstdc++.so.6”. Set LD_PRELOAD environment variable in the conda environment:

conda env config vars set LD_PRELOAD=/usr/lib64/libstdc++.so.6

conda activate

Then retry the command / program.

Matlab dark mode

Matlab Online and locally-installed Matlab Desktop can use “dark mode” for the Matlab Desktop.

s = settings;

s.matlab.appearance.MATLABTheme.PersonalValue = "Dark";

Or from the GUI by searching for “Dark” in the Matlab settings.

Figures can be switched between light and dark mode with theme().


Related: Alternative Matlab code editors

References:

Matlab lint .m script recursive

Matlab codeIssues() command recursively lints Matlab .m code files. The output is a neat table.

The Matlab build system has a built-in CodeIssuesTask for use via buildtool to validate an entire Matlab project from a single command.

Used from CI, this is a quick first step check of a project to help ensure compatibility of code syntax across Matlab versions. Of course, the Matlab version checked is only the currently-running Matlab, so the CI system would need to fan out running across the desired Matlab versions.

Matlab Git operations

Matlab Git operations are a first-class part of the Matlab environment, without need for system() calls. The Matlab Desktop GUI or Matlab factory functions allow most common Git operations to be directly performed.

For example, Git clone is a plain Matlab function that can be called from the command line or script.

gitclone("https://github.invalid/username/repo.git")

The Matlab branching and merging GUI can be helpful for those not familiar with Git commands.

Some Git Matlab operations are object-oriented, for example Git pull

repo = gitrepo("https://github.invalid/username/repo.git");

pull(repo)