Cpp Notes

ch25.finding_things

Chapter 25. Finding Things

Part 3 Preface...

  • Projects often need to interact with external entities rather than operating in isolation.
  • Interaction happens in two main ways:
    • Dependencies: Projects may need external files, libraries, executables, and packages.
    • Consumers: Projects may be integrated by others either at the source level or through pre-built binaries; they may also be expected to be installed system-wide.
  • Providing a project as a standalone package or for consumption by other projects requires maintaining a certain level of quality.
  • Automated testing is crucial in ensuring software quality and should be easy to define, execute, and report results.
  • The CMake suite assists in managing dependencies, automated testing through CTest, and packaging with CPack.
  • CMake also offers tools like presets to handle the increasing complexity in configuring, building, and testing projects as they scale, beneficial for developers and continuous integration builds.
  • The book section on CMake focuses on maximizing its benefits and avoiding common mistakes in managing external project interactions.

Ch 25 preface ...

  • Projects often depend on external resources such as libraries, tools, configuration files, or header files.
  • CMake aids in managing these dependencies by:
    • Finding Resources: Using find_...() commands to locate specific files, libraries, programs, or complete packages.
    • Integration Capabilities: Enabling projects to be easily found and integrated into other projects.
    • Modules and pkg-config: Leveraging CMake modules and pkg-config to obtain and provide information about external packages.
    • Package Files: Facilitating the creation of package files for consumption by other projects.

25.1. Finding Files And Paths

  • The find_...() command in CMake can search for a file using a single name or a list of names with the NAMES option.
  • Naming Variations: Useful when the file has different names across operating systems or versions.
  • Search Order: Names should be listed in preferred order; CMake stops searching after the first match.
  • Version Numbering: Names without version details should be listed before those with version details to prioritize locally built files.
  • Search Locations: Conducted in a well-defined order with options to skip certain locations.
  • Tailoring Search: Options allow customization of the search process to bypass certain locations if needed.
