CMake Compilation Tool and Project Building

Follow the public account “CV Research Society

Set it as “Starred“, heavy content delivered to you first!

Article Guide

This article starts with the compilation process of C/C++ code, clarifying the relationship between Make and Makefile, CMake and CMakeLists, and finally providing examples based on the syntax rules of CMakeLists to help everyone become familiar with how to write a simple compilation script.

1

What is the Compilation Process of C/C++?

The compilation process is essentially the translation of one language (usually a high-level language) into another language (usually a low-level language). The main workflow for compiling C/C++ programs is: source code → preprocessor → compiler → assembler → linker → executable program.

(1) Preprocessing

In C/C++, before the compiler compiles the source program, the program text must first be preprocessed. The preprocessor provides a set of pre-compilation processing directives and preprocessing operators, which start with #, and do not actually belong to C/C++ statements and cannot be translated by the compiler. They need to be preprocessed before actual compilation, ultimately outputting a “.i” file.

(2) Compilation

Compilation is the process of converting C/C++ code into assembly code. The compiler needs to perform lexical analysis and syntax analysis, and after confirming that all instructions comply with syntax rules without syntax errors, it converts the code into assembly language, generating assembly code.

(3) Assembly

Assembly is the process of translating the assembly code output from the previous step into target machine instructions, generating a target file that is equivalent to the source program in machine language.

(4) Linking

Linking is the process of linking the target files generated by assembly, system library target files, and library files together to generate an executable program that can run on a specific platform.

CMake Compilation Tool and Project Building

2

What is the Relationship Between Make and Makefile?

When there are many source files, it is generally not suitable to compile the code directly using gcc, and an automated compilation tool is needed.

Make (GNU Make) is an automation software used to compile source code files into executable binary files, thus completing automated compilation. The Make tool requires a Makefile to provide compilation rules during compilation. The Makefile defines a series of compilation rules, including the order of compilation and which files need to be recompiled, etc.

Using the Make tool can automate the compilation work. If a few source files are modified, only those source files need to be recompiled. If a header file is modified, all source files containing that header file need to be recompiled. This automated compilation greatly improves development efficiency and avoids unnecessary recompilation.

3

What are CMake and CMakeLists?

CMake is a more abstract cross-platform project management tool that can output various Makefile files or project files. For example, it can generate Visual Studio projects on Windows and Makefile files on Linux. In other words, CMake can generate project files for various compilers according to the same abstract rules, thus ignoring the differences between different platforms and abstracting them into a consistent environment.

The rules followed by the execution of CMake commands are written in the CMakeLists.txt file.

CMake Compilation Tool and Project Building

For a large project, writing a Makefile is quite complex. With the CMake tool, all source files can be read in and a Makefile can be automatically generated. The basic process for using CMake to write a cross-platform project is as follows:

  1. Write source files

  2. Write CMakeLists.txt

  3. Generate Makefile from CMake based on CMakeLists.txt

  4. Use Make to call gcc to generate executable files based on Makefile

CMake Compilation Tool and Project Building

4

How to Write CMakeLists

Writing CMakeLists.txt mainly includes the following steps:

  1. cmake_minimum_required(VERSION 2.8.0): Used to specify the minimum version required by cmake;

  2. project(Project): Used to specify the project name;

  3. include_directories(): Used to include header file directories;

  4. aux_source_directory(src dir_srcs): Used to include source file directories;

  5. set(TEST_MATH): Used to set environment variables; all source files needed for compilation should be placed here;

  6. add_executable(${PROJECT_NAME} ${TEST_MATH}): Used to add the executable file to be compiled;

  7. target_link_libraries(${PROJECT_NAME} m): Used to add the libraries required by the executable file;

Some common variables are preset in CMake syntax:

  • CMAKE_MAJOR_VERSION: cmake major version;

  • CMAKE_MINOR_VERSION: cmake minor version;

  • CMAKE_C_FLAGS: Set C compilation options;

  • CMAKE_CXX_FLAGS: Set C++ compilation options;

  • PROJECT_SOURCE_DIR: The root directory of the project;

  • PROJECT_BINARY_DIR: The directory where the cmake command is run;

  • CMAKE_CURRENT_SOURCE_DIR: The path where the current CMakeLists.txt is located;

  • CMAKE_CURRENT_BINARY_DIR: The target file compilation directory;

  • EXECUTABLE_OUTPUT_PATH: Redefine the storage location of the target binary executable file

  • LIBRARY_OUTPUT_PATH: Redefine the storage location of the target link library file

Example code is as follows (CMake syntax is case insensitive)

