Cmake writes its own library batch import script import_libs.cmake

cmake batch load library)

  • Introduction
  • Requirements for importing libraries in batches
  • ideas and code examples
    • Method 1: Example of importing libraries in batches according to the main CMakeLists.txt delivery definition template file:
    • Method 2: According to the example of importing libraries in batches through configuration files:
  • Comparison of single loading library and batch loading library
  • Summary

Introduction

CMake is a cross-platform build tool that helps developers manage the build process of projects. Through CMake, developers can write CMakeLists.txt files to define the construction rules of the project, including compilation options, dependencies, etc. CMake is highly configurable and extensible, so it is widely used for various types of projects.
Macro (Macro) is a special statement in CMake, which can encapsulate repeated code fragments into a functional structure, and call the macro where the code fragment needs to be used. Macros can take parameters, allowing for more flexible code reuse.
The topic of this blog is to write a CMake macro for batch importing libraries. In a project, it is often necessary to introduce multiple different libraries to achieve various functions. The process of manually importing these libraries is tedious and error-prone, and the method of importing libraries one by one is also cumbersome. Therefore, we hope to import libraries in batches by writing a CMake macro to improve code maintainability and development efficiency.
If the number of libraries is relatively small, you can check out my other blogs:
CMake writes its own Findxxx.cmake file
Cmake link external library

Requirements for importing libraries in batches

There are many ways to introduce libraries into CMake projects. Among them, the most basic method is to manually import the library, that is, use the target_link_libraries command in the CMakeLists.txt file to introduce the required libraries one by one. For example:
target_link_libraries(my_target lib1 lib2 lib3)
Another method is to import libraries one by one, that is, use the find_package command to find and import the required libraries. For example:

find_package(Lib1 REQUIRED)
find_package(Lib2 REQUIRED)
find_package(Lib3 REQUIRED)
 target_link_libraries(my_target Lib1::Lib1 Lib2::Lib2 Lib3::Lib3)


Both methods have their own disadvantages. The method of manually importing libraries requires manually adding references to each library. If many libraries need to be imported, it will be very cumbersome and error-prone. The method of importing libraries one by one requires that each library has a corresponding Find module or Config file, otherwise it is necessary to manually write the corresponding search script, which increases the maintenance cost of the project.
Therefore, we need a more efficient way to import libraries in batches. Writing a CMake macro to batch import libraries can solve this problem. This macro can receive a library list as a parameter, and then automatically import all libraries, thereby avoiding the tedious operation of manually importing one by one, and also does not need to write a corresponding search script for each library. In this way, we can manage library dependencies in the project more conveniently and improve development efficiency.

ideas and code examples

First of all, we need to know which libraries to import, which can be passed in the main CMake or recorded in the configuration file.

  • Method 1: Example of batch importing library according to the main CMakeLists.txt delivery definition template file:

# CMakeLists.txt
 # define a directory path
set(THIRD_PARTY_PATH "${CMAKE_SOURCE_DIR}/lib")
# define a list variable
set(MYLIB_LIST "lib_name1" "lib_name2" "lib_name3")
# Import variables into another cmake file
configure_file(import_libs.cmake.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/import_libs.cmake.cmake @ONLY)

Among them, the configure_file command will replace the variables in the import_libs.cmake.in file to generate a new cmake file ${CMAKE_CURRENT_BINARY_DIR}/import_libs.cmake , where @ONLY means that only variables are replaced.
In the import_libs.cmake.in file, you can use this list variable like this:

# import_libs.cmake.in

# Import the list variable
set(MY_LIST @MYLIB_LIST@)
set(THIRD_PARTY_PATH "@THIRD_PARTY_PATH@")

# traverse the list variables
foreach(name ${MYLIB_LIST})
 set(INCLUDE_SEARCH_PATH "${THIRD_PARTY_PATH}/${line}/include") # Set header file search path
set(LINK_SEARCH_PATH "${THIRD_PARTY_PATH}/${line}/lib") # Set library file search path
include_directories(${INCLUDE_SEARCH_PATH}) # Add header file search path
link_directories(${LINK_SEARCH_PATH}) # Add library file search path
message(STATUS "ADD HEADER SEARCH PATH:${INCLUDE_SEARCH_PATH}")
message(STATUS "ADD LIBRARY SEARCH PATH:${LINK_SEARCH_PATH}")
endforeach()

