Convert Makefile to CMakeLists.txt manually

This article will describe converting Makefile source tree to CMakeLists.txt source tree manually.

1 Makefile and CMakeLists.txt

CMakeLists.txt is similar with configure and Makefile. Unix can generate Makefile from CMakeLists.txt with cmake command. This Makefile supports include file dependencies and parallel build. Amount of statement in CMakeLists.txt is equal with Makefile.

2 Makefile source tree

Makefile source tree is as below.

$ tree
.
├── include
│   └── hello.h
├── lib
│   ├── hello.cpp
│   └── Makefile
├── Makefile
└── src
    ├── main.cpp
    └── Makefile

3 directories, 6 files

2.1 Makefile at top directory

Makefile at top directory is as below.

  • Build process is done in Makefile of lib and src direcotry. Because src directory needs library in lib directory, build process in lib directory is done before src directory.
LIBNAME  := libexample.a
PROGNAME := example

export LIBNAME
export PROGNAME

all: lib
        $(MAKE) -C src

lib:
        $(MAKE) -C lib

clean:
        $(MAKE) -C lib clean
        $(MAKE) -C src clean

.PHONY: lib

2.2 Makefile for library

Makefile in lib directory is as below.

  • Getting objects from .cpp source code, creating libexample.a from objects with ar command. Objects will be built with implicit rule.
  • Writing dependencies for object in .d file, running build process when header file which is included by source code is update.
CXXFLAGS := -I../include -I.

SRC := $(wildcard *.cpp)
OBJ := $(patsubst %.cpp,%.o,$(SRC))
DEP := $(patsubst %.cpp,%.d,$(SRC))

all: $(LIBNAME)

clean:
        $(RM) $(DEP) $(OBJ) $(LIBNAME)

%.d: %.cpp
        $(CXX) -MM $(CXXFLAGS) $< | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@

ifneq ($(filter clean,$(MAKECMDGOALS)),clean)
-include $(DEP)
endif

$(LIBNAME): $(OBJ)
        $(AR) rc $@ $(OBJ)

2.3 Makefile for binary which uses library

Makefile in src directory is as below.

  • Getting objects from .cpp source code, creating example from objects with c++ command. Objects will be built with implicit rule.
  • .d file is as same as Makefile for library.
CXXFLAGS = -I../include -I.

SRC := $(wildcard *.cpp)
DEP := $(patsubst %.cpp,%.d,$(SRC))
OBJ := $(patsubst %.cpp,%.o,$(SRC))

all: $(PROGNAME)

$(PROGNAME): $(OBJ) ../lib/$(LIBNAME)
        $(CXX) $(CXXFLAGS) $^ -o $@

%.d: %.cpp
        $(CXX) -MM $(CXXFLAGS) $< | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@

ifneq ($(filter clean,$(MAKECMDGOALS)),clean)
-include $(DEP)
endif

clean:
        $(RM) $(DEP) $(OBJ) $(PROGNAME)

2.4 Build with Makefile

Run make command.

$ make
/Applications/Xcode.app/Contents/Developer/usr/bin/make -C lib
c++ -MM -I../include -I. hello.cpp | sed 's/\(hello\)\.o[ :]*/\1.o hello.d : /g' > hello.d
c++ -I../include -I.   -c -o hello.o hello.cpp
ar rc libexample.a hello.o
/Applications/Xcode.app/Contents/Developer/usr/bin/make -C src
c++ -MM -I../include -I. main.cpp | sed 's/\(main\)\.o[ :]*/\1.o main.d : /g' > main.d
c++ -I../include -I.   -c -o main.o main.cpp
c++ -I../include -I. main.o ../lib/libexample.a -o example

3 CMakeLists.txt source tree

CMakeLists.txt source tree is as below.

$ tree
.
├── CMakeLists.txt
├── include
│   └── hello.h
├── lib
│   ├── CMakeLists.txt
│   └── hello.cpp
└── src
    ├── CMakeLists.txt
    └── main.cpp

3 directories, 6 files

3.1 CMakeLists.txt at top directory

CMakeLists.txt at top directory is as below.

  • The cmake command can separate build directory from source tree. Because the cmake command generates some files, force separating build directory from source tree with checking CMAKE_SOURCE_DIR and CMAKE_BINARY_DIR.
  • Add lib directory and src directory to CMakeLists.txt source tree with add_subdirectory. Not writing dependency of lib directory and src directory. The dependency will be determined by target_link_libraries of CMakeLists.txt in src directory.
if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
  message(FATAL_ERROR "DO NOT BUILD in-tree.")
endif()

cmake_minimum_required (VERSION 2.8.11)
project(example)

add_subdirectory(lib)
add_subdirectory(src)

3.2 CMakeLists.txt for library

CMakeLists.txt in lib directory is as below.

  • Set current directory source codes to variable with AUX_SOURCE_DIRECTORY.
  • Create libexample from source codes in variable with add_library. Because this library name is liblibexample.a, rename to libexample.a with set_target_properties.
  • Publish library with target_include_directories.
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})

AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} source)

# Use libexample for avoiding conflicts with add_executable(example).
# And rename liblibexample.a to libexample.a
add_library(libexample ${source})
set_target_properties(libexample PROPERTIES OUTPUT_NAME "example")

target_include_directories(libexample PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

3.3 CMakeLists.txt for binary which uses library

CMakeLists.txt in src directory is as below.

  • Setting current directory source codes to variable with AUX_SOURCE_DIRECTORY.
  • Creating example from source codes in variable with add_executable. This binary name is example.
  • Refer libexample with target_link_libraries. This libexample is name which is set by target_include_directories.
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})

AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} source)
add_executable(example ${source})
target_link_libraries(example LINK_PUBLIC libexample)

3.4 Build with CMakeLists.txt

Running cmake command create Makefile. Running make command build source code again when source code is update. Need to run cmake command when CMakeLists.txt is update.

$ ls cmake-tree
CMakeLists.txt  include  lib  src
$ mkdir cmake-tree.build
$ cd cmake-tree.build
$ cmake ../cmake-tree -G 'Unix Makefiles'
$ make
[ 50%] Building CXX object lib/CMakeFiles/libexample.dir/hello.cpp.o
Linking CXX static library libexample.a
[ 50%] Built target libexample
[100%] Building CXX object src/CMakeFiles/example.dir/main.cpp.o
Linking CXX executable example