Struggling to Find Paths with CMake? The Find Family Commands Make It Easy!

Struggling to Find Paths with CMake? The Find Family Commands Make It Easy!Struggling to Find Paths with CMake? The Find Family Commands Make It Easy!Click the blue text to follow the author

1. Introduction

In complex software projects, dependency management is one of the core challenges during the build process. When we use CMake to build projects, we often need to introduce external libraries, header files, executables, or configuration files. Manually specifying the paths for these external dependencies can lead to a series of pain points:

  • Inflexible: When the project is in different development environments (such as Windows, Linux, macOS), on different machines, or when the dependency libraries are installed in non-standard paths, hard-coded paths will immediately become invalid, requiring manual modification of numerous <span>CMakeLists.txt</span> files.
  • Not portable: Manual paths make it difficult to compile the project on different systems or build servers, severely hindering the project’s cross-platform capabilities and the implementation of Continuous Integration/Continuous Deployment (CI/CD).
  • As the project scales and the number of dependencies increases, manually managing and updating these paths becomes exceptionally cumbersome and error-prone, significantly increasing development and maintenance costs.

Therefore, how to enable CMake to intelligently and automatically find the required library files (<span>.lib</span>, <span>.a</span>, <span>.so</span>, <span>.dll</span>), header files (<span>.h</span>, <span>.hpp</span>), executables (<span>.exe</span>) and other configuration files is key to ensuring smooth, efficient, and portable project builds.

To address the aforementioned path-finding pain points, CMake provides a powerful and flexible set of commands collectively known as the <span>find</span> family of commands. The core function of these commands is to: automatically search for and configure external dependencies in system preset paths, environment variables, user-specified paths, and project caches. They greatly simplify dependency management for cross-platform projects, allowing developers to focus on the code logic itself rather than tedious path configurations.

This article will delve into the five core commands in the CMake <span>find</span> family, revealing their respective uses, working principles, and commonly used parameters:

  • <span>find_file</span>: Used to find a single file with a specified name.
  • <span>find_library</span>: Used to find and locate library files (static or dynamic).
  • <span>find_package</span>: Used to find and load configuration information for external packages, recommended for managing complex dependencies in CMake.
  • <span>find_path</span>: Used to find the directory containing a specified file, commonly used to locate header file paths.
  • <span>find_program</span>: Used to find an executable program with a specified name.
Struggling to Find Paths with CMake? The Find Family Commands Make It Easy!
Insert image description here

2. Core Concepts and General Rules

Before diving into each specific <span>find</span> command, it is essential to understand the common search logic and general parameters that the <span>find</span> family of commands follows. These core concepts form the foundation of CMake’s powerful search capabilities.

2.1 Overview of Search Logic

The <span>find</span> command in CMake does not blindly search the entire file system; instead, it follows a carefully designed search strategy and priority rules to ensure efficient and predictable target finding.

Search Path Priority: When executing the <span>find</span> command, CMake searches in the following locations in a specific order:

  1. Cache: CMake first checks its internal cache. If a project has been successfully found before (for example, during the first configuration), its path will be cached. Subsequent searches will prioritize using the cached results, significantly speeding up configuration and avoiding redundant searches.
  2. <span>HINTS</span> parameter specified paths: These are paths explicitly provided in the <span>find</span> command using the <span>HINTS</span> keyword. These paths are treated as “hints” and have very high priority, typically used to specify known possible installation paths by users or developers.
  3. Environment Variables: CMake checks specific environment variables related to the target being searched. Additionally, CMake-specific environment variables such as <span>CMAKE_PREFIX_PATH</span>, <span>CMAKE_INCLUDE_PATH</span>, <span>CMAKE_LIBRARY_PATH</span> will also be considered.
  4. <span>PATHS</span> parameter specified paths: These are paths explicitly provided in the <span>find</span> command using the <span>PATHS</span> keyword. These paths have lower priority than <span>HINTS</span>, typically used to specify less certain but still necessary paths.
  5. System Standard Paths: CMake automatically searches a series of predefined standard installation paths based on the operating system type. For example, on Linux, it may search <span>/usr/local</span>, <span>/usr</span>, <span>/opt</span>, etc.;
  6. Registry (Windows specific): On Windows systems, CMake also queries standard locations in the registry to find installed software and libraries.
Struggling to Find Paths with CMake? The Find Family Commands Make It Easy!
Insert image description here

