Simplify the project building process using CMakeLists.txt

Directory

    • 1. CMakeLists.txt file structure
    • 2. Introduction to sample projects
    • 3. Detailed explanation of CMakeLists.txt
    • 4. Configure and build the sample project
    • 5. Frequently Asked Questions and Usage Tips

In the software development process, project construction is an inevitable link. As the size of the project increases, manually managing the compilation process becomes more and more cumbersome. To simplify the build process and achieve cross-platform support, CMake is widely adopted as a popular build system. This article will introduce the structure of the CMakeLists.txt file and how to use CMake to manage the compilation process of the project.

1. CMakeLists.txt file structure

CMakeLists.txt file structure refers to the basic parts and syntax rules in the CMakeLists.txt file. The CMakeLists.txt file is a CMake configuration file that describes the composition and build rules of the entire project. Each project typically contains one or more CMakeLists.txt files, located in the project’s root directory or subdirectory. CMake builds the project’s object files, library files, and executable files by processing these files recursively.

The following is the basic structure and syntax of the CMakeLists.txt file:

# Set the minimum CMake version number as needed
cmake_minimum_required(VERSION 3.10)

#Set project name and supported languages
project(MyProject LANGUAGES CXX)

#Add source files and library files
add_library(MyLibrary SHARED my_library.cpp)

