CMake Meta Build System
- 6 minsFirst Draft: 2019-09-23
CMake: cross-platform compile tool
CMake is a cross-platform compile tool, which enables us to compile our project departed from platforms, and build system. We call it “Meta-build system”.
Meta-build system configures the build environment. So to speak, where to build, which to build with, and such things.
Nowadays, many projects use CMake to build its own project. You might have heard of once before. (Some projects would use its own compile tool such as “gn” in Chromium project.) Without some exceptional cases, most projects use CMake compiling their own source code.
Why are so many projects using CMake? What made them use CMake?
In advance, CMake is not a compiler. It’s a compiling tool. It does not compile actually, only helps to compile.
Then, Why do projects need the tool to compile? Isn’t it better for the compiler to do its job by itself?
Of course, compiler can do its own job for itself, but the script for compiler varies depending on the platform. In other words, there should be n scripts for the n platforms. Although we couldn’t get any benefit writing several scripts for each platform, we should write them without some auxiliary tool.
At this time, we could be free from the platfrom-dependent scripts with CMake compile tool. CMake makes us independent of platform during compilation. Furthermore, CMake provides several useful functionalities in addition to the previous advantage.
What is CMake?
On the CMake official website, it says
CMake is an open-source, cross-platform family of tools designed to build, test and package software. CMake is used to control the software compilation process using simple platform and compiler independent configuration files, and generate native makefiles and workspaces that can be used in the compiler environment of your choice. As it said, CMake is cross-platform tool to build. In addition, CMake provides not only platform-independent feature, but also compiler-independent feature. CMake could output Makefile or Ninja configuration file. Besides, CMake provides flexibility, and usability to build the projects: Directory-Tree-based CMake file structure.
How to Compile with CMake
If you want to compile using CMake, you need to know the grammar of CMake.
CMake config(source) file
CMake reads CMake configuration(source) file, “CMakeLists.txt” to generate Makefile or other compiler configuration file. Several options and settings can be set in the CMakeLists.txt file.
All you need to do is only writing CMakeLists.txt file in order to build your project with CMake.
The problem is, in case of me, when i struggled to learn CMake grammar, the messiness of CMake documentation made me even harder to learn. The best way to learn new things is always example. Decent single example outperforms tons of explanation. Let’s see how to write CMakeLists.txt file from now with some examples.
How to Write CMakeLists.txt
Example of CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(BBUtil LANGUAGES C CXX)
set(CMAKE_C_COMPILER gcc)
set(CMAKE_CXX_COMPILER g++)
set(PROJECT_DIR ${CMAKE_SOURCE_DIR})
set(SOURCE_DIR ${PROJECT_DIR}/src)
set(CMAKE_CXX_FLAGS "-std=c++11")
set(SOURCE_FILES
${SOURCE_DIR}/main.cpp
${SOURCE_DIR}/bit_converter.cpp
)
set(HEADER_DIR
${PROJECT_DIR}/src
${PROJECT_DIR}/include
)
add_definitions(
-DAOPTION=1
)
set(THIRD_PARTY_INCLUDE
${PROJECT_DIR}/third_party
)
set(SYSTEM_LIB
/usr/lib
/usr/local/lib
)
set(CODE_INCLUDE
${HEADER_DIR}
${THIRD_PARTY_INCLUDE}
)
set(CODE_LIB
${SYSTEM_LIB}
${THIRD_PARTY_LIB}
)
include_directories(${CODE_INCLUDE})
link_directories(${CODE_LIB})
add_executable(bbTest ${SOURCE_FILES})
#add_subdirectory(aaa)
You can see the CMakeLists.txt code at the repo.
CMake version.
At the first part of CMakeLists.txt file, is the statement of CMake version. (cmake_minimum_required) it states the minimum required version of CMake literally. Following with the CMake version, CMake native variables vary. CMAKE_C_COMPILER & CMAKE_CXX_COMPILER are native variables. CMake provides a variety of environmental variables, so you can make your projects compilable denpending on the various platforms.
It seems that recent CMake(over 3.0) even supports APPLE variable.
Local Variables
You can use your customized local variables in the CMake. With SET command, you can declare and define your own variables.
Usually, indicating the source files or linkable directories, use this local variables.
Variables in Program
You can define the variables which will be used in the program with ADD_DEFINITION. The exmaple above provided AOPTION variable with its value, 1. You can use the variable in the program like this:
#if AOPTION
blah blah blah
#endif
Of course, you can give the option at runtime like this.
$ cmake .. -DAOPTION=1
Directories
For the compilation, you need to make the compiler know the directories where the libraries and headers are. Then, the compiler could find the libraries and headers required to compile the project.
It’s same as I option or L option in GCC. Usually when compiling with GCC, you let GCC know the directories to find the targets with option I and L.
$ gcc -o ${OUTPUT_NAME} {SOURCE_FILES} -I{INCLUDE_DIR} -L{LINK_DIR}
Like this, in CMake, you can specify those directories with keyword INCLUDE_DIRECTORIES and LINK_DIRECTORIES.
INCLUDE_DIRECTOREIS specifies the directory entry the compiler should look for the headers and LINK_DIRECTORIES for the libraries (objects)
For example, Let’s assume, there are directories like:
ROOT_DIR
^- SUB_DEPTH1_DIR1
^-- SUB_DEPTH2_DIR1
^- SUB_DEPTH1_DIR2
the headers trying to include are loacted in SUB_DEPTH2_DIR, And you set ,
INCLUDE_DIRECTORIES({ROOT_DIR})
Then, in the source code, you need to include those headers like this.
#include "SUB_DEPTH1_DIR1/SUB_DEPTH2_DIR1/{HEADER_FILE}"
You need to note that INCLUDE_DIRECTORIES only specifies the entry point to include the headers.
In addition, such as INCLUDE_DIRECTORIES, LINK_DIRECTORIES only specifies the directories, not the target. You need to do something more to link the targes actually. I will explain more at the next chapter: Outputs.
Additional tips: Linux’s default path for the headers and libraries.
This is just an additional tips for the linux (Unix-like) users. If you are familar to Unix-like system, you can skip this.
Without specifying the directories the targets are, the compilers can find the targets at the default paths they are looking at.
When you compile, the compiler finds the external targets (e.g., libraries and headers) at 3 directory entries.
- First, the compiler finds the target at the directory where the source files are.
- Second, /usr/local/lib & /usr/lib (for library), and /usr/local/include & /usr/include (for headers).
- Lastly, the directories the user specified with LINK_DIRECTORIES in CMake or GCC L option.
Outputs
Lastly, this part is about the output you are trying to compile.
If you are trying to make ELF (executables), then use ADD_EXECUTABLES
ADD_EXECTUABLES({EXE_NAME} {LINKING_LIBRARY})
And for the library,
ADD_LIBRARY({LIBRARY_NAME} {LIBRARY_TYPE} {LINKING_LIBRARY})
$
$
$
###