Cache Mechanism: Results are cached after finding to avoid repeated searches. Once the <span>find</span> command successfully finds a target (whether a file, library, path, or program), its result (i.e., the complete path found) is stored in CMake’s internal cache.

  • Accelerate Subsequent Configurations: In the same build directory, when running CMake configuration a second time, if dependencies have not changed, CMake will read results directly from the cache without re-executing time-consuming file system searches.
  • Users can view and modify cache variables through the CMake GUI or command-line tools (such as <span>cmake -LAH .</span>). If a user manually modifies the installation path of a dependency, they only need to update the corresponding variable in the cache without modifying <span>CMakeLists.txt</span>.
  • <span>_FOUND</span> and <span>_PATH</span> variables: After the <span>find</span> command completes, it sets two important variables to indicate the search results:
    • <span>VAR_FOUND</span>: A boolean variable that is <span>TRUE</span> if the target is found, otherwise <span>FALSE</span>.
    • <span>VAR_PATH</span> (or <span>VAR</span>): A string variable that stores the complete path if the target is found; otherwise, it is empty.

2.2 Parameter Parsing

Although each command in the <span>find</span> family has its specific uses and parameters, they share many common parameters. Understanding these parameters can effectively utilize the <span>find</span> commands.

  • <span>NAMES <name1> [<name2>...]</span>: Specifies the name of the target to be searched. One or more names can be provided. CMake will search for these names in the order provided until it finds the first match.

  • <span>PATHS path1 [path2...]</span>: Specifies an additional list of search paths. CMake will check these paths before its default search paths (but after <span>HINTS</span>).

  • <span>HINTS path1 [path2...]</span>: Provides “hint” paths for the search. These paths have very high priority, typically used to specify specific locations where users might have installed dependencies or known non-standard installation directories. They will be searched before <span>PATHS</span> and system default paths.

  • <span>REQUIRED</span>: If this parameter is specified, it indicates that the dependency is essential for the project build. If the <span>find</span> command fails to find the target, CMake will report an error and stop the configuration process.

  • <span>QUIET</span>: Silent mode. If this parameter is specified, when the <span>find</span> command fails to find the target, CMake will not output warning or error messages to the console (unless <span>REQUIRED</span> is also specified). This is useful when searching for optional dependencies or when wanting to customize error messages.

  • <span>NO_DEFAULT_PATH</span>: Prevents CMake from searching its default standard installation paths and environment variables. This is useful when you only want to search in specific locations specified in <span>HINTS</span> or <span>PATHS</span>, avoiding accidentally finding other versions in the system.

  • <span>_FOUND</span> variable: Each <span>find</span> command sets a variable named <span><VAR>_FOUND</span> (where <span><VAR></span> is the first parameter specified in the <span>find</span> command) to indicate whether the search was successful. If the target is found, this variable is <span>TRUE</span>; otherwise, it is <span>FALSE</span>.

  • <span>_PATH</span> variable: Each <span>find</span> command sets a variable named <span><VAR></span> (or in some cases <span><VAR>_PATH</span>) to store the complete path found. If the target is not found, this variable will be empty.

3. find_file: Precisely Locate a Single File

Among the commands in CMake’s <span>find</span> family, <span>find_file</span> is the most basic and direct one. It focuses on finding a single file with a specified name in the file system.

<span>find_file</span> command’s main purpose is to search for a file with a specific name in a series of specified or default paths. Its application scenarios are very broad:

  • Finding Configuration Files: The project depends on an external configuration file (such as <span>config.ini</span>, <span>settings.json</span>), which are usually not installed in standard library paths but may be located in the application’s installation directory, the user’s home directory, or specific configuration paths.
  • Locating Specific Scripts or Resource Files: For example, finding a Python script used for code generation or deployment, or a specific image, font, or other resource files.
  • Verifying File Existence: During the build process, it is necessary to confirm whether a required file exists in the expected location.
  • Finding Specific Versions of Header Files: Although <span>find_path</span> is more commonly used to find header file directories, <span>find_file</span> is also useful when needing to find header files with specific names, especially when confirming whether a header file exists or its specific location.

<span>find_file</span> command’s basic syntax is as follows:

