CMake is much more high-level. It's tailored to compile C++, for which you write much less build code, but can be also used for general purpose build. make has some built-in C/C++ rules as well, but they are useless at best.
cmake is a system to generate make files based on the platform (i.e. CMake is cross platform) which you can then make using the generated makefiles. While make is you directly writing Makefile for a specific platform that you are working with. If your product is crossplatform, then cmake is a better choice over make .
Since the syntax of cmake-language is not too different from modern programming languages it becomes easier for programmers to create CMakeLists. txt file and maintain it.
The following Makefile builds an executable named prog
from the sources
prog1.c, prog2.c, prog3.c and main.c
. prog
is linked against libmystatlib.a
and libmydynlib.so
which are both also built from source. Additionally, prog
uses
the library libstuff.a
in stuff/lib
and its header in stuff/include
. The
Makefile by default builds a release target, but offers also a debug target:
#Makefile
CC = gcc
CPP = g++
RANLIB = ar rcs
RELEASE = -c -O3
DEBUG = -c -g -D_DEBUG
INCDIR = -I./stuff/include
LIBDIR = -L./stuff/lib -L.
LIBS = -lstuff -lmystatlib -lmydynlib
CFLAGS = $(RELEASE)
PROGOBJS = prog1.o prog2.o prog3.o
prog: main.o $(PROGOBJS) mystatlib mydynlib
$(CC) main.o $(PROGOBJS) $(LIBDIR) $(LIBS) -o prog
debug: CFLAGS=$(DEBUG)
debug: prog
mystatlib: mystatlib.o
$(RANLIB) libmystatlib.a mystatlib.o
mydynlib: mydynlib.o
$(CPP) -shared mydynlib.o -o libmydynlib.so
%.o: %.c
$(CC) $(CFLAGS) $(INCDIR) $< -o $@
%.o: %.cpp
$(CPP) $(CFLAGS) $(INCDIR) -fPIC $< -o $@
Here is a CMakeLists.txt
that does (almost) exactly the same, with some comments to underline the
similarities to the Makefile:
#CMakeLists.txt
cmake_minimum_required(VERSION 2.8) # stuff not directly
project(example) # related to building
include_directories(${CMAKE_SOURCE_DIR}/stuff/include) # -I flags for compiler
link_directories(${CMAKE_SOURCE_DIR}/stuff/lib) # -L flags for linker
set(PROGSRC prog1.c prog2.c prog3.c) # define variable
add_executable(prog main.c ${PROGSRC}) # define executable target prog, specify sources
target_link_libraries(prog mystatlib mydynlib stuff) # -l flags for linking prog target
add_library(mystatlib STATIC mystatlib.c) # define static library target mystatlib, specify sources
add_library(mydynlib SHARED mydynlib.cpp) # define shared library target mydynlib, specify sources
#extra flags for linking mydynlib
set_target_properties(mydynlib PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
#alternatively:
#set_target_properties(mydynlib PROPERTIES COMPILE_FLAGS "-fPIC")
In this simple example, the most important differences are:
CMake recognizes which compilers to use for which kind of source. Also, it
invokes the right sequence of commands for each type of target. Therefore, there
is no explicit specification of commands like $(CC) ...
, $(RANLIB) ...
and so on.
All usual compiler/linker flags dealing with inclusion of header files, libraries, etc. are replaced by platform independent / build system independent commands.
Debugging flags are included by either setting the variable CMAKE_BUILD_TYPE
to "Debug",
or by passing it to CMake when invoking the program: cmake -DCMAKE_BUILD_TYPE:STRING=Debug
.
CMake offers also the platform independent inclusion of the '-fPIC' flag (via
the POSITION_INDEPENDENT_CODE
property) and many others. Still, more obscure settings can be implemented by hand in CMake just as well as in a Makefile (by using COMPILE_FLAGS
and similar properties). Of course CMake really starts to shine when third party
libraries (like OpenGL) are included in a portable manner.
The build process has one step if you use a Makefile, namely typing make
at the command line. For CMake, there are two steps: First, you need to setup your build environment (either by typing cmake <source_dir>
in your build directory or by running some GUI client). This creates a Makefile or something equivalent, depending on the build system of your choice (e.g. make on Unixes or VC++ or MinGW + Msys on Windows). The build system can be passed to CMake as a parameter; however, CMake makes reasonable default choices depending on your system configuration. Second, you perform the actual build in the selected build system.
Sources and build instructions are available at https://github.com/rhoelzel/make_cmake.
Grab some software that uses CMake as its buildsystem (there's plenty of opensource projects to choose from as an example). Get the source code and configure it using CMake. Read resulting makefiles and enjoy.
One thing to keep in mind that those tools don't map one-to-one. The most obvious difference is that CMake scans for dependencies between different files (e.g. C header and source files), whereas make leaves that to the makefile authors.
If this question is about a sample Makefile
output of the CMakeList.txt
file then please check the cmake-backend sources and generate one such Makefile
. If it is not then adding to the reply of @Roberto I am trying to make it simple by hiding the details.
While Make
is flexible tool for rules and recipe, CMake
is a layer of abstraction that also adds the configuration feature.
My plain CMakeLists.txt
will look like the following,
cmake_minimum_required(VERSION 2.8)
project(example)
file(GLOB testapp_SOURCES *.cc)
add_executable(testapp ${testapp_SOURCES})
Note, that CMake
hides how
the build can be done. We only specified what
is the input and output.
The CMakeLists.txt
contains list of function-calls that are defined by cmake
.
In Makefile
the rules and recipes
are used instead of functions
. In addition to function
-like feature, rules and recipes
provide chaining. My minimalistic Makefile
will look like the following,
-include "executable.mk"
TARGETS=testapp.bin
all:${TARGETS}
While the executable.mk
will look like the following,
SOURCES=$(wildcard *.cpp)
OBJECTS=$(SOURCES:.cpp=.o)
DEPS=$(SOURCES:.cpp=.d)
%.bin:$(OBJECTS)
$(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) $(LIBS)
.PHONY: all clean
clean:
$(RM) $(OBJECTS) $(DEPS) $(TARGETS)
-include $(DEPS)
Starting from the scratch I shall start with a Makefile
like the following,
all: testapp.bin
testapp.bin:sourcea.o sourcb.o
$(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) $(LIBS)
.PHONY: all clean
clean:
$(RM) $(OBJECTS) testapp.bin
I got this snippet from here and modified it. Note that some implicit-rules are added to this file which can be found in the makefile-documentation. Some implicit variables are also relevant here.
Note, that Makefile
provides the detail recipe
showing how
the build can be done. It is possible to write executable.mk
to keep the details defined in one file. In that way the makefile can be reduced as I showed earlier.
CMake
and Make
Now getting little advanced, in CMake
we can set a compiler flag like the following,
set(CMAKE_C_FLAGS "-Wall")
Please find out more about CMake
default variables in CMakeCache.txt
file.
The CMake
code above will be equivalent to Make
code below,
CFLAGS = -Wall
Note that CFLAGS
is an internal variable in Make
, the same way, CMAKE_C_FLAGS
is internal variable in CMake
.
We can do it in cmake
using functions.
target_include_directories(testapp PRIVATE "myincludes")
list(APPEND testapp_LIBRARIES
mytest mylibrarypath
)
target_link_libraries(testapp ${testapp_LIBRARIES})
We can add include and libraries by adding lines like the following,
INCLUDES += -Imyincludes
LIBS += -Lmylibrarypath -lmytest
Note this lines above can be generated from auto-gen tools or pkg-config. (though Makefile is not dependent of auto-config tools)
Normally it is possible to generate some config.h
file just like auto-config
tools by using configure_file
function. It is possible to do more trick writing custom functions. And finally we can select a config like the following,
cmake --build . --config "Release"
It is possible to add some configurable option using the option
function.
If somehow we need to compile it with some debug flag, we can invoke the make
like,
make CXXFLAGS=NDEBUG
I think internal variables, Makefile-rules
and CMake-functions
are good start for the comparison, good luck with more digging.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With