126
votes

I was wondering if there was any sample code for Makefiles (make) and CMakeLists.txt (cmake) that both do the same thing (the only difference being that one is written in make and the other in cmake).

I tried looking for 'cmake vs make', but I never found any code comparisons. It would be really helpful to understand the differences, even if just for a simple case.

3
+1 This is a good question; when I was starting with cmake I wanted this too. But I doubt you'll find it because the capabilities just don't map that well one to the other. If you try to make cmake act like make, you'll drive yourself nuts, seriously. Best just to start from scratch. Things that are trivial in make are quite involved in cmake, and vice-versa.Ernest Friedman-Hill
@ErnestFriedman-Hill do you have more details on that? So make and cmake are so distinct that they should be seen more as complementary instead of competing tools?Ehtesh Choudhury
@Shurane -- cmake doesn't build anything itself; it creates Makefiles (and other, similar build scripts), which you then run. Therefore whenever you're writing cmake files, you have to think whether a command should apply during generation time, or during build time. Some actions -- copy a wildcarded set of files at build time, for example -- are quite complicated, compared to the "cp *.x $(OUTDIR)" you'd write in a Makefile. Perhaps the most annoying part for me is that the generated Makefiles are by design completely nonportable and inflexible (continued)Ernest Friedman-Hill
(continued) You can't even move the source directory on the same machine without re-running cmake to regenerate the Makefile! So the choice is not between cmake and make, but rather between writing portable Makefiles yourself, or using cmake to generate nonportable ones on each build machine (and given that you can use Cygwin or mingw on Windows, I generally find the former is easier.)Ernest Friedman-Hill
a nice question, but there is no specific answer, beecause both tools try to solve a different problem. cmake takes information on how to build programs generates makefiles that build the program. Hence cmake is a language with abstract build rules and gnu make is a dependency resolves that executes programs on a directed acyclic graph traversal.Alexander Oh

3 Answers

126
votes

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.txtthat 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.

7
votes

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.

4
votes

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.

CMake function

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.

(CMake function) Vs Make rules

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.

Internal Variables in 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 .

adding include and library path 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})

Vs adding include and library path in Make

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)

CMake configure/tweek

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.

Makefile configure/tweak

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.