#Specify the header file directory and library file directory
target_include_directories(MyLibrary PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_directories(MyLibrary PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/lib)

# Add dependencies
target_link_libraries(MyLibrary PRIVATE some_library)

# Set compilation options
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")

# Define installation rules
install(TARGETS MyLibrary DESTINATION /usr/lib)
install(DIRECTORY include/ DESTINATION /usr/include)

The above code shows the basic structure and syntax of the CMakeLists.txt file. It consists of multiple commands and parameters. The function of each part is explained below:

  • cmake_minimum_required: This command is used to specify the minimum version number requirement of CMake, that is, the CMake version number required by the project. At the beginning of the CMakeLists.txt file, you need to use this command to set the minimum version number of CMake.

  • project: This command is used to set the project name and supported programming languages. Supported languages can be C, C++, Java, etc., or a combination of multiple programming languages.

  • add_library: This command is used to add source files and library files. The source file can be a .cpp file, and the library file can be a dynamic library or a static library.

  • target_include_directories and target_link_directories: These two commands are used to specify the locations of the header file directory and library file directory respectively. The target_include_directories command is used to set the location of the header file directory, and the target_link_directories command is used to set the location of the library file directory.

  • target_link_libraries: This command is used to add dependencies, that is, external library files that the project depends on. These library files can be standard libraries that come with the system or third-party libraries.

  • set: This command is used to set compilation options, such as the standard version of the compiler, compiler warning options, etc.

  • install: This command is used to define the installation rules of the project and specify which files need to be installed to specific locations. Typically, executable files are installed into the /usr/bin directory, library files into the /usr/lib directory, and header files into the /usr/include directory.

2. Introduction to sample projects

Take a UVC project as an example:

list(APPEND CMAKE_MODULE_PATH "/home/lizh/workspaces/rv1126_facial_gate_release/external/rkmedia/cmake")

cmake_minimum_required(VERSION 2.8.0 FATAL_ERROR)
set(CMAKE_CXX_STANDARD 11)
add_definitions(-DDBUG)
add_definitions(-g)
PROJECT(uvc_app)
include(FindPkgConfig)
pkg_check_modules (JSON-C REQUIRED IMPORTED_TARGET json-c)

include_directories(uvc)


set(USE_RKAIQ ON)

if(USE_RKAIQ)
    find_package(RkAiq REQUIRED)
    include_directories(${<!-- -->RKAIQ_INCLUDE_DIRS})
    add_definitions(-DRKAIQ)
endif()

#head file path
set(BASE_PATH /home/lizh/workspaces/rv1126_facial_gate_release/external/camera_engine_rkaiq)
include_directories(
${<!-- -->BASE_PATH}/xcore
${<!-- -->BASE_PATH}/xcore/base
${<!-- -->BASE_PATH}/aiq_core
${<!-- -->BASE_PATH}/algos
${<!-- -->BASE_PATH}/common
${<!-- -->BASE_PATH}/common/linux
${<!-- -->BASE_PATH}/hwi
${<!-- -->BASE_PATH}/hwi/isp20
${<!-- -->BASE_PATH}/ipc
${<!-- -->BASE_PATH}/iq_parser
${<!-- -->BASE_PATH}/uAPI
${<!-- -->BASE_PATH}/ipc_server
#algos/awb
#../core/inc/luma
#../core/inc/stat_3a_ae
#../core/inc/stat_3a_af
#../core/inc/orb
#../core/inc/common
${<!-- -->BASE_PATH}/
${<!-- -->BASE_PATH}/include
${<!-- -->BASE_PATH}/include/common
${<!-- -->BASE_PATH}/include/common/mediactl
${<!-- -->BASE_PATH}/include/iq_parser
${<!-- -->BASE_PATH}/include/uAPI
${<!-- -->BASE_PATH}/include/xcore
${<!-- -->BASE_PATH}/include/xcore/base
${<!-- -->BASE_PATH}/include/algos
${<!-- -->BASE_PATH}/include/algos/a3dlut
${<!-- -->BASE_PATH}/include/algos/ablc
${<!-- -->BASE_PATH}/include/algos/accm
${<!-- -->BASE_PATH}/include/algos/acgc
${<!-- -->BASE_PATH}/include/algos/acp
${<!-- -->BASE_PATH}/include/algos/adebayer
${<!-- -->BASE_PATH}/include/algos/adehaze
${<!-- -->BASE_PATH}/include/algos/adpcc
${<!-- -->BASE_PATH}/include/algos/ae
${<!-- -->BASE_PATH}/include/algos/af
${<!-- -->BASE_PATH}/include/algos/afd
${<!-- -->BASE_PATH}/include/algos/afec
${<!-- -->BASE_PATH}/include/algos/agamma
${<!-- -->BASE_PATH}/include/algos/adegamma
${<!-- -->BASE_PATH}/include/algos/agic
${<!-- -->BASE_PATH}/include/algos/ahdr
${<!-- -->BASE_PATH}/include/algos/aie
${<!-- -->BASE_PATH}/include/algos/aldch
${<!-- -->BASE_PATH}/include/algos/alsc
${<!-- -->BASE_PATH}/include/algos/anr
${<!-- -->BASE_PATH}/include/algos/anr/tnr_md
${<!-- -->BASE_PATH}/include/algos/aorb
${<!-- -->BASE_PATH}/include/algos/ar2y
${<!-- -->BASE_PATH}/include/algos/asd
${<!-- -->BASE_PATH}/include/algos/asharp
${<!-- -->BASE_PATH}/include/algos/awb
${<!-- -->BASE_PATH}/include/algos/awdr
${<!-- -->BASE_PATH}/include/common/gen_mesh
${<!-- -->BASE_PATH}/include/ipc_server
)

include_directories(/home/lizh/workspaces/rv1126_facial_gate_release/external/rkmedia/include/easymedia)
include_directories(/home/lizh/workspaces/rv1126_facial_gate_release/external/rkmedia/include/rkmedia)

include_directories(/home/lizh/workspaces/rv1126_facial_gate_release/external/rkmedia)
include_directories(/home/lizh/workspaces/rv1126_facial_gate_release/external/camera_engine_rkaiq)

set(LIB_SOURCE
    uvc/uvc-gadget.c
    uvc/uvc_video.cpp
    uvc/yuv.c
    uvc/uvc_control.c
    uvc/uvc_encode.cpp
    uvc/mpi_enc.c
    uvc/uevent.c
    uvc/drm.c
    cJSON/cJSON.c
    uvc/mpp_osd.c
)

add_library(rkuvc SHARED ${<!-- -->LIB_SOURCE})
target_link_libraries(rkuvc pthread drm rockchip_mpp easymedia)

set(SOURCE
    main.c
    ${<!-- -->LIB_SOURCE}
)

set(UVC_APP_DEPENDENT_LIBS
    pthread
    drm
    rockchip_mpp
    rt
    rga
    easymedia
)

set(UVC_APP_INC
    ${<!-- -->CMAKE_CURRENT_SOURCE_DIR}/uvc/
)

option(ENABLE_USB3 "support usb3.0 config" OFF)
if (${<!-- -->ENABLE_USB3})
    add_definitions(-DUSE_USB3)
endif()

option(ENABLE_AISERVER "rockchip aiserver" OFF)
if (${<!-- -->ENABLE_AISERVER})
    add_definitions(-DUSE_RK_AISERVER)
    set(SOURCE
        ${<!-- -->SOURCE}
        uvc/uvc_ipc.cpp
       )
    find_package(Protobuf REQUIRED)
    if(PROTOBUF_FOUND)
        message(STATUS "protobuf library found")
        set(UVC_APP_DEPENDENT_LIBS ${<!-- -->UVC_APP_DEPENDENT_LIBS} protobuf-lite)
        include_directories(${<!-- -->PROTOBUF_INCLUDE_DIRS})
        include_directories(${<!-- -->CMAKE_CURRENT_BINARY_DIR})
        protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS
                              ${<!-- -->CMAKE_CURRENT_SOURCE_DIR}/uvc/uvc_data.proto)
        set(UVC_APP_INC ${<!-- -->UVC_APP_INC} ${<!-- -->PROTO_HDRS})
        set(SOURCE ${<!-- -->SOURCE} ${<!-- -->PROTO_SRCS} ${<!-- -->PROTO_HDRS})
    endif()
