Moule Lin
← All notes

CMake Basics

The cmake repository contains some examples from my learning.

1. Why CMake?

There are many Make tools, such as GNU Make, Qt's qmake, Microsoft's MS nmake, BSD Make (pmake), Makepp, and so on. These Make tools follow different specifications and standards, and the Makefile formats they execute vary widely. This creates a serious problem: if software is meant to be cross-platform, it must be guaranteed to compile on different platforms. With the Make tools above, you would have to write a Makefile for each standard, which would be maddening. CMake is a tool designed to solve exactly this problem: it first lets developers write a platform-independent CMakeList.txt file to customise the whole build process, and then generates the required native Makefiles and project files according to the target user's platform.

2. make install

3. configure_file

Demo

```
#cmakedefine var1
#cmakedefine var2 "@var2@" #Note: the name between the @@ must match the variable name after cmakedefine
#cmakedefine var3 "${var3}" #Note: the name inside ${} must match the variable name after cmakedefine

```

4. Getting the Git hash and version with CMake: a complete example

GitHub code
Sometimes we need to record information such as the version number, the Git hash and the build time in a project, but obviously we don't want to fill in the Git hash and build time by hand. Here is a way to write this information into a header file and then compile it into a .so library or an executable.

That way, these values can be obtained through an interface provided by the library or from the output printed by the executable.

4.1 The utils.cmake file

```
# Used to get the Git hash and branch
macro(get_git_hash _git_hash)
    find_package(GIT QUIET)
    if(GIT_FOUND)# GIT_FOUND is a built-in variable
        execute_process( # cmake: execute_process runs an external command
            # COMMAND is the command-line command to run
            # GIT_EXECUTABLE is the path to the git executable; -1 means the last commit
            #--pretty=: display commit history in another format; options include oneline, short, medium, full, fuller, email, raw and format:<string>; the default is medium, e.g.:
            # --pretty=oneline: one line per commit, showing only the hash and the commit message (--oneline can also be used as a standalone option)
            # --pretty=format:” ": controls the format of the displayed records, e.g.:
            #     %H  full hash of the commit object
            #     %h  abbreviated hash of the commit object
            #     %T  full hash of the tree object
            #     %t  abbreviated hash of the tree object
            #     %P  full hash of the parent object
            #     %p  abbreviated hash of the parent object
            #     %an author name
            #     %ae author email address
            #     %ad author date (format can be customised with the -date= option)
            #     %ar author date, relative ("how long ago")
            #     %cn committer name
            #         What is the difference between author and committer?
            #         Relationship between author and committer: the author is the one who modified the program, the committer is the one who committed the code (how could someone else pull your changes and commit them if you never committed them yourself?)
            #         In fact, the author is the person who actually made the change, and the committer is the person who finally committed that work to the repository. So when you publish a patch for a project and a core member merges it into the project, you are the author and that core member is the committer (got it)
            #     %ce committer email address
            #     %cd commit date (format can be customised with the -date= option)
            #     %cr commit date, relative ("how long ago")
            #     %s  commit message
            COMMAND ${GIT_EXECUTABLE} log -1 --pretty=format:%h
            OUTPUT_VARIABLE ${_git_hash}        # store the output string in a variable
            # OUTPUT_VARIABLE: specifies the variable that receives what is written to standard output;
            OUTPUT_STRIP_TRAILING_WHITESPACE    # strip the trailing newline from the string
            ERROR_QUIET                         # silence execution errors
            WORKING_DIRECTORY                   # working directory
            ${CMAKE_CURRENT_SOURCE_DIR}
            )
        endif()
endmacro()

# get git branch
macro(get_git_branch _git_branch)   # start of the macro
    find_package(Git QUIET)     # find Git; QUIET suppresses errors
    if(GIT_FOUND)
    execute_process(          # run a child process
        COMMAND ${GIT_EXECUTABLE} symbolic-ref --short -q HEAD
        OUTPUT_VARIABLE ${_git_branch}        # store the output string in a variable
        OUTPUT_STRIP_TRAILING_WHITESPACE    # strip the trailing newline from the string
        ERROR_QUIET                         # silence execution errors
        WORKING_DIRECTORY                   # working directory
        ${CMAKE_CURRENT_SOURCE_DIR}
        )
    endif()
endmacro()                      # end of the macro

```

4.2 CMakeLists.txt

```
cmake_minimum_required(VERSION 2.9)

include(utils.cmake)
project(demo_git_config)


string(TIMESTAMP BUILD_TIMESTAMP "%Y-%m-%d %H:%M:%S")
message("Build timestamp is ${BUILD_TIMESTAMP}")

set(VERSION_MAJOR 0)
set(VERSION_MINOR 0)
set(VERSION_PATCH 1)
message("Version is ${VERSION_MAJOR} ${VERSION_MINOR} ${VERSION_PATCH}")

set(GIT_HASH "")
get_git_hash(GIT_HASH)
message("Git hash is ${GIT_HASH}")

set(GIT_BRANCH "")
get_git_branch(GIT_BRANCH)
message("Git branch is ${GIT_BRANCH}")


configure_file(
    "${PROJECT_SOURCE_DIR}/include/utils.h.in"
    "${PROJECT_SOURCE_DIR}/include/utils.h"
)
add_executable(${PROJECT_NAME} main.cpp)

include_directories(
    ${PROJECT_SOURCE_DIR}/include
)

install(TARGETS ${PROJECT_NAME}
    DESTINATION ${PROJECT_SOURCE_DIR})

```

4.3 utils.h.in

```
#ifndef UTILS_H_IN
#define UTILS_H_IN

#define VERSION_MAJOR @VERSION_MAJOR@
#define VERSION_MINOR @VERSION_MINOR@
#define VERSION_PATCH @VERSION_PATCH@

#define BUILD_TIMESTAMP "@BUILD_TIMESTAMP@"

#define GIT_BRANCH "@GIT_BRANCH@"
#define GIT_HASH "@GIT_HASH@"

#endif // UTILS_H_IN

```

4.4 Run cmake .

The generated utils.h is as follows

```
#ifndef UTILS_H_IN
#define UTILS_H_IN

#define VERSION_MAJOR 0
#define VERSION_MINOR 0
#define VERSION_PATCH 1

#define BUILD_TIMESTAMP "2022-11-26 21:35:07"

#define GIT_BRANCH "master"
#define GIT_HASH "0e1f110"

#endif // UTILS_H_IN

```

The output is as follows

```
(base) moule@mouledeMacBook-Air demo_git_config % ./demo_git_config
version is 0, 0, 1
timestamp is 2022-11-26 21:35:07
git is master, 0e1f110
```