cmake_minimum_required(VERSION 2.8.12)option(ARM "Activate the ARM cross-compile" OFF)if(ARM)    message(STATUS "ARM Cross-Compile")    set(CMAKE_SYSTEM_NAME Linux)    set(CMAKE_C_COMPILER /usr/local/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc)    set(CMAKE_CXX_COMPILER /usr/local/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-g++)    set(CMAKE_BUILD_TYPE "Release")else()    message(STATUS "Default Compile")endif()project("Demo")

In the above code, cmake_minimum_required specifies the minimum version of cmake, and the if-else statement performs conditional judgment. Since cmake can compile across platforms, the above code selects either the ARM cross-compiler or the default compiler for Windows using macros.

project() is mentioned above to specify the name of the current project. The message() function, similar to printf, is used to print information. The set() function is used to explicitly define variables, such as defining the CMAKE_C_COMPILER variable to save the path of the ARM platform C compiler and defining the CMAKE_BUILD_TYPE variable to specify that the program being compiled is a Release version.

add_compile_options(-D_DEBUG)add_compile_options(-D_INTERNALDEBUG) add_compile_options(-std=c++11)add_compile_options(-O0)add_compile_options(-g)add_compile_options(-Wall)

add_compile_options is mainly used to set compilation options. For example, in the example code, -std=c++11 specifies to add C++11 support when compiling C++ code; -g allows all useful warnings provided by gcc to be emitted into the generated binary file; -O0 adjusts the compilation optimization level; to set the highest, -O3 is needed, and the lowest is -O0, which means no optimization;

# Find requirementsfind_package(PCL REQUIRED)if(NOT PCL_FOUND)    message("Not found PCL")endif()include_directories(${PCL_INCLUDE_DIRS})link_directories(${PCL_LIBRARY_DIRS})add_definitions(${PCL_DEFINITIONS})

When using third-party libraries in a project, it is necessary to know where to find header files, where to find library files, and what the names of the linked libraries are. The find_package() command will search for Find.cmake in the module path, which records the relevant information about the library. For example, when the configuration path of the third-party library is found, the header files can be included using include_directories; the library files can be linked using link_directories; and add_definitions controls the source code switch of the library, ensuring that when modifying the third-party library code, the source code is not damaged and additional functionality can be added.

file(GLOB_RECURSE SRC_LIST        ${CMAKE_CURRENT_SOURCE_DIR}/modules/src/*.cpp)aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/modules/common     SRC_COMMON_LIST)add_library(perception STATIC            ${SRC_LIST}    ${SRC_COMMON_LIST})

file() stores the contents of files into variables according to custom search rules, where the parameter GLOB generates a list of all files matching the globbing expression and saves it to the variableSRC_LIST. Alternatively, using the parameter GLOB_RECURSE traverses all files matching the directory and subdirectory below and saves it to the variable SRC_LIST.

aux_source_directory() works similarly to file(), searching for all source files in the specified directory and storing the results in the specified variable name. For example, the above code saves the files in the common path to the variable SRC_COMMON_LIST.

add_library is used to package the source files saved in the variable into a library form, and you can choose between static or dynamic libraries. For example:

add_library(perception STATIC ${SRC_LIST})

add_library(perception SHARED ${SRC_LIST})

link_directories(    ${CMAKE_CURRENT_SOURCE_DIR}/modules/lib/)add_executable(main ${SRC_FILES})target_link_libraries(main     libdetect.a    libsegment.a    libtracker.a)

Finally, the process of generating the executable file and linking library files occurs. The add_executable function uses the specified source files to generate the target executable file.

Target executable files can be divided into three categories:

  • Regular executable target files

  • Imported executable target files

  • Alias executable target files

Among them, the link_libraries and target_link_libraries commands look quite similar but have different functions:

The link_libraries command specifies the path to the library files to be linked. You can use this command to specify the directory path of your generated library files so that the project can find them.

The target_link_libraries command links target files with library files, allowing you to specify dynamic/static libraries. If you only provide the library name, the system will search for xxx.so or xxx.a files based on the linked library directory; alternatively, you can specify the full path.

Writing is not easy. If you find this useful, please like and follow, thank you!

Recommended Reading

  • How to Deploy Deep Learning Algorithms on Embedded Systems?

  • Cloud Environment Setup from Scratch (1)

  • Cloud Environment Setup from Scratch (2)

Follow the editor’s public account for daily shares on computer vision or autonomous driving perception topics.

CMake Compilation Tool and Project Building

Click the card below to follow the public account “CV Research Society” for more information:

Important!The CV Research Society Communication Group has been established

To provide a technical platform for everyone to communicate, we have specially opened the CV Research Society Communication Group.
If you need to join the group, you can long-press to scan the QR code below to add you to the group.

CMake Compilation Tool and Project Building

Likes and views are the biggest support❤️

Leave a Comment