Build systems

CMake executable minimal example

This CMakeLists.txt builds an executable called main from main.cpp:

add_executable(main main.cpp)

 

Posted by Uli Köhler in CMake

Towards a docker-based build of C/C++ applications

Note: Based on this post I have published buildock on GitHub.

Many C/C++ programmers and project managers know the pain of creating a reproducible build environment for all developers: Works for me is a common meme not without a reason.

My approach is to dockerize not neccessarily the application itself but the build system, encapsulating both the specific compiler version and the system around it plus all required system-level libraries in a Docker image.

Take the obligatory Hello World in C++:

// main.cpp
#include <iostream>
using namespace std;

int main() {
    cout << "Hello, World!" << endl;
    return 0;
}

and the corresponding Makefile:

all:
        g++ -o helloworld main.cpp

How can we compile this simple project without installing a compiler and GNU make on the local computer (no cheating by using build servers allowed)?

Actually it’s rather simple:

docker run --user $(id -u):$(id -g) -v $PWD:/app -it ulikoehler/ubuntu-gcc-make make

Breaking it down:

  • docker run: Create a new docker container and run a command in it
  • --user $(id -u):$(id -g): This makes the docker container run with the current user’s ID and the current user’s group – both for preventing the compiler to create output files as root and to safeguard against some IT security risks. Also see How to run docker container as current user & group
  • -v $PWD:/app: Mount the current directory ($PWD) on /app in the container. Since the Dockerfile used to build the container contains the WORKDIR /app directive, whatever command we run in the container will by default be executed in the /app directory – and therefore in the current local directory on the host.
  • -it runs the container in interactive mode, i.e. keypressed are passed down to the command you are running. Also, this means that our command will only finish when the container has finished executing.
  • ulikoehler/ubuntu-gcc-make: This is the image we’re using for that example. It’s nothing more than an ubuntu:18.04 base image with build-essentials and make installed and WORKDIR set to /app
  • make: This is the command we’ll run in the container. You can use any command here, even no command is possible (in which case the container’s default command will be used – in case of ulikoehler/ubuntu-gcc-make that is CMD [ "/usr/bin/make" ])

Here’s the complete Dockerfile used to generate ulikoehler/ubuntu-gcc-make:

FROM ubuntu:18.04
RUN apt update && apt -y install build-essential make && rm -rf /var/lib/apt/lists/*
WORKDIR /app
CMD [ "/usr/bin/make" ]
Posted by Uli Köhler in Build systems, C/C++, Container, Docker

What is the CMake equivalent to AC_CHECK_FUNCS()?

With automake/m4 in configure.ac you use syntax like

AC_CHECK_FUNCS(atan2)

which defines HAVE_FDATASYNC if the function is available.

In CMake, you can use check_symbol_exists() for the same purpose like this:

cmake_minimum_required(VERSION 3.0)
include(CheckSymbolExists)

# Define executable
add_executable(myexe main.c)

# atan2 requires the math library to be linked
list(APPEND CMAKE_REQUIRED_LIBRARIES m)
check_symbol_exists(atan2 math.h HAVE_ATAN2)

# Add compile definitions if we have the library
if(HAVE_ATAN2)
    target_compile_definitions(myexe PRIVATE -DHAVE_ATAN2)
endif()

Note that check_symbol_exists() does not automatically add a preprocessor define but you have to do that manually (see the last block in the code shown above). While this might seem less comfortable at first, this approach provides you with much more flexibility on how to handle missing or available functions.

See the CMake check_symbol_exists() documentation for more details.

Posted by Uli Köhler in CMake

CMake check_symbol_exists() minimal example

cmake_minimum_required(VERSION 3.0)
include(CheckSymbolExists)

# Define executable
add_executable(myexe main.c)

# atan2 requires the math library to be linked
list(APPEND CMAKE_REQUIRED_LIBRARIES m)
check_symbol_exists(atan2 math.h HAVE_ATAN2)

# Add compile definitions if we have the library
if(HAVE_ATAN2)
    target_compile_definitions(myexe PRIVATE -DHAVE_ATAN2)
endif()
Posted by Uli Köhler in CMake

How to fix ‘Unknown CMake command “check_symbol_exists”‘

If you have CMake code using check_symbol_exists(...) like

list(APPEND CMAKE_REQUIRED_LIBRARIES m)
check_symbol_exists(atan2 math.h HAVE_ATAN2)

but you get an error message like

CMake Error at CMakeLists.txt:8 (check_symbol_exists):
  Unknown CMake command "check_symbol_exists".

-- Configuring incomplete, errors occurred!

you need to add

include(CheckSymbolExists)

near the top of your CMakeLists.txt (before your first call to check_symbol_exists()).

Posted by Uli Köhler in CMake

Minimal CMake if/else/endif example

if/else/endif looks like this in CMake:

if(myvar)
  message("if")
else()
  message("else")
endif()

 

Posted by Uli Köhler in CMake

How to add boost::program_options to your CMake build

Simply add these lines to the end of your CMakeLists.txt and replace myTarget by the name of your build target (usually the first argument to add_executable(...) or add_library(...)):

# Include boost
find_package( Boost 1.30 COMPONENTS program_options REQUIRED )
target_include_directories( myTarget PRIVATE ${Boost_INCLUDE_DIR})
target_link_libraries( myTarget ${Boost_LIBRARIES} )

If you have multiple targets, copy & paste the last two lines for each target.

If you need a specific version of boost, replace 1.30 by the minimum version you need.

Posted by Uli Köhler in Boost, C/C++, CMake

How to build debug in CMake

If you want to build an executable / library with debug symbols in CMake, run

cmake -DCMAKE_BUILD_TYPE=Debug .
make

Conversely, if you want to build an executable / library in release mode, run

cmake -DCMAKE_BUILD_TYPE=Release .
make

 

Posted by Uli Köhler in C/C++, CMake

How to fix SDCC ‘at 1: warning 119: don’t know what to do with file ‘main.o’. file extension unsupported’

If you use SDCC to compile your C code for your microcontroller and you encounter an error message like this:

at 1: warning 119: don't know what to do with file 'main.o'. file extension unsupported

you have to configure your build system to use the .rel suffix for object files instead of the standard .o. SDCC expects built object files to have the .rel extension! See How to change CMake object file suffix from default “.o” for details on how to do that in CMake.

Posted by Uli Köhler in Build systems, Embedded

How to fix dput ‘error 58: gpgme_op_verify’

Problem:

You want to upload a package to a PPA using dput <filename>.changes, but you see an error message like this:

Checking signature on .changes
gpg: /home/uli/dev/deb-buildscripts/stm8flash_0.1-git261-deb1-1_amd64.changes: error 58: gpgme_op_verify
gpgme_op_verify: GPGME: No data

Solution:

You need to sign your .changes file before uploading! Do this using

debsign -k 1BBC8BAA <filename>.changes

where 1BBC8BAA is your GPG key ID which you intend to sign with.

Posted by Uli Köhler in Build systems

How to change CMake object file suffix from default ‘.o’

In order to configure CMake to use an alternate object file suffix (default: .o on Linux) use these lines in your CMakeLists.txt:

set(CMAKE_C_OUTPUT_EXTENSION ".rel")
set(CMAKE_CXX_OUTPUT_EXTENSION ".rel")

This example changes the output extension from .o to .rel (which is required for the SDCC compiler). Be sure to replace ".rel" by your desired output suffix.

Note that in order for these to take effect, you might need to completely remove CMakeCache.txt, CMakeFiles & cmake_install.cmake:

rm -rf CMakeCache.txt CMakeFiles cmake_install.cmake

 

Posted by Uli Köhler in C/C++, CMake

A working SDCC STM8 CMake configuration

If you have been looking desperately for a working CMake example for the SDCC compiler for STM8 microcontrollers here’s my take on it:

cmake_minimum_required(VERSION 3.2)

set(CMAKE_C_OUTPUT_EXTENSION ".rel")
set(CMAKE_C_COMPILER sdcc)
set(CMAKE_SYSTEM_NAME Generic) # No linux target etc

# Prevent default configuration
set(CMAKE_C_FLAGS_INIT "")
set(CMAKE_EXE_LINKER_FLAGS_INIT "")

project(STM8Blink C)
SET(CMAKE_C_FLAGS "-mstm8 --std-c99")
add_executable(main.ihx main.c)

# Flash targets
add_custom_target(flash ALL COMMAND stm8flash -c stlink -p stm8s105c6 -w main.ihx)

This will build main.ihx from main.c. main.ihx is a Intel Hex file which can be directly flashed using stm8flash.

The last lines setup make flash ; you might need to use the correct microcontroller (stm8s105c6 in this example, run stm8flash -l to show supported devices) and the correct flash adapter (stlink, stlinkv2, stlinkv21, stlinkv3 or espstlink).

The setup example shown here is for the STM8S eval board.

I suppose it can be easily modified for other microcontrollers, but I haven’t tried that so far.

Posted by Uli Köhler in CMake, Embedded, Hardware

How to fix CMake ‘make: *** No targets specified and no makefile found. Stop.’

Problem:

You are trying to build a software that is using the CMake build system.

You are trying to run make to build, but you see this error message:

make: *** No targets specified and no makefile found.  Stop.

Solution:

Before running make, you need to configure your build using CMake.

The simplest way of doing that is to run

cmake .

Typically you only need to do that once for each project ; CMake will automatically detect changes to CMakeLists.txt when you run make.

After that, you can run make again. If the build is successful, you’ll see a message like this:

[ 50%] Building CXX object CMakeFiles/main.dir/main.cpp.o
[100%] Linking CXX executable main
[100%] Built target main
Posted by Uli Köhler in CMake

How to fix CMake Parse error: Expected a command name, got unquoted argument with text "//"

Problem:

You want to build an application using CMake but you see an error message like this:

CMake Error at CMakeLists.txt:1:
  Parse error.  Expected a command name, got unquoted argument with text
  "//".


-- Configuring incomplete, errors occurred!
See also "/ram/CMakeFiles/CMakeOutput.log".
Makefile:176: recipe for target 'cmake_check_build_system' failed
make: *** [cmake_check_build_system] Error 1

Solution:

You CMakeLists.txt likely looks like this:

// Create main executable
add_executable(main main.cpp)

The issue is in the first line:

// Create main executable

CMake comments start with #, not with // !

Change any comment line starting with // to start with # instead. In our example:

# Create main executable
add_executable(main main.cpp)

Then try building again (e.g. using cmake . or make). Unless you have other issues in your CMake configuration, the output should now look like this (for simple builds):

-- Configuring done
-- Generating done
-- Build files have been written to: /ram
[100%] Built target main

 

Posted by Uli Köhler in CMake

How to build package or program using maven

In order to build a package using maven, run

mvn package

If you also want to install the maven package in the local maven repository (usually in ~/.m2), use

mvn install

In case you don’t have Maven installed, you can install it on Ubuntu using

sudo apt install maven

 

Posted by Uli Köhler in Build systems, Java

Minimal CMakeLists.txt for executables

This is the minimal CMakeLists.txt for building an executable named myproject from main.cpp:

cmake_minimum_required (VERSION 2.8.11)
project (MyProject)
add_executable (myproject main.cpp)

 

Posted by Uli Köhler in CMake

Fixing numpy.distutils.system_info.NotFoundError: No lapack/blas resources found on Ubuntu or Travis

Note: If you are on Windows, you can not install scipy using pip! Follow this guide instead: https://www.scipy.org/install.html. This blog post is only for Linux-based systems!

When building some of my libraries on Travis, I encountered this error during

sudo pip3 install numpy scipy --upgrade
numpy.distutils.system_info.NotFoundError: No lapack/blas resources

Solution

Install lapack and blas:

sudo apt-get -y install liblapack-dev libblas-dev

In most cases you will then get this error message:

error: library dfftpack has Fortran sources but no Fortran compiler found

Fix that by

sudo apt-get install -y gfortran

In Travis, you can do it like this in .travis.yml:

before_install:
    - sudo apt-get -y install liblapack-dev libblas-dev gfortran
Posted by Uli Köhler in Build systems, Linux, Python

How to fix “Cannot find crt1.o” on Ubuntu

Problem:

You’re trying to compile something (e.g. using GCC) on Ubuntu, but you get an error message similar to this one:

/usr/bin/ld: error: cannot open crt1.o: No such file or directory
/usr/bin/ld: error: cannot open crti.o: No such file or directory
/usr/bin/ld: error: cannot open crtn.o: No such file or directory

Continue reading →

Posted by Uli Köhler in Build systems, Linux

How to compile & install libc++ on Linux

Problem:

You want to compile and install libc++ (sometimes also named libcxx), but CMake complains with this error message

CMake Error at cmake/Modules/MacroEnsureOutOfSourceBuild.cmake:7 (message):
libcxx requires an out of source build. Please create a separate</em>

build directory and run 'cmake /path/to/libcxx [options]' there.
Call Stack (most recent call first):
 CMakeLists.txt:24 (MACRO_ENSURE_OUT_OF_SOURCE_BUILD)
CMake Error at cmake/Modules/MacroEnsureOutOfSourceBuild.cmake:8 (message):
 In-source builds are not allowed.

CMake would overwrite the makefiles distributed with Compiler-RT.
 Please create a directory and run cmake from there, passing the path
 to this source directory as the last argument.
 This process created the file `CMakeCache.txt' and the directory `CMakeFiles'.
 Please delete them.
Call Stack (most recent call first):
 CMakeLists.txt:24 (MACRO_ENSURE_OUT_OF_SOURCE_BUILD)

Continue reading →

Posted by Uli Köhler in Build systems, C/C++, Linux