find_file(<VAR> NAMES <name1> [<name2>...]
          [HINTS path1 [path2...]]
          [PATHS path1 [path2...]]
          [NO_DEFAULT_PATH]
          [REQUIRED]
          [QUIET]
          [NO_CACHE]
          [DOC "description"]
          [ENV var]
          [PATH_SUFFIXES suffix1 [suffix2...]]
          [NO_CMAKE_ENVIRONMENT_PATH]
          [NO_CMAKE_PATH]
          [NO_SYSTEM_ENVIRONMENT_PATH]
          [NO_CMAKE_PACKAGE_REGISTRY]
          [NO_CMAKE_BUILDS_PATH]
          [NO_CMAKE_INSTALL_PATH]
          [NO_CMAKE_SYSTEM_PATH]
          [NO_CMAKE_SYSTEM_PREFIX_PATH]
          [ONLY_CMAKE_PATH | ONLY_CMAKE_ENVIRONMENT_PATH | ONLY_SYSTEM_ENVIRONMENT_PATH]
)

The most commonly used parameters are: <span><VAR></span>, <span>NAMES <name1> [<name2>...]</span>, <span>PATHS path1 [path2...]</span>.

Example 1: Find the <span>config.ini</span> file in the project.

# Attempt to find config.ini in the following locations:
# 1. Project source directory (CMAKE_SOURCE_DIR)
# 2. Project build directory (CMAKE_BINARY_DIR)
# 3. Directory specified by environment variable MY_APP_CONFIG_DIR
# 4. /etc/my_app/ (common configuration path for Linux/macOS)
# 5. /usr/local/etc/my_app/ (another common configuration path)
find_file(MY_APP_CONFIG_FILE
          NAMES config.ini
          HINTS "$ENV{MY_APP_CONFIG_DIR}"
          PATHS "${CMAKE_SOURCE_DIR}"
                "${CMAKE_BINARY_DIR}"
                "/etc/my_app"
                "/usr/local/etc/my_app"
          REQUIRED  # If not found, report error and stop configuration
          DOC "Path to the application configuration file (config.ini)"
)

# Check if the file was successfully found
if(MY_APP_CONFIG_FILE)
    message(STATUS "Found config.ini at: ${MY_APP_CONFIG_FILE}")
    # You can add the path to compile definitions or installation rules here
    # add_definitions(-DMY_CONFIG_PATH="${MY_APP_CONFIG_FILE}")
else()
    message(FATAL_ERROR "config.ini not found! Please set MY_APP_CONFIG_DIR environment variable or place it in a standard location.")
endif()

Example 2: Find a specific version of a header file.

# Find the mylibrary_v2.h header file
find_file(MY_LIB_V2_HEADER
          NAMES mylibrary_v2.h
          PATHS "/usr/include/mylibrary/v2"
                "/opt/mylibrary/include"
                "/usr/local/include/mylibrary/v2"
          QUIET # If not found, do not print warnings
)

if(MY_LIB_V2_HEADER)
    message(STATUS "Found mylibrary_v2.h at: ${MY_LIB_V2_HEADER}")
    # You can add the directory containing this header file to the include path
    # include_directories(BEFORE "${MY_LIB_V2_HEADER}/../") # Assuming the header file is in a subdirectory
else()
    message(STATUS "mylibrary_v2.h not found. Using default version or alternative implementation.")
endif()

<span>find_file</span> is the first step in building flexible CMake projects, helping reliably locate various non-code files required in the project.

4. find_library: Link Library Files

<span>find_library</span> command is the core tool for finding and locating library files required for program linking (whether static or dynamic). It simplifies the complexity of handling library file paths and naming differences in cross-platform builds.

<span>find_library</span> command is specifically used to search for and locate library files in the file system. Its main purposes and common scenarios include:

  • Finding Static Libraries (<span>.lib</span>, <span>.a</span>) or Dynamic Libraries (<span>.dll</span>, <span>.so</span>, <span>.dylib</span>): This is the core function of <span>find_library</span>. It can find the corresponding static or dynamic link library for the operating system based on the library’s name in specified or default paths.
  • Resolving External Library Linking Issues: In C/C++ projects, programs often need to link to various external libraries (such as math libraries, graphics libraries, network libraries, etc.). <span>find_library</span> allows developers to avoid manually specifying the exact paths and filenames for each library on different operating systems, greatly simplifying build scripts.
  • Through <span>find_library</span>, CMake can automatically adapt to the naming conventions and installation paths of library files on different platforms (Windows, Linux, macOS, etc.), thus achieving good portability for the project.

<span>find_library</span> command’s basic syntax is similar to that of <span>find_file</span>, but its internal processing logic is optimized for library files.

find_library(<VAR> NAMES <name1> [<name2>...]
          [HINTS path1 [path2...]]
          [PATHS path1 [path2...]]
          [NO_DEFAULT_PATH]
          [REQUIRED]
          [QUIET]
          [NO_CACHE]
          [DOC "description"]
          [ENV var]
          [PATH_SUFFIXES suffix1 [suffix2...]]
          [NO_CMAKE_ENVIRONMENT_PATH]
          [NO_CMAKE_PATH]
          [NO_SYSTEM_ENVIRONMENT_PATH]
          [NO_CMAKE_PACKAGE_REGISTRY]
          [NO_CMAKE_BUILDS_PATH]
          [NO_CMAKE_INSTALL_PATH]
          [NO_CMAKE_SYSTEM_PATH]
          [NO_CMAKE_SYSTEM_PREFIX_PATH]
          [ONLY_CMAKE_PATH | ONLY_CMAKE_ENVIRONMENT_PATH | ONLY_SYSTEM_ENVIRONMENT_PATH]
)

Example 1: Find and link the <span>zlib</span> library, which is a commonly used data compression library.

# Find zlib library
# ZLIB_LIBRARY will store the full path of the found zlib library
# ZLIB_LIBRARY_FOUND will indicate whether it was found
find_library(ZLIB_LIBRARY
             NAMES zlib zlib1 # Try to find zlib or zlib1
             REQUIRED          # If not found, report error
             DOC "Path to the zlib compression library"
)

# Check if zlib library was successfully found
if(ZLIB_LIBRARY_FOUND)
    message(STATUS "Found zlib library: ${ZLIB_LIBRARY}")
    # Add zlib library to the global list of link libraries, or use in target_link_libraries
    # set(MY_APP_LIBRARIES ${MY_APP_LIBRARIES} ${ZLIB_LIBRARY})
else()
    # REQUIRED parameter has already handled the case of not found, this is just for demonstration
    message(FATAL_ERROR "zlib library not found!")
endif()

# Link the found zlib library to the executable target
# target_link_libraries(my_executable PRIVATE ${ZLIB_LIBRARY})

Example 2: Find a specific version of the OpenSSL library. OpenSSL is a powerful encryption library consisting of two main components: <span>ssl</span> (or <span>ssleay32</span>) and <span>crypto</span> (or <span>libeay32</span>).

# Find the SSL component of OpenSSL
find_library(OPENSSL_SSL_LIBRARY
             NAMES ssl ssleay32 # Try to find ssl or ssleay32
             DOC "Path to OpenSSL SSL library"
)

# Find the Crypto component of OpenSSL
find_library(OPENSSL_CRYPTO_LIBRARY
             NAMES crypto libeay32 # Try to find crypto or libeay32
             DOC "Path to OpenSSL Crypto library"
)

# Check if both components were found
if(OPENSSL_SSL_LIBRARY_FOUND AND OPENSSL_CRYPTO_LIBRARY_FOUND)
    message(STATUS "Found OpenSSL SSL library: ${OPENSSL_SSL_LIBRARY}")
    message(STATUS "Found OpenSSL Crypto library: ${OPENSSL_CRYPTO_LIBRARY}")
    set(OPENSSL_FOUND TRUE) # Set an overall FOUND variable
    set(OPENSSL_LIBRARIES "${OPENSSL_SSL_LIBRARY}" "${OPENSSL_CRYPTO_LIBRARY}") # Combine library paths
else()
    message(WARNING "OpenSSL libraries not found. Some features might be disabled.")
    set(OPENSSL_FOUND FALSE)
endif()

# Decide build behavior based on whether OpenSSL was found
if(OPENSSL_FOUND)
    # Assuming there is an executable target named 'my_secure_app'
    # Link OpenSSL library
    # target_link_libraries(FOUND)
    message(STATUS "Found OpenSSL SSL library: ${OPENSSL_SSL_LIBRARY}")
    message(STATUS "Found OpenSSL Crypto library: ${OPENSSL_CRYPTO_LIBRARY}")
    set(OPENSSL_FOUND TRUE) # Set an overall FOUND variable
    set(OPENSSL_LIBRARIES "${OPENSSL_SSL_LIBRARY}" "${OPENSSL_CRYPTO_LIBRARY}") # Combine library paths
else()
    message(WARNING "OpenSSL libraries not found. Some features might be disabled.")
    set(OPENSSL_FOUND FALSE)
endif()

# Decide build behavior based on whether OpenSSL was found
if(OPENSSL_FOUND)
    # Link OpenSSL library
    # target_link_libraries(my_secure_app PRIVATE ${OPENSSL_LIBRARIES})
endif()

5. find_path: Directory Containing Files

<span>find_path</span> command is a powerful tool used to find the directory containing a specific file. Its most common use is to locate the directory where external library header files are located.