Location Skip option
Package root variables NO_PACKAGE_ROOT_PATH
Cache variables (CMake-specific) NO_CMAKE_PATH
Environment variables (CMake-specific) NO_CMAKE_ENVIRONMENT_PATH
Paths specified via the HINTS option X can't skip
Environment variables (system-specific) NO_SYSTEM_ENVIRONMENT_PATH
Cache variables (platform-specific) NO_CMAKE_SYSTEM_PATH
Paths specified via the PATHS option X can't skip
  • Package Root Variables (CMake >= 3.12):

    • Only searched when find_file() is invoked via a script during find_package() execution.
  • Cache Variables (CMake-specific):

    • CMAKE_PREFIX_PATH: Central variable affecting all find_...() commands; assumes a directory structure with subdirectories like bin, lib, include.
      • For find_file(), searches <prefix>/include and <prefix>/include/${CMAKE_LIBRARY_ARCHITECTURE} if CMAKE_LIBRARY_ARCHITECTURE is set, giving priority to architecture-specific directories.
    • CMAKE_INCLUDE_PATH and CMAKE_FRAMEWORK_PATH:
      • Provide directories for specific searches when not included in standard layouts.
      • CMAKE_INCLUDE_PATH used by find_file() and find_path(); does not append subdirectories.
      • CMAKE_FRAMEWORK_PATH used by find_file(), find_path(), and find_library(); handled similarly to CMAKE_INCLUDE_PATH.
  • CMAKE_LIBRARY_ARCHITECTURE:

    • Automatically set by CMake to prioritize architecture-specific directories; not typically user-modified.
  • Environment Variables (CMake-specific):

    • Variables: Utilizes CMAKE_PREFIX_PATH, CMAKE_INCLUDE_PATH, and CMAKE_FRAMEWORK_PATH.
    • Behavior: Treated similarly to their corresponding cache variables.
    • Platform Specifics:
      • On Unix platforms, list items in these environment variables are separated by a colon (:) instead of a semi-colon (;), aligning with Unix path list conventions.
    • Control Variable (CMake 3.16+):
      • CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH can be set to control default search behavior akin to CMAKE_FIND_USE_CMAKE_PATH.
  • Environment Variables (System-specific):

    • Variables: Utilizes INCLUDE and PATH, which may contain a list of directories separated by platform-specific path separators (colon on Unix, semi-colon on Windows).
    • Search Location Addition:
      • INCLUDE directories are added to the search locations before those from PATH.
    • Windows-Specific Processing:
      • For each PATH entry, the base path is derived by removing any trailing bin or sbin subdirectory.
      • If CMAKE_LIBRARY_ARCHITECTURE is defined, the search includes <base>/include/${CMAKE_LIBRARY_ARCHITECTURE}.
      • Regardless of CMAKE_LIBRARY_ARCHITECTURE, <base>/include is always added.
      • These paths are positioned immediately before their corresponding unmodified PATH entry in the search order.
    • Control Variable (CMake 3.16+):
      • CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH can be set to control default search behavior similar to CMAKE_FIND_USE_CMAKE_PATH.
  • Cache Variables (Platform-specific):

    • Variables: Includes CMAKE_SYSTEM_PREFIX_PATH, CMAKE_SYSTEM_INCLUDE_PATH, and CMAKE_SYSTEM_FRAMEWORK_PATH.

    • Purpose: These variables are set automatically by CMake to reflect compiler and platform-specific locations as part of the platform toolchain setup.

    • Developer Control:

      • Generally, developers should not manually set these variables.
      • An exception is when using a custom toolchain file, where setting these variables might be appropriate.
    • Behavior Control Variable (CMake 3.16+):

      • CMAKE_FIND_USE_CMAKE_SYSTEM_PATH can be set to control the default search behavior, similar to CMAKE_FIND_USE_CMAKE_PATH.
    • Install Prefix Considerations:

      • CMAKE_SYSTEM_PREFIX_PATH: May include the base install location, which could lead to undesirable search results.
      • CMake 3.24 Updates:
        • Introduced NO_CMAKE_INSTALL_PREFIX to ignore install prefixes in CMAKE_SYSTEM_PREFIX_PATH.
      • CMAKE_FIND_USE_INSTALL_PREFIX:
        • Set to true to block searching an install prefix by default.
      • CMAKE_FIND_NO_INSTALL_PREFIX:
        • Used in CMake 3.23 and earlier to similar effect, but with negated meaning.
        • The newer CMAKE_FIND_USE_INSTALL_PREFIX should be preferred and overrides the older variable if both are defined.
  • HINTS and PATHS Overview:

    • Purpose: Allow projects to specify additional search paths for locating files and packages.

    • Difference between HINTS and PATHS:

      • HINTS: Typically computed from dynamic variables or previously found locations.
      • PATHS: Fixed locations, usually searched last and do not depend on other values.
    • Environment Variables in HINTS and PATHS:

      • Supported by specifying the environment variable with ENV, e.g., PATHS ENV FooDirs.
      • Allows for platform-specific path separators: colon-separated on Unix, semicolon-separated on Windows.
    • Skip Options and Search Customization:

      • NO_..._PATH: Skip specific sets of locations.
      • NO_DEFAULT_PATH: Limits search to only the specified HINTS and PATHS.
      • Overrides any default behavior set by CMAKE_FIND_USE_... variables.
    • PATH_SUFFIXES:

      • Used to check additional subdirectories beneath each search location, expanding the total number of search locations.
    • Simplified Command Usage:

      • For basic needs, projects might only need to specify a single file and a few paths (similar to using the PATHS option).
      • Example of simplified command: find_file(outVar name [path1 [path2...]]).
    • Enforcing Search Priorities:

      • Projects can prioritize specific paths by invoking find_file() multiple times with different options.

      • Subsequent searches skip if the file is found, controlled by cache variable settings.

      • Example for prioritized path:

        find_file(FOO_HEADER foo.h
          PATHS /opt/foo/include
          NO_DEFAULT_PATH
        )
        find_file(FOO_HEADER foo.h)
  • Handling Unfound Files and Errors:

    • If a file is not found, the result variable evaluates to false in an if() expression.

    • From CMake 3.18, the REQUIRED option simplifies error handling for unfound files:

      find_file(FOO_HEADER foo.h
        PATHS /opt/foo/include
        NO_DEFAULT_PATH
      )
      find_file(FOO_HEADER foo.h REQUIRED)