else()
    add_definitions(-fno-rtti)
option(USE_ROCKIT "uvc camera use rockit" OFF)
if(USE_ROCKIT)
add_definitions(-DUSE_ROCKIT=1)
SET(UVC_APP_DEPENDENT_LIBS
${<!-- -->UVC_APP_DEPENDENT_LIBS}
rockit
)
install(FILES conf/uvc.json DESTINATION ../etc/uvc_app/)
else()
# add_definitions(-DUSE_RKMEDIA=1)
# add_definitions(-DEPTZ_ENABLE=1)
# include_directories(${<!-- -->ROCKX_HEADER_DIR})
# set(UVC_APP_DEPENDENT_LIBS
# ${<!-- -->UVC_APP_DEPENDENT_LIBS}
#easymedia
#rockx
#rga
# ${<!-- -->CMAKE_CURRENT_LIST_DIR}/libs/libuvcAlgorithm.so)
#set(SOURCE
# ${<!-- -->SOURCE}
# process/eptz_control.cpp
# process/zoom_control.cpp
# )
# set(Algorithm_LIBS ${<!-- -->CMAKE_CURRENT_LIST_DIR}/libs/libuvcAlgorithm.so)
# install(FILES ${<!-- -->Algorithm_LIBS} DESTINATION lib)
endif()
endif()

option(DBSERVER_SUPPORT "enbale dbserver" OFF)
if (${<!-- -->DBSERVER_SUPPORT})
    add_definitions(-DDBSERVER_SUPPORT)
    set(UVC_APP_DEPENDENT_LIBS ${<!-- -->UVC_APP_DEPENDENT_LIBS} IPCProtocol)
endif()

if(CMAKE_SIZEOF_VOID_P EQUAL 8)
    add_definitions(-DUSE_ARM64)
else()
    target_link_libraries(rkuvc ${<!-- -->CMAKE_SOURCE_DIR}/libs/libmjpeg_fps_ctr.a)
    SET(UVC_APP_DEPENDENT_LIBS
                   ${<!-- -->UVC_APP_DEPENDENT_LIBS}
                   ${<!-- -->CMAKE_SOURCE_DIR}/libs/libmjpeg_fps_ctr.a
                   )

    option(ENABLE_MINILOGGER "enbale minilogger" ON)
    if (${<!-- -->ENABLE_MINILOGGER})
        find_package(MiniLogger REQUIRED)
        add_definitions(-DENABLE_MINILOGGER)
        set(UVC_APP_DEPENDENT_LIBS ${<!-- -->UVC_APP_DEPENDENT_LIBS} MiniLogger::MiniLogger)
        target_link_libraries(rkuvc MiniLogger::MiniLogger)
    endif()
endif()

option(COMPILES_CAMERA "compile:with rkmedia v4l2 flow " OFF)
option(EPTZ_SUPPORT "uvc support eptz" OFF)
if(COMPILES_CAMERA)
include_directories(process)
add_definitions(-DCAMERA_CONTROL)
set(SOURCE
${<!-- -->SOURCE}
process/camera_control.cpp
process/camera_pu_control.cpp
)
endif()

ADD_EXECUTABLE(uvc_app ${<!-- -->SOURCE})
#target_link_libraries(uvc_app ${<!-- -->UVC_APP_DEPENDENT_LIBS} PkgConfig::JSON-C)
target_link_libraries(uvc_app ${<!-- -->UVC_APP_DEPENDENT_LIBS} PkgConfig::JSON-C rkuvc -lrkaiq)


install(DIRECTORY ./uvc DESTINATION include
        FILES_MATCHING PATTERN "*.h")

install(TARGETS uvc_app DESTINATION bin)
install(DIRECTORY . DESTINATION bin
        FILES_MATCHING PATTERN "*.sh"
        PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
        GROUP_READ GROUP_WRITE GROUP_EXECUTE
        WORLD_READ WORLD_WRITE WORLD_EXECUTE)

if(EXISTS "mpp_enc_cfg.conf")
set(ETC_DST "${PROJECT_SOURCE_DIR}/../../target/etc/")
file(COPY mpp_enc_cfg.conf DESTINATION ${<!-- -->ETC_DST})
endif(EXISTS "mpp_enc_cfg.conf")

if(DEFINED INSTALL_LIBRKUVC)
install(TARGETS rkuvc DESTINATION lib)
endif()

This is a CMakeLists.txt file used to build a project named uvc_app. The following is the parsing of the file:

First, specify the minimum required CMake version using cmake_minimum_required and set the C++ standard to 11.

Then, set the project name to uvc_app through the PROJECT command.

Next, use the include command to include the FindPkgConfig module, and use the pkg_check_modules command to find the dependent library named JSON-C. The IMPORTED_TARGET parameter is used here to import the json-c library.

Then, use the include_directories command to add a series of header file paths.

Continue, use the add_library command to compile the source files in LIB_SOURCE into a shared library named rkuvc, and link to libraries such as pthread, drm, rockchip_mpp, and easymedia.

Then, specify the source file of the project by setting the variable SOURCE.

Next, set the UVC_APP_DEPENDENT_LIBS variable to include the libraries the project depends on.

Next, perform related definitions and processing based on the settings of the ENABLE_USB3 and ENABLE_AISERVER options.

Then, according to the setting of the DBSERVER_SUPPORT option, link the relevant definitions and libraries.

Selectively process according to the size of CMAKE_SIZEOF_VOID_P.

Finally, use the ADD_EXECUTABLE command to compile the source file into the executable file uvc_app and link the dependent libraries.

Finally, use the INSTALL command to install the relevant files to the specified directory.

3. Detailed explanation of CMakeLists.txt

CMakeLists.txt is a script file used by the CMake build system. Through this file, CMake can generate standard build files for various environments.

The writing of the CMakeLists.txt configuration file mainly includes the following parts:

  1. cmake_minimum_required: This command is used to set the minimum version requirement for running this cmake. For different versions of cmake, the commands, variables, attributes, etc. supported are different.
cmake_minimum_required(VERSION 3.5)
  1. project: This command indicates the name of the generated workspace, which is generally the same as your project name.
project(MyProject)
  1. set: Mainly used to explicitly define variables.
set(SRC_LIST main.c)
  1. add_executable and add_library: These two commands are used to add executable files and libraries that need to be compiled respectively. like:
add_executable(main ${SRC_LIST})

This command will generate an executable file main. The source file is the SRC_LIST variable defined through the previous set command.

add_library(my_lib STATIC ${SRC_LIST})

This command will generate a static library my_lib, and the source file is the SRC_LIST variable defined through the previous set command.

  1. target_link_libraries: This command is used to add shared libraries that need to be linked to the target.
target_link_libraries(main my_lib)
  1. include_directories and link_directories: These two commands are used to specify the search path for header files and the search path for library files respectively.
include_directories(${PROJECT_SOURCE_DIR}/include)
link_directories(${PROJECT_SOURCE_DIR}/lib)
  1. find_package: This command is used to find libraries that already exist in the system. For example, if your program needs to use the Boost library, you need to use this command to find Boost.
find_package(Boost REQUIRED)
if(Boost_FOUND)
    include_directories(${Boost_INCLUDE_DIRS})
    target_link_libraries(main ${Boost_LIBRARIES})
endif()

These are some commonly used commands in CMakeLists.txt. In fact, CMake also supports more complex syntax and can adapt to different compilation requirements. You can find more detailed information in the official documentation.

4. Configure and build the sample project

  1. Create a directory called “build” and enter it:
mkdir build
cd build
  1. Execute the CMake command to generate the build file:
cmake ..
  1. Compile the project using the generated build tools:
make
  1. Installation items:
make install

5. Frequently Asked Questions and Usage Tips

FAQ:

  1. How to use CMake?

CMake is a cross-platform automated build tool commonly used in C++ projects. You can describe the project and configure the build process by writing a CMakeLists.txt file. Enter the target folder in the console and run cmake . to start the build. For detailed usage, please refer to the official documentation.

  1. How to add a dependent library?

You can use the find_package command to find installed libraries, or add the path and linking method of dependent libraries in CMakeLists.txt.

  1. How to set compilation options?

Compiler options can be added using the add_definitions command. For example, add_definitions(-DDEBUG) can define a DEBUG macro.

  1. How to set the output path?

You can use the set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin) command to specify the output path of the executable file.

  1. How to use external header files or source files?

You can use the include_directories command to add the header file path, or use add_library to compile external source files into libraries and then link them.

Tips:

  1. Use the target_link_libraries command to link libraries, which avoids manually setting link paths and dependencies.

  2. When writing the CMakeLists.txt file, you should save as much information as variables as possible to facilitate subsequent modification and maintenance.

  3. You can use the file(GLOB sources "*.cpp") command to automatically search for all source files that meet the requirements for easy management.

  4. If you need to compile multiple executable files, you can use the add_executable() command to define them multiple times. At the same time, you need to pay attention to using different variable names to save the source file list to avoid overwriting each other.

  5. The CMake cache should be cleared before building. You can usually run the rm CMakeCache.txt command in the build directory to delete existing cache files.

BestPractice:

  1. It is recommended to split the CMakeLists.txt file into multiple small files and reference them using the include command. This can make the file structure clearer and facilitate maintenance.

  2. It is recommended to use CMake version control tools (such as cmake-format) to format the code and automatically complete missing standard options to ensure better code style and readability.

  3. It is recommended to use CTest for test management. This tool can easily add unit tests and integration tests to the project.

  4. It is recommended to use if statements in CMakeLists.txt to control compilation options, such as switching compilation options based on whether the platform or library is installed.

  5. It is recommended to use CMake’s cross-compilation feature to support building projects on other platforms. Cross-compilation can be used by setting the CMAKE_TOOLCHAIN_FILE path.

in conclusion:
CMakeLists.txt, as the core file of the CMake build system, provides us with a way to simplify the project building process. By understanding the structure and usage of the CMakeLists.txt file, we can manage the project’s compilation process more efficiently and improve development efficiency. I hope this article can help readers better understand and use CMake.