<span>find_path</span> command’s core function is to search for and locate the directory containing a specified file in the file system. Its main purposes and common scenarios include:

  • Finding the Directory Containing a Specified File, Usually for Header Files: When a project depends on third-party libraries, the compiler needs to know where the header files for these libraries are located to successfully compile the source code. <span>find_path</span> can automatically find the root directory where these header files are located.
  • Resolving Header File Not Found Issues During Compilation: By finding the header file directory through <span>find_path</span>, these directories can be added to the compiler’s search paths, thus resolving compilation errors of “file not found”.
  • Using in Combination with <span>target_include_directories</span>: This is the most typical application scenario for <span>find_path</span>. Once <span>find_path</span> successfully finds the header file directory, the path of this directory can be passed to the <span>target_include_directories</span> command to add the required header file search path for a specific target (executable or library).
  • Similar to <span>find_library</span>, <span>find_path</span> also supports searching for files under different platforms and installation paths, thus enhancing the portability of CMake projects.

<span>find_path</span> command’s syntax is very similar to that of <span>find_library</span> and <span>find_file</span>:

find_path(<VAR> NAMES <name1> [<name2>...]
          [HINTS path1 [path2...]]
          [PATHS path1 [path2...]]
          [NO_DEFAULT_PATH]
          [REQUIRED]
          [QUIET]
          [NO_CACHE]
          [DOC "description"]
          [ENV var]
          [PATH_SUFFIXES suffix1 [suffix2...]]
          [NO_CMAKE_ENVIRONMENT_PATH]
          [NO_CMAKE_PATH]
          [NO_SYSTEM_ENVIRONMENT_PATH]
          [NO_CMAKE_PACKAGE_REGISTRY]
          [NO_CMAKE_BUILDS_PATH]
          [NO_CMAKE_INSTALL_PATH]
          [NO_CMAKE_SYSTEM_PATH]
          [NO_CMAKE_SYSTEM_PREFIX_PATH]
          [ONLY_CMAKE_PATH | ONLY_CMAKE_ENVIRONMENT_PATH | ONLY_SYSTEM_ENVIRONMENT_PATH]
)

Example 1: Find the header file directory for a custom library.

# Find the header directory for MyCustomLib
# MYCUSTOMLIB_INCLUDE_DIR will store the found directory path
find_path(MYCUSTOMLIB_INCLUDE_DIR
          NAMES mylib.h # Find the file named mylib.h
          HINTS
              /opt/MyCustomLib/include
              /usr/local/MyCustomLib/include
              "C:/Program Files/MyCustomLib/include"
          PATHS
              /opt/include
              /usr/local/include
              "C:/Program Files/include"
          PATH_SUFFIXES
              MyCustomLib/include # Try appending MyCustomLib/include to these paths
              include/MyCustomLib # Try appending include/MyCustomLib to these paths
              include             # Try appending include to these paths
          REQUIRED # If not found, report error
          DOC "Path to MyCustomLib header directory"
)

# Check if successfully found
if(MYCUSTOMLIB_INCLUDE_DIR)
    message(STATUS "Found MyCustomLib include directory: ${MYCUSTOMLIB_INCLUDE_DIR}")
    # Assuming there is an executable target named 'my_application'
    # Add the found header directory to the include path of that target
    # target_include_directories(my_application PRIVATE ${MYCUSTOMLIB_INCLUDE_DIR})
else()
    # REQUIRED parameter has already handled the case of not found, this is just for demonstration
    message(FATAL_ERROR "MyCustomLib header directory not found!")
endif()

<span>find_path</span> and <span>find_library</span> are two core commands in CMake used to find external dependencies. They work together to ensure that the compiler can find the required header files and the linker can find the required library files, thus achieving smooth project builds.

6. find_program: Executable Programs

<span>find_program</span> command is used to locate executable programs in the file system. It is very useful during the build process, allowing the search and use of various external tools, compilers, script interpreters, or any other executable files.

<span>find_program</span> command’s core function is to search for and locate specified executable programs. Its main purposes and common scenarios include:

  • Finding Executable Programs in the System or Specified Paths: Whether common development tools (such as <span>Git</span>, <span>Python</span>, <span>Java</span>, <span>Doxygen</span>) or specific compilers (such as <span>clang-format</span>, <span>gcc</span>), <span>find_program</span> can find their paths.
  • Executing External Commands During the Build Process: Once the path of an executable program is found, it can be used in CMake scripts with <span>execute_process</span> or custom commands (<span>add_custom_command</span>, <span>add_custom_target</span>) to call these programs, such as running code generators, executing tests, formatting code, etc.
  • Checking Toolchain Dependencies: In some complex build systems, specific external tools may be required to complete the compilation or linking process. <span>find_program</span> can be used to verify whether these tools exist and obtain their paths.
  • Through <span>find_program</span>, CMake can automatically adapt to the naming conventions and installation paths of executable programs under different operating systems (for example, the <span>.exe</span> suffix on Windows), thus improving the portability of the project.