25.1.1. Apple-specific Behavior (Skip)

25.1.2. Cross-compilation Controls

  • Complexity in Cross-Compiling:

    • Cross-compiling setups typically use a distinct directory structure for the toolchain, separate from the host toolchain, to prioritize finding target platform-specific versions of files, programs, and libraries.
  • Search Locations and Variables:

    • CMAKE_FIND_ROOT_PATH: Allows re-rooting the set of search locations by prepending directories to each search location.
    • CMAKE_SYSROOT: Acts as the system root directory in cross-compiling scenarios, affecting both the search locations and compilation flags.
    • CMAKE_SYSROOT_COMPILE and CMAKE_SYSROOT_LINK (From CMake 3.9): Similar to CMAKE_SYSROOT, affecting the root for compiling and linking.
    • These variables are typically set in the toolchain file, not directly by the project.
  • Handling Non-Rooted Locations:

    • Paths already under directories specified by CMAKE_FIND_ROOT_PATH, CMAKE_SYSROOT, CMAKE_SYSROOT_COMPILE, or CMAKE_SYSROOT_LINK are not re-rooted.
    • Paths under CMAKE_STAGING_PREFIX or starting with a ~ (indicating a user’s home directory) are also not re-rooted.
  • Search Order Control:

    • CMAKE_FIND_ROOT_PATH_MODE_INCLUDE: Controls the default order of searching among re-rooted and non-rooted locations.
    • Can be overridden per command call with options: CMAKE_FIND_ROOT_PATH_BOTH, ONLY_CMAKE_FIND_ROOT_PATH, or NO_CMAKE_FIND_ROOT_PATH.
  • Reliability in Cross-Compiling Contexts:

    • The find_file() command provides only one location, which may not be reliable in scenarios supporting switching between device and simulator builds without re-running CMake. This could lead to discrepancies if find_file() results depend on the build context.

25.1.3. Validators

  • Typically, the existence of a file is enough for it to be accepted as the found file. In more advanced scenarios, the file may need to pass other criteria for it to be accepted.
  • Advanced File Validation in CMake 3.25+:
    • VALIDATOR Keyword: Introduced to add a custom validation step to the file-finding process using find_file().
    • Validator Function Requirements:
      • Must accept two arguments:
        1. Result Variable Name: The name of a result variable to be set in the calling scope.
        2. File Absolute Path: The absolute path to the candidate file.
      • The file is accepted as found if the function does not set the result variable to false before returning.
# Only accept files that define a version string
function(has_version result_var file)
  file(STRINGS "${file}" version_line
  REGEX "#define +THING_VERSION" LIMIT_COUNT 1
  )
  if(version_line STREQUAL "")
  set(${result_var} FALSE PARENT_SCOPE)
  endif()
endfunction()
find_file(THING_HEADER thing.h VALIDATOR has_version)
# Require a companion version file in the same directory
function(has_version_file result_var file)
  cmake_path(GET file PARENT_PATH dir)
  if(NOT EXISTS "${dir}/thing_version.h")
  set(${result_var} FALSE PARENT_SCOPE)
  endif()
endfunction()
find_file(THING_HEADER thing.h VALIDATOR has_version_file)

25.2. Finding Paths

  • The find_path() command provides this functionality and is identical to find_file() in every way except that the directory of the file to be found is stored in the result variable

25.3. Finding Programs

  • Differences Between find_file() and find_program():
  1. Cache Variables (CMake-specific):

    • find_file(): Appends include to each item in CMAKE_PREFIX_PATH.
    • find_program(): Appends bin and sbin instead of include; CMAKE_LIBRARY_ARCHITECTURE has no effect.
    • CMAKE_PROGRAM_PATH: Replaces CMAKE_INCLUDE_PATH, used specifically by find_program().
    • CMAKE_APPBUNDLE_PATH: Replaces CMAKE_FRAMEWORK_PATH, used by both find_program() and find_package().
  2. Environment Variables (System-specific):

    • INCLUDE: Not applicable for find_program().
    • PATH: Items are checked directly without modification, consistent across all platforms.
  3. General Behavioral Differences:

    • NAMES_PER_DIR Option: Added in CMake 3.4, reverses the search order to check each name at a specific location before moving to the next location.
    • Windows Specifics: Automatically checks for .com and .exe extensions; .bat and .cmd files are not searched automatically.
    • CMAKE_FIND_APPBUNDLE: Replaces CMAKE_FIND_FRAMEWORK in find_program(), controlling the search between app bundle and non-bundle paths on Apple platforms, targeting Contents/MacOS subdirectory within app bundles.
    • CMAKE_FIND_ROOT_PATH_MODE_PROGRAM: Replaces CMAKE_FIND_ROOT_PATH_MODE_INCLUDE for find_program(), usually set to NEVER in cross-compiling scenarios to prioritize host platform tools.

25.4. Finding Libraries

  • Library Finding Similarities and Differences in CMake:

    • find_library() is similar to find_file() but includes NAMES_PER_DIR and behaves differently in specific areas like cache variables, environment variables, and general search behavior.
  • Cache Variables:

    • Under CMAKE_PREFIX_PATH, find_file() appends "include", while find_library() appends "lib".
    • CMAKE_LIBRARY_PATH is used instead of CMAKE_INCLUDE_PATH for find_library().
    • Both use CMAKE_LIBRARY_ARCHITECTURE similarly.
  • Environment Variables:

    • find_library() uses "LIB" environment variable instead of "INCLUDE".
    • On Windows, both commands use a complex logic involving "PATH", appending "lib" for libraries.
  • General Search Options:

    • NAMES_PER_DIR behaves the same as in find_program(), effective in CMake 3.4 or later.
    • CMAKE_FIND_FRAMEWORK sets search order between framework and non-framework paths; stores framework directory name in result if found.
    • CMAKE_FIND_ROOT_PATH_MODE_LIBRARY replaces CMAKE_FIND_ROOT_PATH_MODE_INCLUDE for library searches, particularly affecting search paths on Apple platforms.
  • Platform-Specific Naming and Extensions:

    • Library names usually prefixed with "lib" on Unix; Windows uses DLLs with associated import libraries.
    • find_library() abstracts most differences; however, may require overriding to prioritize static over shared libraries.
  • Techniques to Prioritize Static Libraries:

    • Simple example giving priority to static libraries on Linux but not on macOS or Windows:

      # WARNING: Not robust!
      find_library(FOOBAR_LIBRARY NAMES libfoobar.a foobar)
    • Robust method ensuring static library priority across all locations:

      # Better, static library now has priority across all search locations
      find_library(FOOBAR_LIBRARY libfoobar.a)
      find_library(FOOBAR_LIBRARY foobar)
  • Handling Import Libraries on Windows with CMake 3.25 or Later:

    • Using VALIDATOR to distinguish static libraries from import libraries using Visual Studio's lib tool:

      function(is_import_lib result_var file)
        cmake_path(GET file FILENAME filename)
        string(TOLOWER "${filename}" filename_lower)
        string(REGEX REPLACE "\\.lib$" ".dll" dll_filename_lower "${filename_lower}")
        execute_process(
          COMMAND ${CMAKE_AR} /nologo /list "${file}"
          RESULT_VARIABLE result
          OUTPUT_VARIABLE output
          ERROR_VARIABLE errors
        )
        string(TOLOWER "${output}" output_lower)
        if(result OR NOT errors STREQUAL "" OR NOT output_lower MATCHES "(^|\n|\\)${dll_filename_lower}(\n|$)")
          set(${result_var} FALSE PARENT_SCOPE)
        endif()
      endfunction()
  • Architecture-Specific Directory Search:

    • Different platforms and distributions use various directories for 32-bit and 64-bit libraries.
    • CMake properties like FIND_LIBRARY_USE_LIB32_PATHS, FIND_LIBRARY_USE_LIB64_PATHS, and FIND_LIBRARY_USE_LIBX32_PATHS help manage these searches.
    • CMAKE_FIND_LIBRARY_CUSTOM_LIB_SUFFIX can override default suffixes for specific project needs.
  • Special Considerations:

    • When using CMake with Xcode, direct specification of linker flags without paths might be necessary due to the inability of find_library() to differentiate between device and simulator libraries in cache.