In this file, the variables in @ @ will be replaced with the variables defined in the CMakeLists.txt file, which Represents a placeholder.
The purpose of using the placeholder is to mark the variable name in the template file, so that the configure_file command can replace the placeholder with the actual variable value. For example, set(MY_LIST @MY_LIST@) uses @MY_LIST@ as a placeholder.
The advantage of using placeholders is that when you need to use multiple variables in a template file, you can mark them all, and then replace them with actual variable values at once in the configure_file command, which avoids manual replacement one by one The trouble with variable names.
You can then include the import_libs.cmake file into other CMakeLists.txt files via the include command for loading libraries.

# another_CMakeLists.txt

# include cmake file
include(${CMAKE_CURRENT_BINARY_DIR}/other_cmake_file.cmake)
#In another_CMakeLists.txt file, you can load the library directly.
  • Method 2: According to the example of importing libraries in batches through configuration files:

#import_libs.cmake
cmake_minimum_required (VERSION 3.20)
#############################Set up auxiliary macros################### ####################################
MACRO(GETLINES_LINES_FILENAME)
 #Read the content of the specified file ${_FILENAME} and assign it to the variable contents
 FILE(READ ${_FILENAME} contents)
 message("TEST_FILE contents:" ${contents})
 #Replace all newlines \
 in the variable contents with semicolons ; and store the result in the variable ${_LINES}. Here a regular expression substitution is used, where \
 is escaped as \\
.
 #In CMake, semicolons are usually used to separate elements in lists
 STRING(REGEX REPLACE "\
" ";" ${_LINES} "${contents}")
ENDMACRO()
#############################Set key macro#################### #############################################
MACRO(INIT_LIB_INFO)
 set(THIRD_PARTY_PATH "${CMAKE_SOURCE_DIR}/third_party")
 set(CONTIDEP_file "${CMAKE_SOURCE_DIR}/lib.config") # Set dependent file path
 set(lines "")
GETLINES(lines ${CONTIDEP_file}) # read dependent file content
 set(DEPENDENCIES_PATH ${lines})# set dependency path
   foreach(line ${lines})
   set(INCLUDE_SEARCH_PATH "${THIRD_PARTY_PATH}/${line}/include") # Set header file search path
set(LINK_SEARCH_PATH "${THIRD_PARTY_PATH}/${line}/lib") # Set library file search path
include_directories(${INCLUDE_SEARCH_PATH}) # Add header file search path
link_directories(${LINK_SEARCH_PATH}) # Add library file search path
message(STATUS "ADD HEADER SEARCH PATH:${INCLUDE_SEARCH_PATH}")
message(STATUS "ADD LIBRARY SEARCH PATH:${LINK_SEARCH_PATH}")
  endforeach()
ENDMACRO()

#################################################### #################################################### ##
#list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/third_party/cmake")
INIT_LIB_INFO()

Comparison between single loading library and batch loading library


Use link_libraries to include directories and link libraries with target_link_directories:
advantage:
Simple, intuitive and easy to use;
You can directly specify the search path of the library in CMakeLists.txt, avoiding manual setting of environment variables;
Multiple libraries can be linked at once.
shortcoming:
Only the library under the specified path can be linked, and the default search path of the system cannot be used;
Finding system libraries is not supported.
Use find_library to find the library and use target_link_directories to link the library:
advantage:
You can find the default library of the system, which is more flexible;
You can increase the search path by setting the CMAKE_PREFIX_PATH environment variable;
The search path of find_library can be controlled by setting the CMAKE_MODULE_PATH environment variable.
shortcoming:
More cumbersome, you need to manually specify the library name and search path;
If the library name or search path changes, you need to manually modify the CMakeLists.txt file.
Usage scenario: If the library to be linked already exists in the specified path and there is no need to search for the system library, you can use link_libraries to include the directory and then use target_link_directories to link the library. If you need to find the system library or need to increase the search path by setting environment variables, you can use find_library to find the library and use target_link_directories to link the library.

Summary

This blog introduces how to write a CMake macro to import libraries in batches. First, the concepts of CMake and macros are introduced, and then the methods of manually importing and importing libraries one by one and their disadvantages are introduced respectively. Finally, we put forward the need to import libraries in batches, and explained the advantages and importance of writing CMake macros.
By writing a CMake macro, we can manage the library dependencies in the project more conveniently, and improve the maintainability and development efficiency of the project. At the same time, we also need to pay attention to some details, such as the need to ensure the correctness and version compatibility of the library when using macros.
In short, writing CMake macros is a very practical skill that can make our development work more efficient and convenient. Hope this blog is helpful to readers!