<span>find_program</span> command’s syntax is similar to other <span>find_</span> series commands:

find_program(<VAR> NAMES <name1> [<name2>...]
             [HINTS path1 [path2...]]
             [PATHS path1 [path2...]]
             [NO_DEFAULT_PATH]
             [REQUIRED]
             [QUIET]
             [NO_CACHE]
             [DOC "description"]
             [ENV var]
             [PATH_SUFFIXES suffix1 [suffix2...]]
             [NO_CMAKE_ENVIRONMENT_PATH]
             [NO_CMAKE_PATH]
             [NO_SYSTEM_ENVIRONMENT_PATH]
             [NO_CMAKE_PACKAGE_REGISTRY]
             [NO_CMAKE_BUILDS_PATH]
             [NO_CMAKE_INSTALL_PATH]
             [NO_CMAKE_SYSTEM_PATH]
             [NO_CMAKE_SYSTEM_PREFIX_PATH]
             [ONLY_CMAKE_PATH | ONLY_CMAKE_ENVIRONMENT_PATH | ONLY_SYSTEM_ENVIRONMENT_PATH]
)

Example 1: Find the <span>Git</span> executable. In many projects, it may be necessary to obtain Git’s version information or execute Git commands during the build process.

# Find Git executable
# GIT_EXECUTABLE will store the full path of the found Git program
find_program(GIT_EXECUTABLE
             NAMES git # Find the program named git
             DOC "Path to the Git executable"
)

# Check if Git was successfully found
if(GIT_EXECUTABLE)
    message(STATUS "Found Git executable: ${GIT_EXECUTABLE}")
    # You can use execute_process here to get Git version or execute other Git commands
    # execute_process(COMMAND ${GIT_EXECUTABLE} --version OUTPUT_VARIABLE GIT_VERSION)
    # message(STATUS "Git Version: ${GIT_VERSION}")
else()
    message(WARNING "Git executable not found. Some version control features might be unavailable.")
endif()

Example 2: Find the <span>Python</span> interpreter. If the project depends on Python scripts for code generation, testing, or other auxiliary tasks, the Python interpreter must be found.

# Find Python interpreter
# PYTHON_EXECUTABLE will store the full path of the found Python interpreter
find_program(PYTHON_EXECUTABLE
             NAMES python3 python python2 # Try to find python3, then python, finally python2
             DOC "Path to the Python interpreter"
)

# Check if Python was successfully found
if(PYTHON_EXECUTABLE)
    message(STATUS "Found Python interpreter: ${PYTHON_EXECUTABLE}")
    # You can use execute_process here to run Python scripts
    # For example, run a script named generate_code.py
    # add_custom_command(
    #     OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated_file.h
    #     COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate_code.py
    #     DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate_code.py
    # )
else()
    message(FATAL_ERROR "Python interpreter not found! Please install Python.")
endif()

<span>find_program</span> is a key command in CMake for integrating external tools and scripts. It allows CMake projects to flexibly utilize various executable programs already available on the system, thus achieving more powerful automated builds and project management.

7. find_package: Package Search

<span>find_package</span> is the core command for finding and configuring external dependencies. It is more advanced and powerful than <span>find_path</span>, <span>find_library</span>, and <span>find_program</span> because it is designed to find and load the configuration information of entire software packages (Package), not just individual files or programs.

<span>find_package</span> command aims to automate the integration process of external libraries or packages. Its main purposes and common scenarios include:

  • Finding and Loading Configuration Information for External Packages: A “package” typically contains header files, library files, executables, and possibly additional configuration scripts needed. <span>find_package</span> can find all these components at once and set the corresponding variables.
  • The Recommended and Most Modern Dependency Management Method in CMake: With the development of CMake, <span>find_package</span> has become the preferred method for managing project dependencies. It provides a standardized way to integrate third-party libraries, making projects easier to build and maintain.
  • Handling Complex Dependencies: Some packages may have multiple components or submodules (such as Qt’s Core, Widgets, Network modules) or version requirements. <span>find_package</span> can handle these complexities, ensuring the correct version and required components of the package are found.
  • Through <span>find_package</span>, CMake can automatically adapt to different operating systems and installation paths for packages, greatly improving project portability and avoiding hard-coded paths.

