CMake detect CPU model

CMake can detect the host CPU arch to use with Intel compilers. We discourage package maintainers from settings flags like “-march=native” (GCC, Clang) and “-xHost” (Intel oneAPI) because they may break on user systems such as ARM or HPC. However, the user can set flags for a project by setting environment variables like CFLAGS before the first project configure in CMake. This allows optimizing for a target compiler while compiling from a different host. Or, the user on an appropriate system may simply set their ~/.profile to have CFLAGS=-march=native or similar.

Identifying Fortran compiler with CMake

Projects use a variety of methods to detect which compiler is being used to thereby set compilation options. Although this discussion focuses on Fortran, it is easily and equally applicable to other languages such as C and C++.

Robustly detect compiler in CMake CMAKE_Fortran_COMPILER_ID. This tells the compiler vendor (GNU, Intel, Clang, etc.) Don’t use CMAKE_Fortran_COMPILER because there are several compiler executables per vendor and this will not be robust over time. To get the compiler version, CMAKE_Fortran_COMPILER_VERSION allows version comparisons.


  # Gfortran
  add_compile_options(-Wall -Werror=array-bounds -Wextra -Wpedantic)

Visual-Studio only _set_printf_count_output

[_set_printf_count_output}( is a Visual-Studio only function. Therefore, C++ programs that call it should use a preprocessor conditional:

#ifdef _MSC_VER

Cygwin startup environment variables

Cygwin startup configuration is controlled by Windows environment variable CYGWIN. Not populating Cygwin PATH from Windows PATH avoids confusion from Windows programs being used instead of Cygwin programs. Set Windows environment variable:


will result in a basic Cygwin PATH like:


Look inside Cygwin “/etc/profile” for more environment variables that may be of interest.

NOTE: all Windows environment variables are imported by Cygwin. Override these variables in ~/.profile or scripts if desired.

Related: don’t populate WSL PATH from Windows PATH.

Fortran logical boolean byte size

Fortran compilers typically use 4 bytes for logical while C compilers usually use 1 byte for bool. For C interoperability, Fortran can use:

use, intrinsic :: iso_c_binding

logical(kind=C_BOOL) :: L
logical :: Q

c_sizeof(L) == 1
c_sizeof(Q) == 4
! typically

while C uses:

#include <stdbool.h>
#include <stdio.h>

int main(void) {
bool L;
printf("%d\n", sizeof(L));

and likewise C++ bool is typically 1 byte:

#include <iostream>

int main(){
  bool L;
  std::cout << sizeof(L) << std::endl;

Always use iso_c_binding when using C or C++ with Fortran modules to produce cross-platform compatible projects.

See “bool” examples for interfacing between C, C++ and Fortran.

Use libutil on MacOS and Linux

Libutil gives abstractions for OS-specific TTY operations. When using these abstractions across MacOS and Linux, use this preprocessing statement for the appropriate header:

#ifdef __APPLE__
#include <util.h>
#include <pty.h>

If using CMake, ensure the library and header are found:

find_library(UTIL_LIBRARY NAMES util)

  find_path(UTIL_INCLUDE_DIR NAMES util.h)
  find_path(UTIL_INCLUDE_DIR NAMES pty.h)

Cross-compile for DOS from Windows or Linux

OpenWatcom is an open-source C/C++ compiler that can compile for a variety of systems, particularly legacy 16/32-bit Windows and DOS systems. This can be of interest for retro gamers and those using old devices that have DOS-based software, including industrial controllers and two-way radio programming.

CMake supports OpenWatcom, and is conveniently used with a toolchain file. GitHub Actions has easy scripting for OpenWatcom to test building of DOS programs from Linux.

The easiest way to show this is by example: see the Ascii Patrol game for how to build with OpenWatcom for DOS from Windows/Linux and GitHub Actions build CI.

Force older language standard in CMake

CMake target_compile_features sets a transitive MINIMUM language standard necessary. If the compiler defaults to a newer language standard, target_compile_features allows that default. This can make issues for legacy code that requires an older language standard. For example, an old C++98 code may need to have the compiler in C++98 mode. This is accomplished with the target property CXX_STANDARD. Other languages may have a similar property.

Example: C++98 needed for old code: