Make and CMake
HPC Tutorials has a whole chapter on Make and another on CMake; use them as references if you have syntax questions or would like to go deeper.
This video gives a quick overview of how to use Make and CMake:
GNU Make
GNU Make is a filesystem-based tool used to help build or make things. A makefile is a set of rules. Each rule specifies a target, its prerequisites, and the recipes, which are the instructions on how to build the target.
target: prereq1 prereq2
recipe
Makefiles are typically named “Makefile
” (no extension). If a makefile is named something other than Makefile
, use the -f
flag to identify it, e.g.: make -f Linux-clang.make
.
Makefiles require tabs. You cannot use leading spaces instead; you may need to configure your editor not to expand tabs when working with Makefiles. It will error which often looks like this:
Makefile:31 *** missing separator. Stop.
When make
builds a target, it will recursively build prerequisites. If a prerequisite is changed and make
is called again, it will try to only build the pieces that have been affected by the changes.
A particular target can be built by running make <target>
(e.g. make all
). If make
is invoked without a target, it will try to build the first rule that does not begin with a period.
Here’s a Makefile that could work for phase 2, assuming that your WaveOrthotope
class is in a header and that you’re also building the original wavesolve
from phase 1:
.PHONY: all
all: wavesolve wavesolve_serial
wavesolve: wavesolve.cpp WaveOrthotope.hpp
wavesolve_serial: wavesolve_serial.cpp WaveOrthotope.hpp
Implicit Rules
Make has a catalogue of implicit rules; here is one for C++ files:
n.o
is made automatically fromn.cc
,n.cpp
, orn.C
with a recipe of the form ‘$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c
’. We encourage you to use the suffix ‘.cc
’ for C++ source files instead of ‘.C
’.
This is how the previous example avoided writing recipes. The $(CXX)
, $(CPPFLAGS)
, and $(CXXFLAGS)
parts are how CMake does variables. By default CXX
is set to g++
while CPPFLAGS
and CXXFLAGS
are empty.
We can hook into these variables present in the implicit rules, for example to set the C++ version, by putting this at the top of the file:
CXXFLAGS += "-std=c++14"
The +=
means that the right-hand-side will be appended to the variable instead of setting it.
Food for thought: what if my compiler uses a different option for that same thing (e.g., -std=c++17
or maybe /Ostd
)?
Linking
GNU Make also has variables for hooking into the linker:
LDFLAGS
: specifies the search paths for the linker (e.g.-L/apps/libevent/2.0.22/lib
)LIBS
: Specifies libraries to link (e.g.-levent
)
As an example, if you want to link against /my/libs/foo.so
, you could use LDFLAGS += "-L/my/libs"
and LIBS += "-lfoo"
.
Cleaning Up
It is good practice to add a clean
PHONY
target to a Makefile which removes files generated by the build system. For instance, executables, libraries, and object files should be removed. For the sample makefile for phase 2, it might look like this:
.PHONY: clean
clean:
$(RM) wavesolve
$(RM) wavesolve_serial
CMake
Note: In order to use cmake
on the super computer, first run module load cmake
.
CMake manages the build process in an operating system and compiler independent manner–it aims to run anywhere, including Windows, unlike GNU Make which typically runs only on Linux-like systems.
This means that CMake allows developers to build software independent of platform and compiler. For example, on Windows, the Intel C++ compiler uses /Qstd=c++17
to specify the C++17 standard, while on Linux, the synonymous flag is --std=c++17
. CMake abstracts away these differences so that developers can avoid the headache of keeping track of such differences.
CMake solves these problems by declaring features and properties in a named way and then using generators to create the correct flags. For example:
CMakeLists.txt
cmake_minimum_required(VERSION 3.19)
project(Example CXX) # takes a list of languages
add_executable(Example example.cc)
Building a CMake Project
Here is an example directory. CMake is designed to do out-of-tree builds. In some CMake projects, you can’t even do it as part of the subtree and you need to do it elsewhere, such as /tmp
. This keeps the area containing the source code nice and clean.
$ tree .
.
| -- CMakeLists.txt
` -- example.cc
$ mkdir build
$ cd build
$ cmake ..
-- The CXX compiler identification is GNU 13.2.0
...
-- Build files have been written to: /tmp/build
By default, this will create a Makefile
in Linux (in addition to supporting files):
$ tree -L 2 -C ..
.
|-- build
| |-- CMakeCache.txt
| |-- CMakeFiles
| |-- cmake_install.cmake
| `-- MakeFile
|-- CMakeLists.txt
`-- example.cc
$ make
Scanning dependencies of target Example
[ 50%] Building CXX object
CMakeFiles/Example.dir/example.cc.o
[100%] Linking CXX executable Example
[100%] Built target Example
$ ./Example
Hello, World
Cleaning Up
CMake doesn’t have an equivalent of make clean
, so you should remove everything from the build directory and run cmake
afresh whenever you update CMakeLists.txt
. If you fail to do so, the updates you made to CMakeLists.txt
may not be fully reflected in your build configuration.