7.1 Module Mode and Config Mode

<span>find_package</span> command has two main working modes, which determine how CMake searches for and loads package configuration information: Module Mode and Config Mode.

Module Mode works by searching for files named <span>Find<PackageName>.cmake</span>. These files are usually written by the CMake community or users to help CMake find packages that do not provide their own configuration files.

How It Works with Search Paths: When calling <span>find_package(<PackageName>)</span> and CMake cannot find the package in Config Mode, it will try to search for <span>Find<PackageName>.cmake</span> files in the following paths:

  1. <span>CMAKE_MODULE_PATH</span> variable specified paths.
  2. The <span>Modules</span> directory under the CMake installation directory (e.g., <span>/usr/share/cmake/Modules</span>). Once the <span>Find<PackageName>.cmake</span> file is found, CMake will execute it. This file typically uses <span>find_path</span>, <span>find_library</span>, <span>find_program</span>, etc., to locate the various components of the package and then set a series of variables.

Config Mode works by searching for <span><PackageName>Config.cmake</span> or <span><PackageName>-config.cmake</span> files provided by the package itself during installation. This is the recommended, more modern, and robust way of searching in CMake.

How It Works and Advantages: When calling <span>find_package(<PackageName>)</span>, CMake will first try to find the package in Config Mode. It will search in a series of predefined paths (such as <span>CMAKE_PREFIX_PATH</span>, <span>CMAKE_INSTALL_PREFIX</span>, system paths, etc.) and in the directory containing <span><PackageName>_DIR</span> that includes <span><PackageName>Config.cmake</span> or <span><PackageName>-config.cmake</span>.

Once these configuration files are found, CMake will load and execute them. These files are typically generated by the package developers during installation and contain all necessary information about the package (such as version, components, header file paths, library file paths, dependencies, etc.).

Advantages:

  • Config files are generated by the package itself, so they know precisely the installation location, version information, and dependencies of the package, reducing the likelihood of errors.
  • Provided by Package Maintainers: When the package is updated, its Config files are also updated accordingly, reducing compatibility issues.
  • Config files can contain complex logic to handle component selection, version matching, and transitive dependencies.

In Config Mode, a package may depend on other packages. To correctly handle these transitive dependencies, the Config files internally use the <span>find_dependency(<AnotherPackage>)</span><code><span> command. </span><code><span>find_dependency</span> is similar to <span>find_package</span>, but it is used internally within another <span>find_package</span> call to find the dependencies of the current package. It ensures that the search for dependencies follows the same patterns and parameters as the parent package (for example, if the parent package is <span>REQUIRED</span>, then the sub-dependencies should also be <span>REQUIRED</span>).

7.2 Syntax and Common Parameters

<span>find_package</span> command’s complete syntax is as follows:

find_package(<PackageName> [version] [EXACT] [QUIET] [REQUIRED]
             [COMPONENTS comp1 [comp2...]]
             [OPTIONAL_COMPONENTS comp3 [comp4...]]
             [NO_POLICY_SCOPE]
             [CONFIG|MODULE]
             [NO_DEFAULT_PATH]
             [NO_CMAKE_ENVIRONMENT_PATH]
             [NO_CMAKE_PATH]
             [NO_SYSTEM_ENVIRONMENT_PATH]
             [NO_CMAKE_PACKAGE_REGISTRY]
             [NO_CMAKE_BUILDS_PATH]
             [NO_CMAKE_INSTALL_PATH]
             [NO_CMAKE_SYSTEM_PATH]
             [NO_CMAKE_SYSTEM_PREFIX_PATH]
             [ONLY_CMAKE_PATH | ONLY_CMAKE_ENVIRONMENT_PATH | ONLY_SYSTEM_ENVIRONMENT_PATH]
             [PATHS path1 [path2...]]
             [HINTS path1 [path2...]]
             [PATH_SUFFIXES suffix1 [suffix2...]]
             [NO_CACHE]
             [DOC "description"]
)

Upon successfully calling <span>find_package</span>, the following variables are typically set:

  • <span><PackageName>_FOUND</span>: A boolean variable indicating whether the package was successfully found.
  • <span><PackageName>_INCLUDE_DIRS</span> or <span><PackageName>_INCLUDE_DIR</span>: A list of directories containing the package’s header files.
  • <span><PackageName>_LIBRARIES</span> or <span><PackageName>_LIBRARY</span>: A list of the package’s library files.
  • <span><PackageName>_DEFINITIONS</span>: Any preprocessor definitions that may be required.
  • <span><PackageName>_VERSION</span>: The version number of the found package.
  • For packages with components, there are usually variables like <span><PackageName>_<Component>_FOUND</span>, <span><PackageName>_<Component>_INCLUDE_DIRS</span>, etc.