25.5. Finding Packages

  • Package Discovery in CMake: CMake allows finding packages as a whole using the find_package() command, catering to project needs for version numbers or feature support without manual integration of components.

  • Definition Methods: Packages in CMake can be defined through two main methods:

    • Module: Defined externally, often by CMake or the project itself, and can become outdated as the package evolves.
    • Config: Defined within the package, aligns closely with find_package() functionality, providing more up-to-date integration.
  • Package Elements: Both methods typically specify variables, imported targets, and possibly functions or macros, influencing how projects use the packages (e.g., specifying program locations, library paths).

  • Preference for Imported Targets: Newer definitions lean towards providing imported targets over variables for better robustness and integration with CMake’s features, especially transitive dependencies.

  • Version Handling: find_package() can include a version argument specifying required minimum versions or exact matches, with newer CMake versions supporting version ranges to define upper and lower version limits.

  • Optional and Mandatory Packages: Projects can declare packages as optional or mandatory using REQUIRED. Messages regarding failures or discoveries can be controlled via the QUIET option.

  • Component Specification: Allows specifying which parts of a package are needed, with COMPONENTS for mandatory parts and OPTIONAL_COMPONENTS for optional parts. Not all packages support component-based selection.

  • Global Imported Targets: Recent CMake versions allow the use of the GLOBAL keyword in find_package() to create global visibility for imported targets, enhancing consistency across different directory scopes.

  • Search Mechanisms and Keywords: The MODULE keyword restricts searches to module packages, while the absence of it allows for config packages as well. Other keywords like NO_POLICY_SCOPE and REGISTRY_VIEW provide additional control and compatibility adjustments.

  • Module and Config File Differences:

    • Module Files: Typically located via CMAKE_MODULE_PATH and responsible for locating the package, checking versions, and handling components.
    • Config Files: Located by default naming conventions (e.g., <packageName>Config.cmake), providing richer, more robust package details and supporting additional search customization through the CONFIGS option.
  • Search Locations: CMake searches multiple potential directories for packages, with the ability to skip certain paths through specific NO_... keywords, reflecting a comprehensive approach to locate needed package components effectively.

<prefix>/
<prefix>/(cmake|CMake)/
<prefix>/<packageName>*/
<prefix>/<packageName>*/(cmake|CMake)/
<prefix>/<packageName>*/(cmake|CMake)/<packageName>*/
<prefix>/(lib/<arch>|lib*|share)/cmake/<packageName>*/
<prefix>/(lib/<arch>|lib*|share)/<packageName>*/
<prefix>/(lib/<arch>|lib*|share)/<packageName>*/(cmake|CMake)/
<prefix>/<packageName>*/(lib/<arch>|lib*|share)/cmake/<packageName>*/
<prefix>/<packageName>*/(lib/<arch>|lib*|share)/<packageName>*/
<prefix>/<packageName>*/(lib/<arch>|lib*|share)/<packageName>*/(cmake|CMake)/
  • In the above, <packageName> is treated case-insensitively and the lib/<arch> subdirectories are only searched if CMAKE_LIBRARY_ARCHITECTURE is set.
  • The lib* subdirectories represent a set of directories that may include lib64, lib32, libx32 and lib, the last of which is always checked.
  • If the NAMES option is given to find_package(), all of the above directories are checked for each name provided.
  • Most search locations can be disabled by adding the associated NO_… keyword