Example: Find the Qt5 Library

# Find the Core and Widgets components of Qt5, requiring a minimum version of 5.12
find_package(Qt5 5.12 REQUIRED COMPONENTS Core Widgets)

if(Qt5_FOUND)
    message(STATUS "Found Qt5 version ${Qt5_VERSION} with Core and Widgets components.")
    # Add Qt5's header file directories to the project
    include_directories(${Qt5Core_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS})
    # Or more succinctly use Qt5's standard variables
    # include_directories(${Qt5_INCLUDE_DIRS})

    # Link to Qt5 libraries
    target_link_libraries(my_qt_app PRIVATE Qt5::Core Qt5::Widgets) # Recommended import target in modern CMake
    # Or use old-style variables
    # target_link_libraries(my_qt_app PRIVATE ${Qt5Core_LIBRARIES} ${Qt5Widgets_LIBRARIES})
else()
    message(FATAL_ERROR "Qt5 not found! Please ensure Qt5 5.12+ is installed and configured.")
endif()

<span>find_package</span> is the cornerstone of integrating external dependencies in CMake. Understanding its two modes and common parameters is crucial for writing robust and portable CMake projects. By effectively utilizing <span>find_package</span>, dependency management during the project build process can be greatly simplified.

8. Conclusion

The <span>find</span> family of commands in CMake for finding files, libraries, programs, and packages is the foundation for building portable, robust, and efficient CMake projects.

CMake provides a built-in mechanism to list its own <span>Find</span> modules. You can view the list of all <span>Find<PackageName>.cmake</span> modules included in the current CMake version by running the command <span>cmake --help-module-list</span>.

This command will list all built-in <span>Find</span> modules in CMake, such as <span>FindBoost.cmake</span>, <span>FindPython.cmake</span>, etc. These modules are written by CMake’s official or community for common libraries and tools, aimed at simplifying their search process.

It is important to note:

  • Not all libraries and programs are included: Especially for some niche, newer, or self-developed libraries, CMake may not have built-in <span>Find</span> modules.
  • Sometimes you must write detection scripts yourself: When the third-party library to be integrated does not provide a Config file (i.e., does not support Config mode), and CMake does not provide the corresponding <span>Find</span> module, you will need to write a <span>Find<PackageName>.cmake</span> file yourself. This usually involves using <span>find_path</span>, <span>find_library</span>, <span>find_program</span>, and other low-level commands to locate the required components and set the corresponding variables.

Reviewing the learned <span>find</span> family commands:

  • <span>find_file</span>: Find files.
  • <span>find_path</span>: Find the directory containing a specific file (usually for header files).
  • <span>find_library</span>: Find library files.
  • <span>find_program</span>: Find executable programs.
  • <span>find_package</span>: Find and configure entire packages, supporting both Module and Config modes.

These commands together form a powerful toolkit for CMake to handle external dependencies. Their core value lies in:

  • By abstracting file system paths, <span>find</span> commands enable CMake projects to work across different operating systems (Windows, Linux, macOS) and different installation layouts without hard-coding paths.
  • They provide rich search options (such as <span>HINTS</span>, <span>PATHS</span>, <span>ENV</span>) and can handle version requirements and component dependencies, thus improving the success rate and accuracy of searches.
  • <span>find</span> commands encapsulate the complexity of finding external dependencies, allowing developers to focus on the logic of the project itself rather than spending a lot of time configuring paths for third-party libraries.
  • Standardization: Especially the <span>find_package</span> command, which promotes standardized configuration information for packages (Config mode), making dependency management more unified and efficient.

It is these <span>find</span> family commands that make CMake an indispensable part of modern C++ project build systems.

Past Review Tutorials

Customized learning plans + code reviews! C++ training camp helps you grow quickly!

Build

CMake + Python: Building Seamlessly Linked C/C++ and Python Projects

Network

Struggling with TCP packet fragmentation? Just understand this principle and write code like this!

C++

Introduction to Range Adapters: Mastering transform, filter, and take three essentials

Struggling to Find Paths with CMake? The Find Family Commands Make It Easy!Lion LainWelcome to follow my public account Learn technology or submit articles

Leave a Comment