Location Skip Option
Package root variables NO_PACKAGE_ROOT_PATH
Cache variables (CMake-specific) NO_CMAKE_PATH
Environment variables (CMake-specific) NO_CMAKE_ENVIRONMENT_PATH
Paths specified via the HINTS option No skip
Environment variables (system-specific) NO_SYSTEM_ENVIRONMENT_PATH
User package registry NO_CMAKE_PACKAGE_REGISTRY
Cache variables (platform-specific) NO_CMAKE_SYSTEM_PATH
System package registry NO_CMAKE_SYSTEM_PACKAGE_REGISTRY
Paths specified via the PATHS option No skip

CMake Package Root Variables Overview

  • Package Root Variables (Use CMake >=3.12):

    • Variables like <packageName>_ROOT are used to specify search paths for find_package() and related commands.
    • These paths are prioritized in the same manner as CMAKE_PREFIX_PATH.
  • Usage:

    • When a find_package() command loads a module (e.g., FindFoo.cmake), it uses <Foo_ROOT> and $ENV{Foo_ROOT} as initial search paths.
    • Nested modules inherit and prioritize these paths, enhancing search efficiency without manual path specification.

CMake Cache and Environment Variables

  • Cache Variables:

    • Include CMAKE_PREFIX_PATH, CMAKE_FRAMEWORK_PATH, and CMAKE_APPBUNDLE_PATH.
    • Directly represent package installation points without additional directory specifications.
  • Environment Variables:

    • CMAKE_PREFIX_PATH, CMAKE_INCLUDE_PATH, CMAKE_FRAMEWORK_PATH adhere to platform-specific path separators.
    • <packageName>_DIR is checked before other path variables.

System and Platform-Specific Variables

  • System-Specific Variables:

    • Supported system-specific variable is PATH, used as a package install base point.
  • Platform-Specific Cache Variables:

    • Include CMAKE_SYSTEM_PREFIX_PATH, CMAKE_SYSTEM_FRAMEWORK_PATH, and CMAKE_SYSTEM_APPBUNDLE_PATH.

Additional Search Options

  • Package Registries:

    • Provide alternative package discovery methods outside standard system locations.
  • Search Control Options:

    • NO_... options and PATH_SUFFIXES offer controls over search locations.
    • CMAKE_FIND_PACKAGE_SORT_DIRECTION and CMAKE_FIND_PACKAGE_SORT_ORDER help in prioritizing package versions.

Advanced Usage

  • Caching and Config Files:

    • Once a config file for a package is found, subsequent searches are accelerated by using <packageName>_DIR.
    • If a newer package version is installed elsewhere, the cache may need updating to recognize this.
  • Disabling Package Discovery:

    • CMAKE_DISABLE_FIND_PACKAGE_<packageName> prevents discovery unless REQUIRED is specified.
    • CMAKE_REQUIRE_FIND_PACKAGE_<packageName> forces mandatory discovery of a package, influencing build process error handling.

25.5.1. Package Registries

  • Purpose:

    • Simplify package discovery by allowing packages to be registered in a centralized location accessible by CMake without additional configuration.
  • Functionality:

    • Supports referencing locations within standard system directories, custom directories, or build tree directories.

Creating and Managing Package Entries

  • Adding to Registry:

    • Use export(PACKAGE packageName) within CMakeLists.txt to add a package's build directory to the user package registry.
    • Ensures that the required config file is present in the directory; otherwise, the registry entry is removed if permissions allow.
  • Naming Conventions:

    • Registry entries typically use the MD5 hash of the directory path to avoid name collisions.
  • Potential Issues:

    • Exported locations can remain in the registry unintentionally, leading to unexpected behaviors in builds.
    • May cause issues in continuous integration systems by inadvertently using outdated or incorrect build directories.

Disabling Export Command

  • Pre CMake 3.15 Behavior:

    • By default, export(PACKAGE) modifies the package registry unless CMAKE_EXPORT_NO_PACKAGE_REGISTRY is set to true.
  • Post CMake 3.15 Behavior:

    • Introduces policy CMP0090; when set to NEW, export(PACKAGE) requires CMAKE_EXPORT_PACKAGE_REGISTRY set to true to modify the registry.

Controlling Package Registry Usage

  • Legacy Variables:

    • CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY and CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY control the reading from user and system package registries, respectively.
  • Updated Variables (from CMake 3.16):

    • Replaced by CMAKE_FIND_USE_PACKAGE_REGISTRY and CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY for more consistent behavior with other CMAKE_FIND_USE... variables.

In practice: Underutilization

  • Due to the manual effort required to manage entries and the limited automation support, package registries are infrequently used.
  • Automatic registration/unregistration by package installers/uninstallers is conceptually straightforward but rarely implemented due to the complexity of varying installation methods.

25.5.2. FindPkgConfig

Limitations of Some Find Modules

  • Outdated Modules: Some modules do not use modern CMake practices, such as creating imported targets, instead defining variables that projects must manage manually.
  • Incompatibility: Modules may not be updated to accommodate the latest package releases, potentially leading to incorrect configurations.

Alternative: PkgConfig Support

  • PkgConfig Module: Utilizes pkg-config for package information, which is typically more up-to-date as it is provided directly by the package.
  • Automatic Target Creation: Imported targets can be created automatically, simplifying project configuration.

Implementation of PkgConfig in CMake

  • FindPkgConfig Module:
    • Locates the pkg-config executable and sets variables like PKG_CONFIG_FOUND and PKG_CONFIG_VERSION_STRING.
    • Functions such as pkg_check_modules() and pkg_search_module() are used to find package details through pkg-config.

Key Functions and Their Usage

pkg_check_modules(prefix
  [REQUIRED] [QUIET]
  [IMPORTED_TARGET [GLOBAL] ]
  [NO_CMAKE_PATH]
  [NO_CMAKE_ENVIRONMENT_PATH]
  moduleSpec1 [moduleSpec2...]
)
pkg_search_module(prefix
  [REQUIRED] [QUIET]
  [IMPORTED_TARGET [GLOBAL] ]
  [NO_CMAKE_PATH]
  [NO_CMAKE_ENVIRONMENT_PATH]
  moduleSpec1 [moduleSpec2...]
)
  • pkg_check_modules():
    • Checks all modules listed in its arguments and sets variables based on package details from .pc files.
  • pkg_search_module():
    • Stops searching after finding the first module that meets the criteria, setting variables for that specific module.

Advanced Options and Variables

  • Imported Targets:
    • From CMake 3.6, the IMPORTED_TARGET option allows the creation of an imported target with the name PkgConfig::<prefix>.
    • GLOBAL keyword (CMake 3.13+) gives the imported targets global visibility.
  • Variable Settings:
    • Version requirements in module specifications can influence the search, e.g., name>=version.
    • Variables such as YYY_VERSION, YYY_PREFIX, YYY_INCLUDEDIR, and YYY_LIBDIR are set based on .pc file data.

Considerations and Best Practices

  • CMake Version Compatibility:
    • Ensure CMake 3.15 or later is used to avoid bugs in earlier versions, especially when using pkg_get_variable() to extract specific .pc file variables.
  • System-Specific Limitations:
    • Some older systems may have outdated versions of pkg-config, affecting the functionality of the FindPkgConfig module.

Recommendations

  • Prefer Imported Targets: When possible, use the IMPORTED_TARGET option to automate the handling of package details.
  • Version Management: Specify minimum required versions of CMake and pkg-config to avoid compatibility issues.
  • System Compatibility Checks: Validate the version of pkg-config on older systems to ensure reliable operation of the FindPkgConfig functions.