CMAKE-COMMANDS(7)

๐ŸŒธ NAME ๐ŸŒธ

cmake-commands โ€” CMake Language Command Reference. Yep bestie, this is the CMAKE-COMMANDS(7) manual page. Buckle up. ๐Ÿ’…

โœจ SCRIPTING COMMANDS โœจ

These commands are always available. No cap.

๐Ÿ’– block ๐Ÿ’–

Added in version 3.25.

Okay so block() is literally the โ€œevaluate a group of commands with a dedicated variable and/or policy scopeโ€ icon. ๐Ÿซถ

block([SCOPE_FOR [DIAGNOSTICS] [POLICIES] [VARIABLES]]
      [PROPAGATE <var-name>...])
  <commands>
endblock()

All commands between block() and the matching endblock() are recorded without being invoked. Once the endblock() is evaluated, the recorded list of commands is invoked inside the requested scopes, then the scopes created by the block() command are removed. Not me casually recording commands and then running them later. ๐Ÿ˜ญ

  • SCOPE_FOR โ€” specify which scopes must be created:
    • DIAGNOSTICS โ€” Added in version 4.4. Create a new diagnostic scope. This is equivalent to cmake_diagnostic(PUSH) with an automatic cmake_diagnostic(POP) when leaving the block scope.
    • POLICIES โ€” create a new policy scope. This is equivalent to cmake_policy(PUSH) with an automatic cmake_policy(POP) when leaving the block scope.
    • VARIABLES โ€” create a new variable scope.

If SCOPE_FOR is not specified, this is equivalent to:

block(SCOPE_FOR VARIABLES POLICIES DIAGNOSTICS)
  • PROPAGATE โ€” when a variable scope is created by the block() command, this option sets or unsets the specified variables in the parent scope. This is equivalent to set(PARENT_SCOPE) or unset(PARENT_SCOPE) commands. ๐Ÿซก
set(var1 "INIT1")
set(var2 "INIT2")
set(var3 "INIT3")

block(PROPAGATE var1 var2)
  set(var1 "VALUE1")
  unset(var2)
  set(var3 "VALUE3")
endblock()

# Now var1 holds VALUE1, var2 is unset, and var3 holds the initial value INIT3
This option is only allowed when a variable scope is created. An error will be raised in the other cases. โš ๏ธ

When the block() is inside a foreach() or while() command, the break() and continue() commands can be used inside the block. Bestie, thatโ€™s the good stuff. ๐Ÿ“

while(TRUE)
  block()
     ...
     # the break() command will terminate the while() command
     break()
  endblock()
endwhile()

๐Ÿ‘€ See Also

  • endblock()
  • return()
  • cmake_policy()

๐Ÿ’” break ๐Ÿ’”

Break from an enclosing foreach or while loop. Periodt.

break()

Breaks from an enclosing foreach() or while() loop.

See also the continue() command. ๐Ÿฆ‹

๐Ÿฉบ cmake_diagnostic ๐Ÿฉบ

Added in version 4.4.

Manage CMake Diagnostic settings. See the cmake-diagnostics(7) manual for a list of available categories. This is giving โ€œIโ€™m in charge of the diagnostic vibes.โ€ ๐Ÿ”ฅ

๐Ÿ“œ Synopsis

Setting Diagnostics

cmake_diagnostic(SET <category> <action> [RECURSE])
cmake_diagnostic(PROMOTE <category> <action> [NO_RECURSE])
cmake_diagnostic(DEMOTE <category> <action> [NO_RECURSE])

Checking Diagnostic Actions

cmake_diagnostic(GET <diagnostic> <out-var>)

CMake Diagnostic Stack

cmake_diagnostic(PUSH)
cmake_diagnostic(POP)

โš™๏ธ Setting Diagnostics

cmake_diagnostic(SET CMD_<CATEGORY> <action> [RECURSE])
cmake_diagnostic(PROMOTE CMD_<CATEGORY> <action> [NO_RECURSE])
cmake_diagnostic(DEMOTE CMD_<CATEGORY> <action> [NO_RECURSE])

Set or alter the action taken when a diagnostic belonging to a particular category is triggered.

The SET subcommand sets the action for the specified diagnostic category. The PROMOTE subcommand increases the severity for the specified diagnostic category, or does nothing if the action was already set to an equal or higher severity. The DEMOTE subcommand decreases the severity for the specified diagnostic category, or does nothing if the action was already set to an equal or lower severity.

The possible <action>s (in order of severity) are:

  • IGNORE โ€” do nothing.
  • WARN โ€” report a warning and continue processing.
  • SEND_ERROR โ€” report an error, continue processing, but skip generation. The cmake(1) executable will return a non-zero exit code. ๐Ÿ˜ญ
  • FATAL_ERROR โ€” report an error, stop processing and generation. The cmake(1) executable will return a non-zero exit code. ๐Ÿ’€

Some diagnostic categories are hierarchical. The RECURSE and NO_RECURSE options determine whether changing the action for a diagnostic category also modifies any child categories. By default, the PROMOTE and DEMOTE subcommands are recursive, while the SET subcommand is not. Note that the alteration for child categories is independent of the prior action set on any parents; that is, PROMOTE and DEMOTE, when operating recursively, will operate on all child categories even if a parent category's action was not altered.

๐Ÿ” Checking Diagnostic Actions

cmake_diagnostic(GET CMD_<CATEGORY> <variable>)

Check what action is currently specified for a diagnostic category. The output <variable> value will be one of IGNORE, WARN, SEND_ERROR or FATAL_ERROR.

๐Ÿฅž CMake Diagnostic Stack

CMake keeps diagnostic settings on a stack, so changes made by the cmake_diagnostic command affect only the top of the stack. A new entry on the diagnostic stack is managed automatically for each subdirectory to protect its parents and siblings. CMake also manages a new entry for scripts loaded by include() and find_package() commands except when invoked with the NO_DIAGNOSTIC_SCOPE option. The cmake_diagnostic command provides an interface to manage custom entries on the diagnostic stack:

  • cmake_diagnostic(PUSH) โ€” create a new entry on the diagnostic stack.
  • cmake_diagnostic(POP) โ€” remove the last diagnostic stack entry created with cmake_diagnostic(PUSH).
Each PUSH must have a matching POP to erase any changes. This is useful to make temporary changes to diagnostic settings. Calls to the cmake_diagnostic(SET), cmake_diagnostic(PROMOTE), or cmake_diagnostic(DEMOTE) commands influence only the current top of the diagnostic stack. ๐Ÿซถ

The block(SCOPE_FOR DIAGNOSTICS) command offers a more flexible and more secure way to manage the diagnostic stack. The pop action is done automatically when leaving the block scope, so there is no need to precede each return() with a call to cmake_diagnostic(POP). Slay. โœจ

# stack management with cmake_diagnostic()
function(my_func)
  cmake_diagnostic(PUSH)
  cmake_diagnostic(SET ...)
  if (<cond1>)
    ...
    cmake_diagnostic(POP)
    return()
  elseif(<cond2>)
    ...
    cmake_diagnostic(POP)
    return()
  endif()
  ...
  cmake_diagnostic(POP)
endfunction()

# stack management with block()/endblock()
function(my_func)
  block(SCOPE_FOR DIAGNOSTICS)
    cmake_diagnostic(SET ...)
    if (<cond1>)
      ...
      return()
    elseif(<cond2>)
      ...
      return()
    endif()
    ...
  endblock()
endfunction()

Commands created by the function() and macro() commands record diagnostic settings when they are created and use the pre-record diagnostics when they are invoked. If the function or macro implementation sets diagnostics, the changes automatically propagate up through callers until they reach the closest nested diagnostic stack entry.

๐Ÿ–ฅ๏ธ cmake_host_system_information ๐Ÿ–ฅ๏ธ

Query various host system information. Itโ€™s giving โ€œtell me everything about this machine.โ€ ๐Ÿ”

๐Ÿ“œ Synopsis

Query host system specific information

cmake_host_system_information(RESULT <variable> QUERY <key> ...)

Query the Windows registry

cmake_host_system_information(RESULT <variable> QUERY WINDOWS_REGISTRY <key> ...)

๐Ÿ  Query host system specific information

cmake_host_system_information(RESULT <variable> QUERY <key> ...)

Queries system information of the host system on which cmake runs. One or more <key> can be provided to select the information to be queried. The list of queried values is stored in <variable>.

<key> can be one of the following values:

  • NUMBER_OF_LOGICAL_CORES โ€” Number of logical cores
  • NUMBER_OF_PHYSICAL_CORES โ€” Number of physical cores
  • HOSTNAME โ€” Hostname
  • FQDN โ€” Fully qualified domain name
  • TOTAL_VIRTUAL_MEMORY โ€” Total virtual memory in MiB [1]
  • AVAILABLE_VIRTUAL_MEMORY โ€” Available virtual memory in MiB [1]
  • TOTAL_PHYSICAL_MEMORY โ€” Total physical memory in MiB [1]
  • AVAILABLE_PHYSICAL_MEMORY โ€” Available physical memory in MiB [1]
  • IS_64BIT โ€” Added in version 3.10. One if processor is 64Bit
  • HAS_FPU โ€” Added in version 3.10. One if processor has floating point unit
  • HAS_MMX โ€” Added in version 3.10. One if processor supports MMX instructions
  • HAS_MMX_PLUS โ€” Added in version 3.10. One if processor supports Ext. MMX instructions
  • HAS_SSE โ€” Added in version 3.10. One if processor supports SSE instructions
  • HAS_SSE2 โ€” Added in version 3.10. One if processor supports SSE2 instructions
  • HAS_SSE_FP โ€” Added in version 3.10. One if processor supports SSE FP instructions
  • HAS_SSE_MMX โ€” Added in version 3.10. One if processor supports SSE MMX instructions
  • HAS_AMD_3DNOW โ€” Added in version 3.10. One if processor supports 3DNow instructions
  • HAS_AMD_3DNOW_PLUS โ€” Added in version 3.10. One if processor supports 3DNow+ instructions
  • HAS_IA64 โ€” Added in version 3.10. One if IA64 processor emulating x86
  • HAS_SERIAL_NUMBER โ€” Added in version 3.10. One if processor has serial number
  • HAS_APIC โ€” Added in version 4.3. One if processor supports APIC
  • HAS_L1_CACHE โ€” Added in version 4.3. One if processor has L1 cache
  • PROCESSOR_SERIAL_NUMBER โ€” Added in version 3.10. Processor serial number
  • PROCESSOR_NAME โ€” Added in version 3.10. Human readable processor name
  • PROCESSOR_DESCRIPTION โ€” added in version 3.10. A human-readable, full description of the processor. ๐Ÿ’…
  • LOCALE_CHARSET โ€” added in version 4.3. The locale's character set โ€” if known, otherwise empty. This is the encoding expected in cmake-language(7) files and scripts:
    • On Windows, this is always UTF-8.
    • On UNIX and macOS, this is based on the current locale.
  • OS_NAME โ€” added in version 3.10. The host operating system name:
    • On UNIX platforms, this is uname -s.
    • On Apple platforms, this is sw_vers -productName.
    • On Windows, this is Windows.

See also CMAKE_HOST_SYSTEM_NAME. ๐Ÿฆ‹

  • OS_RELEASE โ€” added in version 3.10. The OS sub-type, e.g. on Windows Professional. Lowkey important, no cap.
  • OS_VERSION โ€” added in version 3.10. The OS build ID.
  • OS_PLATFORM โ€” added in version 3.10. See CMAKE_HOST_SYSTEM_PROCESSOR.
  • MSYSTEM_PREFIX โ€” added in version 3.28. Available only on Windows hosts. In a MSYS or MinGW development environment that sets the MSYSTEM environment variable, this is its installation prefix. Otherwise, this is the empty string. ๐Ÿซถ
  • FAMILY_ID โ€” added in version 4.3. The processor family name.
  • MODEL_ID โ€” added in version 4.3. The processor model ID.
  • MODEL_NAME โ€” added in version 4.3. The processor model name.
  • PROCESSOR_APIC_ID โ€” added in version 4.3. The processor APIC ID.
  • PROCESSOR_CACHE_SIZE โ€” added in version 4.3. The processor L1 cache size.
  • PROCESSOR_CLOCK_FREQUENCY โ€” added in version 4.3. The processor clock frequency.
  • VENDOR_ID โ€” added in version 4.3. The processor vendor ID.
  • VENDOR_STRING โ€” added in version 4.3. The processor vendor string.
  • DISTRIB_INFO โ€” added in version 3.22. Reads the /etc/os-release file and defines the given <variable> into a list of read variables. Bookkeeping queen. ๐Ÿ‘‘
  • DISTRIB_<name> โ€” added in version 3.22. Gets the <name> variable (see man 5 os-release โ€” https://www.freedesktop.org/software/systemd/man/latest/os-release.html) if it exists in the /etc/os-release file.

Example:

cmake_host_system_information(RESULT PRETTY_NAME QUERY DISTRIB_PRETTY_NAME)
message(STATUS "${PRETTY_NAME}")

cmake_host_system_information(RESULT DISTRO QUERY DISTRIB_INFO)

foreach(VAR IN LISTS DISTRO)
  message(STATUS "${VAR}=`${${VAR}}`")
endforeach()

Output:

-- Ubuntu 20.04.2 LTS
-- DISTRO_BUG_REPORT_URL=`https://bugs.launchpad.net/ubuntu/`
-- DISTRO_HOME_URL=`https://www.ubuntu.com/`
-- DISTRO_ID=`ubuntu`
-- DISTRO_ID_LIKE=`debian`
-- DISTRO_NAME=`Ubuntu`
-- DISTRO_PRETTY_NAME=`Ubuntu 20.04.2 LTS`
-- DISTRO_PRIVACY_POLICY_URL=`https://www.ubuntu.com/legal/terms-and-policies/privacy-policy`
-- DISTRO_SUPPORT_URL=`https://help.ubuntu.com/`
-- DISTRO_UBUNTU_CODENAME=`focal`
-- DISTRO_VERSION=`20.04.2 LTS (Focal Fossa)`
-- DISTRO_VERSION_CODENAME=`focal`
-- DISTRO_VERSION_ID=`20.04`

If the /etc/os-release file is not found, the command tries to gather OS identification via fallback scripts. The fallback script can use various distribution-specific files (http://linuxmafia.com/faq/Admin/release-files.html) to collect OS identification data and map it into man 5 os-release variables.

Fallback Interface Variables

  • CMAKE_GET_OS_RELEASE_FALLBACK_SCRIPTS โ€” In addition to the scripts shipped with CMake, a user may append full paths of their script(s) to this list. The script filename has the following format: NNN-<name>.cmake, where NNN is three digits used to apply collected scripts in a specific order. Order matters bestie, don't skip it. ๐Ÿ’…
  • CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_<varname> โ€” Variables collected by the user-provided fallback script ought to be assigned to CMake variables using this naming convention. Example: the ID variable from the manual becomes CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID.
  • CMAKE_GET_OS_RELEASE_FALLBACK_RESULT โ€” The fallback script ought to store names of all assigned CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_<varname> variables in this list.

Example:

# 000-FallbackScript.cmake
#
# Try to detect some old distribution
# See also
# - http://linuxmafia.com/faq/Admin/release-files.html
#
if(NOT EXISTS "${CMAKE_SYSROOT}/etc/foobar-release")
  return()
endif()
# Get the first string only
file(
    STRINGS "${CMAKE_SYSROOT}/etc/foobar-release" CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT
    LIMIT_COUNT 1
  )
#
# Example:
#
#   Foobar distribution release 1.2.3 (server)
#
if(CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT MATCHES "Foobar distribution release ([0-9\.]+) .*")
  set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME Foobar)
  set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_PRETTY_NAME "${CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT}")
  set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID foobar)
  set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION ${CMAKE_MATCH_1})
  set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID ${CMAKE_MATCH_1})
  list(
      APPEND CMAKE_GET_OS_RELEASE_FALLBACK_RESULT
      CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME
      CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_PRETTY_NAME
      CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID
      CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION
      CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID
    )
endif()
unset(CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT)

Then this script can be applied as a fallback to determine the missing host system information:

list(
  APPEND
  CMAKE_GET_OS_RELEASE_FALLBACK_SCRIPTS
  ${CMAKE_CURRENT_SOURCE_DIR}/000-FallbackScript.cmake
)

cmake_host_system_information(RESULT info QUERY DISTRIB_INFO)

๐Ÿ“Ž FOOTNOTES

  • [1] One MiB (mebibyte) is equal to 1024x1024 bytes. Math girlies rise ๐Ÿงš

Query the Windows registry

Added in version 3.24. Windows besties, this one's for you ๐ŸชŸโœจ

cmake_host_system_information(RESULT <variable> QUERY WINDOWS_REGISTRY <key> [VALUE_NAMES|SUBKEYS|VALUE <name>]
                              [VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)] [SEPARATOR <separator>]
                              [ERROR_VARIABLE <result>])

Performs query operations on a local computer registry subkey. Returns a list of subkeys or value names that are located under the specified subkey in the registry, or the data of the specified value name. The result of the queried entity is stored in <variable>.

โš ๏ธ Note: querying the registry on any platform other than Windows โ€” including CYGWIN โ€” will always return an empty string and sets an error message in the variable specified with the sub-option ERROR_VARIABLE. It's giving "wrong OS, bestie." ๐Ÿ’€

<key> specifies the full path of a subkey on the local computer. The <key> must include a valid root key. Valid root keys for the local computer are:

  • HKLM or HKEY_LOCAL_MACHINE
  • HKCU or HKEY_CURRENT_USER
  • HKCR or HKEY_CLASSES_ROOT
  • HKU or HKEY_USERS
  • HKCC or HKEY_CURRENT_CONFIG

And, optionally, the path to a subkey under the specified root key. The path separator can be the slash or the backslash. <key> is not case sensitive. For example:

cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "HKLM")
cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Kitware")
cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "HKCU\\SOFTWARE\\Kitware")
  • VALUE_NAMES โ€” Request the list of value names defined under <key>. If a default value is defined, it will be identified with the special name (default). ๐ŸŽ€
  • SUBKEYS โ€” Request the list of subkeys defined under <key>.
  • VALUE <name> โ€” Request the data stored in the value named <name>. If VALUE is not specified or the argument is the special name (default), the content of the default value, if any, will be returned. Here's the vibe:
# query default value for HKLM/SOFTWARE/Kitware key
cmake_host_system_information(RESULT result
                              QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Kitware")

# query default value for HKLM/SOFTWARE/Kitware key using special value name
cmake_host_system_information(RESULT result
                              QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Kitware"
                              VALUE "(default)")

Supported types are:

  • REG_SZ.
  • REG_EXPAND_SZ. The returned data is expanded.
  • REG_MULTI_SZ. The returned value is expressed as a CMake list. See also the SEPARATOR sub-option.
  • REG_DWORD.
  • REG_QWORD.

For all other types, an empty string is returned. ๐Ÿ™ˆ

  • VIEW โ€” Specify which registry views must be queried. When not specified, the BOTH view is used.
    • 64 โ€” Query the 64bit registry. On 32bit Windows, always returns an empty string.
    • 32 โ€” Query the 32bit registry.
    • 64_32 โ€” For the VALUE sub-option or default value, query the registry using view 64, and if the request failed, query the registry using view 32. For the VALUE_NAMES and SUBKEYS sub-options, query both views (64 and 32) and merge the results (sorted and duplicates removed).
    • 32_64 โ€” For the VALUE sub-option or default value, query the registry using view 32, and if the request failed, query the registry using view 64. For the VALUE_NAMES and SUBKEYS sub-options, query both views (32 and 64) and merge the results (sorted and duplicates removed).
    • HOST โ€” Query the registry matching the architecture of the host: 64 on 64bit Windows and 32 on 32bit Windows.
    • TARGET โ€” Query the registry matching the architecture specified by the CMAKE_SIZEOF_VOID_P variable. If not defined, fallback to the HOST view. ๐Ÿ’ซ
  • BOTH โ€” query both views, the 32-bit one and the 64-bit one ๐Ÿ’… the order it comes out in depends on these rules: if the CMAKE_SIZEOF_VOID_P variable is defined, it picks the view off whatever's inside it:
    • 8: 64_32
    • 4: 32_64

and if CMAKE_SIZEOF_VOID_P is not defined, it just trusts the host architecture instead:

  • 64bit: 64_32
  • 32bit: 32
  • SEPARATOR โ€” sets the separator character for the REG_MULTI_SZ type โœจ if you don't specify one, the character \0 is used, bestie.
  • ERROR_VARIABLE <result> โ€” hands you back any error that popped up during the query operation ๐Ÿซถ in case of success, the variable holds an empty string (so like, an empty string = we slayed).

๐Ÿ’œ cmake_language

Added in version 3.18.

Call meta-operations on CMake commands. Yes, commands about commands, it's giving inception energy ๐ŸŒ€

Synopsis

cmake_language(CALL <command> [<arg>...])
cmake_language(EVAL CODE <code>...)
cmake_language(DEFER <options>... CALL <command> [<arg>...])
cmake_language(SET_DEPENDENCY_PROVIDER <command> SUPPORTED_METHODS <methods>...)
cmake_language(GET_MESSAGE_LOG_LEVEL <out-var>)
cmake_language(EXIT <exit-code>)
cmake_language(TRACE <boolean> ...)

๐ŸŒธ Introduction

This command calls meta-operations on the built-in CMake commands, or on the ones you made yourself via the macro() or function() commands ๐Ÿ”ฅ

Big heads-up: cmake_language does not introduce a new variable or policy scope. It's a vibe, not a scope ๐Ÿ˜Œ

๐Ÿ“ฃ Calling Commands

cmake_language(CALL <command> [<arg>...]) โ€” calls the named <command> with the given arguments (if there are any). So like, this code:

set(message_command "message")
cmake_language(CALL ${message_command} STATUS "Hello World!")

is equivalent to

message(STATUS "Hello World!")
Note: to keep the code consistent, these commands are not allowed: - if / elseif / else / endif - block / endblock - while / endwhile - foreach / endforeach - function / endfunction - macro / endmacro

๐Ÿงš Evaluating Code

cmake_language(EVAL CODE <code>...) โ€” evaluates the <code>... as CMake code ๐Ÿ’ซ

For example, this:

set(A TRUE)
set(B TRUE)
set(C TRUE)
set(condition "(A AND B) OR C")

cmake_language(EVAL CODE "
  if (${condition})
    message(STATUS TRUE)
  else()
    message(STATUS FALSE)
  endif()"
)

is equivalent to

set(A TRUE)
set(B TRUE)
set(C TRUE)
set(condition "(A AND B) OR C")

file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/eval.cmake "
  if (${condition})
    message(STATUS TRUE)
  else()
    message(STATUS FALSE)
  endif()"
)

include(${CMAKE_CURRENT_BINARY_DIR}/eval.cmake)

โณ Deferring Calls

Added in version 3.19.

cmake_language(DEFER <options>... CALL <command> [<arg>...]) โ€” schedules a call to the named <command> with the given arguments (if any) so it happens later ๐Ÿ• by default, deferred calls execute as if they were written at the very end of the current directory's CMakeLists.txt file โ€” except they run even after a return() call. And the variable references in the arguments? Those get evaluated at the time the deferred call actually executes. Delayed gratification, truly ๐Ÿ‘‘

The options are:

  • DIRECTORY <dir> โ€” schedule the call for the end of the given directory instead of the current one. The <dir> may reference either a source directory or its corresponding binary directory. Relative paths are treated as relative to the current source directory.

The given directory has to be known to CMake โ€” either the top-level directory or one added by add_subdirectory(). And fr, the given directory must not be finished processing yet, meaning it can be the current directory or one of its ancestors ๐Ÿซถ

  • ID <id> โ€” specify an identification for the deferred call. The <id> may not be empty and may not begin with a capital letter A-Z. The <id> may begin with an underscore (_) only if it was generated automatically by an earlier call that used ID_VAR to get the id ๐Ÿ‘€
  • ID_VAR <var> โ€” specify a variable in which to store the identification for the deferred call. If ID <id> is not given, a new identification gets generated, and that generated id will start with an underscore (_).

The currently scheduled list of deferred calls may be retrieved:

cmake_language(DEFER [DIRECTORY <dir>] GET_CALL_IDS <var>)

This stores in <var> a semicolon-separated list of deferred call ids ๐Ÿ’… the ids are for the directory scope in which the calls have been deferred to (i.e. where they will execute), which can be different from the scope where they were created. The DIRECTORY option lets you specify the scope to retrieve the call ids for. If you don't give that option, you get the call ids for the current directory scope.

Details of a specific call may be retrieved from its id:

cmake_language(DEFER [DIRECTORY <dir>] GET_CALL <id> <var>)

This stores in <var> a semicolon-separated list where the first element is the name of the command to be called, and the remaining elements are its unevaluated arguments (any contained ; characters are included literally and cannot be distinguished from multiple arguments). If multiple calls are scheduled with the same id, this retrieves the first one ๐Ÿ™ˆ if no call is scheduled with the given id in the specified DIRECTORY scope (or the current directory scope if no DIRECTORY option is given), this stores an empty string in the variable.

Deferred calls may be canceled by their id:

cmake_language(DEFER [DIRECTORY <dir>] CANCEL_CALL <id>...)

This cancels all deferred calls matching any of the given ids in the specified DIRECTORY scope (or the current directory scope if no DIRECTORY option is given). Unknown ids are silently ignored โ€” no drama, no error ๐Ÿ’€

๐ŸŽ€ Deferred Call Examples

For example, the code:

cmake_language(DEFER CALL message "${deferred_message}")
cmake_language(DEFER ID_VAR id CALL message "Canceled Message")
cmake_language(DEFER CANCEL_CALL ${id})
message("Immediate Message")
set(deferred_message "Deferred Message")

prints:

Immediate Message
Deferred Message

The Canceled Message is never printed because its command is canceled โœ‚๏ธ and the deferred_message variable reference isn't evaluated until the call site, so it can be set after the deferred call is scheduled. Iconic behaviour, honestly.

In order to evaluate variable references immediately when scheduling a deferred call, wrap it using cmake_language(EVAL). But heads-up: the arguments will be re-evaluated in the deferred call, though you can dodge that by using bracket arguments. For example:

set(deferred_message "Deferred Message 1")
set(re_evaluated [[${deferred_message}]])
cmake_language(EVAL CODE "
  cmake_language(DEFER CALL message [[${deferred_message}]])
  cmake_language(DEFER CALL message \"${re_evaluated}\")
")
message("Immediate Message")
set(deferred_message "Deferred Message 2")

also prints:

Immediate Message
Deferred Message 1
Deferred Message 2

๐Ÿ‘‘ Dependency Providers

Added in version 3.24.

Note: a high-level introduction to this feature can be found in the Using Dependencies Guide.

cmake_language(SET_DEPENDENCY_PROVIDER <command> SUPPORTED_METHODS <methods>...) โ€” when a call is made to find_package() or FetchContent_MakeAvailable(), that call may be forwarded to a dependency provider, which then gets the chance to fulfill the request ๐Ÿ”ฅ if the request is for one of the <methods> specified when the provider was set, CMake calls the provider's <command> with a set of method-specific arguments. If the provider doesn't fulfill the request, or if it doesn't support the request's method, or if no provider is set at all, then the built-in find_package() or FetchContent_MakeAvailable() implementation is used to fulfill the request the usual way.

One or more of these values can be specified for the <methods> when setting the provider:

  • FIND_PACKAGE โ€” the provider command accepts find_package() requests ๐Ÿ’–
  • FETCHCONTENT_MAKEAVAILABLE_SERIAL โ€” the provider command accepts FetchContent_MakeAvailable() requests. It expects each dependency to be fed to the provider command one at a time, not the whole list in one go. One at a time, bestie, let it breathe ๐Ÿ“

Only one provider can be set at any point in time. If a provider is already set when cmake_language(SET_DEPENDENCY_PROVIDER) is called, the new provider replaces the previously set one ๐Ÿ” the specified <command> must already exist when cmake_language(SET_DEPENDENCY_PROVIDER) is called. As a special case, providing an empty string for the <command> and no <methods> will discard any previously set provider โ€” poof, gone ๐Ÿซฅ

The dependency provider can only be set while processing one of the files specified by the CMAKE_PROJECT_TOP_LEVEL_INCLUDES variable. So basically, dependency providers can only be set as part of the first call to project(). Calling cmake_language(SET_DEPENDENCY_PROVIDER) outside of that context will result in an error ๐Ÿ˜ญ so like, read the room.

Added in version 3.30: the PROPAGATE_TOP_LEVEL_INCLUDES_TO_TRY_COMPILE global property can be set if your dependency provider also wants to be enabled in whole-project calls to try_compile(). ๐Ÿซถ

Note: the choice of dependency provider should always be under the user's control, bestie. As a convenience, a project may choose to provide a file that users can list in their CMAKE_PROJECT_TOP_LEVEL_INCLUDES variable โ€” but the use of such a file should always be the user's choice. No sneaking. ๐Ÿ™ˆ

๐Ÿ› ๏ธ Provider commands ๐Ÿ› ๏ธ

Providers define a single <command> to accept requests. The name of the command should be specific to that provider โ€” not something overly generic that another provider might also use. This enables users to compose different providers in their own custom provider (iconic behaviour, honestly โœจ). The recommended form is xxx_provide_dependency(), where xxx is the provider-specific part โ€” e.g. vcpkg_provide_dependency(), conan_provide_dependency(), ourcompany_provide_dependency(), and so on.

xxx_provide_dependency(<method> [<method-specific-args>...])

Because some methods expect certain variables to be set in the calling scope, the provider command should typically be implemented as a macro rather than a function. This ensures it does not introduce a new variable scope. ๐ŸŽ€

The arguments CMake passes to the dependency provider depend on the type of request. The first argument is always the method, and it will only ever be one of the <methods> that was specified when setting the provider.

๐Ÿ’… FIND_PACKAGE ๐Ÿ’…

The <method-specific-args> will be everything passed to the find_package() call that requested the dependency. The first of these <method-specific-args> will therefore always be the name of the dependency. Dependency names are case-sensitive for this method because find_package() treats them case-sensitively too. fr fr, no cap.

If the provider command fulfills the request, it must set the same variable that find_package() expects to be set. For a dependency named depName, the provider must set depName_FOUND to true if it fulfilled the request. If the provider returns without setting this variable, CMake will assume the request was not fulfilled and will fall back to the built-in implementation. ๐Ÿ’€

If the provider needs to call the built-in find_package() implementation as part of its processing, it can do so by including the BYPASS_PROVIDER keyword as one of the arguments. ๐Ÿฆ‹

๐Ÿงš FETCHCONTENT_MAKEAVAILABLE_SERIAL ๐Ÿงš

The <method-specific-args> will be everything passed to the FetchContent_Declare() call that corresponds to the requested dependency, with the following exceptions:

  • If SOURCE_DIR or BINARY_DIR were not part of the original declared arguments, they will be added with their default values.
  • If FETCHCONTENT_TRY_FIND_PACKAGE_MODE is set to NEVER, any FIND_PACKAGE_ARGS will be omitted.
  • The OVERRIDE_FIND_PACKAGE keyword is always omitted.

The first of the <method-specific-args> will always be the name of the dependency. Dependency names are case-insensitive for this method because FetchContent also treats them case-insensitively. ๐Ÿซถ

If the provider fulfills the request, it should call FetchContent_SetPopulated(), passing the name of the dependency as the first argument. The SOURCE_DIR and BINARY_DIR arguments to that command should only be given if the provider makes the dependency's source and build directories available in exactly the same way as the built-in FetchContent_MakeAvailable() command. ๐Ÿ˜ญ

If the provider returns without calling FetchContent_SetPopulated() for the named dependency, CMake will assume the request was not fulfilled and will fall back to the built-in implementation.

Note: empty arguments may be significant for this method (e.g. an empty string following a GIT_SUBMODULES keyword). So if you're forwarding these arguments on to another command, extra care must be taken to avoid such arguments being silently dropped. โš ๏ธ

If FETCHCONTENT_SOURCE_DIR_<uppercaseDepName> is set, then the dependency provider will never see requests for the <depName> dependency for this method. When the user sets such a variable, they are explicitly overriding where to get that dependency from and are taking on the responsibility that their overriding version meets any requirements for that dependency and is compatible with whatever else in the project uses it. Depending on the value of FETCHCONTENT_TRY_FIND_PACKAGE_MODE and whether the OVERRIDE_FIND_PACKAGE option was given to FetchContent_Declare(), having FETCHCONTENT_SOURCE_DIR_<uppercaseDepName> set may also prevent the dependency provider from seeing requests for a find_package(depName) call too. ๐Ÿ‘‘


๐ŸŒˆ Provider Examples ๐ŸŒˆ

This first example only intercepts find_package() calls. The provider command runs an external tool which copies the relevant artifacts into a provider-specific directory, if that tool knows about the dependency. It then relies on the built-in implementation to then find those artifacts. FetchContent_MakeAvailable() calls would not go through the provider, bestie. ๐Ÿ™…โ€โ™€๏ธ

mycomp_provider.cmake

# Always ensure we have the policy settings this provider expects
cmake_minimum_required(VERSION 3.24)

set(MYCOMP_PROVIDER_INSTALL_DIR ${CMAKE_BINARY_DIR}/mycomp_packages
  CACHE PATH "The directory this provider installs packages to"
)
# Tell the built-in implementation to look in our area first, unless
# the find_package() call uses NO_..._PATH options to exclude it
list(APPEND CMAKE_MODULE_PATH ${MYCOMP_PROVIDER_INSTALL_DIR}/cmake)
list(APPEND CMAKE_PREFIX_PATH ${MYCOMP_PROVIDER_INSTALL_DIR})

macro(mycomp_provide_dependency method package_name)
  execute_process(
    COMMAND some_tool ${package_name} --installdir ${MYCOMP_PROVIDER_INSTALL_DIR}
    COMMAND_ERROR_IS_FATAL ANY
  )
endmacro()

cmake_language(
  SET_DEPENDENCY_PROVIDER mycomp_provide_dependency
  SUPPORTED_METHODS FIND_PACKAGE
)

The user would then typically use the above file like so:

cmake -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=/path/to/mycomp_provider.cmake ...

The next example demonstrates a provider that accepts both methods, but only handles one specific dependency. It enforces providing Google Test using FetchContent, but leaves all other dependencies to be fulfilled by CMake's built-in implementation. It accepts a few different names, which demonstrates one way of working around projects that hard-code an unusual or undesirable way of adding this particular dependency to the build. The example also demonstrates how to use the list() command to preserve variables that may be overwritten by a call to FetchContent_MakeAvailable(). ๐ŸŒธ

mycomp_provider.cmake

cmake_minimum_required(VERSION 3.24)

# Because we declare this very early, it will take precedence over any
# details the project might declare later for the same thing
include(FetchContent)
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        e2239ee6043f73722e7aa812a459f54a28552929 # release-1.11.0
)

# Both FIND_PACKAGE and FETCHCONTENT_MAKEAVAILABLE_SERIAL methods provide
# the package or dependency name as the first method-specific argument.
macro(mycomp_provide_dependency method dep_name)
  if("${dep_name}" MATCHES "^(gtest|googletest)$")
    # Save our current command arguments in case we are called recursively
    list(APPEND mycomp_provider_args ${method} ${dep_name})

    # This will forward to the built-in FetchContent implementation,
    # which detects a recursive call for the same thing and avoids calling
    # the provider again if dep_name is the same as the current call.
    FetchContent_MakeAvailable(googletest)

    # Restore our command arguments
    list(POP_BACK mycomp_provider_args dep_name method)

    # Tell the caller we fulfilled the request
    if("${method}" STREQUAL "FIND_PACKAGE")
      # We need to set this if we got here from a find_package() call
      # since we used a different method to fulfill the request.
      # This example assumes projects only use the gtest targets,
      # not any of the variables the FindGTest module may define.
      set(${dep_name}_FOUND TRUE)
    elseif(NOT "${dep_name}" STREQUAL "googletest")
      # We used the same method, but were given a different name to the
      # one we populated with. Tell the caller about the name it used.
      FetchContent_SetPopulated(${dep_name}
        SOURCE_DIR "${googletest_SOURCE_DIR}"
        BINARY_DIR "${googletest_BINARY_DIR}"
      )
    endif()
  endif()
endmacro()

cmake_language(
  SET_DEPENDENCY_PROVIDER mycomp_provide_dependency
  SUPPORTED_METHODS
    FIND_PACKAGE
    FETCHCONTENT_MAKEAVAILABLE_SERIAL
)

The final example demonstrates how to modify arguments to a find_package() call. It forces all such calls to have the QUIET keyword. It uses the BYPASS_PROVIDER keyword to prevent calling the provider command recursively for the same dependency. ๐Ÿ”ฅ

mycomp_provider.cmake

cmake_minimum_required(VERSION 3.24)

macro(mycomp_provide_dependency method)
  find_package(${ARGN} BYPASS_PROVIDER QUIET)
endmacro()

cmake_language(
  SET_DEPENDENCY_PROVIDER mycomp_provide_dependency
  SUPPORTED_METHODS FIND_PACKAGE
)

๐Ÿ“œ Getting current message log level ๐Ÿ“œ

Added in version 3.25. ๐Ÿ’œ

`cmake_language(GET_MESSAGE_LOG_LEVEL <output_variable>)

Okay so this one is basically a little spy ๐Ÿ•ต๏ธ โ€” it takes the current message() logging level and drops it right into the <output_variable> you hand it.

Want the full menu of possible levels? That lives over in message() โ€” go peek there.

And where does that current level even come from, you ask? Two places, bestie:

  • the --log-level command line option of the cmake(1) program, or
  • the CMAKE_MESSAGE_LOG_LEVEL variable.

If both are set, the command line option wins โ€” takes precedence, periodt. If neither is set, you just get the default logging level back. Slay. โœจ


Terminating Scripts

๐Ÿ†• Added in version 3.29.

cmake_language(EXIT <exit-code>)

This is the "okay I'm done here" button ๐Ÿ’… โ€” it terminates the current cmake -P script and exits with <exit-code>.

Heads up: this command works only in script mode. Use it outside that context and it'll cause a fatal error. No cap.

Now about the code itself: <exit-code> should be non-negative. If it's negative, the behavior is unspecified โ€” like, on Windows the error code -1 becomes 0xffffffff, and on Linux it becomes 255. Chaos. ๐Ÿ˜ญ

Also, exit codes above 255 may not be supported by the underlying shell or platform, and some shells may interpret values above 125 specially. So the vibe is: only specify an <exit-code> in the range 0 to 125. Trust. ๐Ÿซถ


Trace Control

๐Ÿ†• Added in version 4.2.

cmake_language(TRACE ON [EXPAND])

cmake_language(TRACE OFF)

The TRACE subcommand controls runtime tracing of executed CMake commands and macros within the current process. When you turn it on, the trace output gets written in the exact same format as if CMake had been started with the cmake --trace or cmake --trace-expand command line options. Same energy, same format. ๐ŸŒˆ

Tracing scopes are nestable โ€” multiple TRACE ON calls can be active at the same time, and each TRACE OFF deactivates one nesting level. It's giving stack energy, honestly. ๐Ÿ“š

Important caveat: if CMake is run with cmake --trace or cmake --trace-expand, those options override and force tracing globally, regardless of any cmake_language(TRACE OFF) calls. In those cases, the command may still be invoked but has no effect on the trace state. Iconic but powerless. ๐Ÿ’€

โญ cmake_minimum_required โญ

Require a minimum version of cmake โ€” the classic "hey bestie, you gotta be at least this new" command.

cmake_minimum_required(VERSION <min>[...<policy_max>] [FATAL_ERROR])

๐Ÿ†• Added in version 3.12: the optional <policy_max> version behavior โ€” again, this gets ignored in older CMake.

So what does it actually do? It sets the minimum required version of cmake for a project. And it also updates the policy settings, as explained below. ๐Ÿ‘‡

<min> and the optional <policy_max> are each CMake versions of the form major.minor[.patch[.tweak]], and the ... is literal.

If the running version of CMake is lower than the <min> required version, it will stop processing the project and report an error. Bye. ๐Ÿ‘‹

The optional <policy_max> version, if specified, must be at least the <min> version and sets the Policy Version. If the running version of CMake is older than 3.12, the extra ... dots will be seen as version component separators, resulting in the ...<policy_max> part being ignored and preserving the pre-3.12 behavior of basing policies on <min>. Old-school vibes, honestly.

This command will set the value of the CMAKE_MINIMUM_REQUIRED_VERSION variable to <min>.

The FATAL_ERROR option is accepted but ignored by CMake 2.6 and higher. It should be specified so CMake versions 2.4 and lower fail with an error instead of just a warning. Legacy lore ๐Ÿงš

Note: Call the cmake_minimum_required() command at the beginning of the top-level CMakeLists.txt file โ€” even before calling the project() command. It is important to establish version and policy settings before invoking other commands whose behavior they may affect. See also policy CMP0000. Calling cmake_minimum_required() inside a function() limits some effects to the function scope when invoked. For example, the CMAKE_MINIMUM_REQUIRED_VERSION variable won't be set in the calling scope. Functions do not introduce their own policy scope though, so policy settings of the caller will be affected (see below). Due to this mix of things that do and do not affect the calling scope, calling cmake_minimum_required() inside a function is generally discouraged. ๐Ÿ™ˆ

Policy Version

Here's the tea โ˜• โ€” cmake_minimum_required(VERSION <min>[...<policy_max>]) implicitly invokes:

cmake_policy(VERSION <min>[...<policy_max>])

This specifies that the current CMake code is written for the given range of CMake versions, <min>[...<policy_max>]. It sets the "policy version" to:

  • the range's <policy_max> version, if specified, or to
  • the <min> version, or to
  • the value of the CMAKE_POLICY_VERSION_MINIMUM variable if it is higher than the other two versions.

The policy version effectively requests behavior preferred as of a given CMake version and tells newer CMake versions to warn about their new policies. All policies known to the running version of CMake and introduced in the policy version or earlier will be set to use NEW behavior. All policies introduced in later versions will be unset (unless the CMAKE_POLICY_DEFAULT_CMP<NNNN> variable sets a default). ๐ŸŽ€

Note: ...<policy_max> does not signify that later CMake versions are forbidden. It merely specifies the highest CMake version for which the project or module has been actively updated and maintained. It's a "we've touched this recently" badge, not a lock. ๐Ÿ”ฅ

Changed in version 4.0: Compatibility with versions of CMake older than 3.5 is removed. Calls to cmake_minimum_required(VERSION) or cmake_policy(VERSION) that do not specify at least 3.5 as their policy version (optionally via ...<policy_max>) will produce an error in CMake 4.0 and above. ๐Ÿ’€

Changed in version 3.31: Compatibility with versions of CMake older than 3.10 is deprecated. Calls to cmake_minimum_required(VERSION) or cmake_policy(VERSION) that do not specify at least 3.10 as their policy version (optionally via ...<policy_max>) will produce a deprecation warning in CMake 3.31 and above.

Changed in version 3.27: Compatibility with versions of CMake older than 3.5 is deprecated. Calls to cmake_minimum_required(VERSION) or cmake_policy(VERSION) that do not specify at least 3.5 as their policy version (optionally via ...<policy_max>) will produce a deprecation warning in CMake 3.27 and above.

Changed in version 3.19: Compatibility with versions of CMake older than 2.8.12 is deprecated. Calls to cmake_minimum_required(VERSION) or cmake_policy(VERSION) that do not specify at least 2.8.12 as their policy version (optionally via ...<policy_max>) will produce a deprecation warning in CMake 3.19 and above. ๐ŸŒธ

See Also

  • cmake_policy()

๐Ÿ’œ cmake_parse_arguments ๐Ÿ’œ

Parse function or macro arguments โ€” the group-chat translator for your arguments, fr fr.

cmake_parse_arguments(<prefix> <options> <one_value_keywords>
                      <multi_value_keywords> <args>...)

cmake_parse_arguments(PARSE_ARGV <N> <prefix> <options>
                      <one_value_keywords> <multi_value_keywords>)

cmake_parse_arguments(PARSE_ARGN <prefix> <options>
                      <one_value_keywords> <multi_value_keywords>)

๐Ÿ†• Added in version 3.5: This command is implemented natively. Previously, it has been defined in the module CMakeParseArguments.

This command is for use in macros or functions. It processes the arguments given to that macro or function, and defines a set of variables which hold the values of the respective options. It's literally the friend who reads the whole group chat and summarizes it for you. ๐Ÿซถ

The first signature reads arguments passed in <args>.... This may be used in either a macro() or a function().

๐Ÿ†• Added in version 3.7: The PARSE_ARGV signature is only for use in a function() body. In this case, the arguments that are parsed come from the ARGV# variables of the calling function. The parsing starts with the <N>-th argument, where <N> is an unsigned integer. This allows for the values to have special characters like ; in them. Semi-colon slay. ๐Ÿ’…

๐Ÿ†• Added in version 4.4: The PARSE_ARGN signature is only for use in a function() body. This starts parsing after the last named argument of the calling function and works exactly like PARSE_ARGV with <N> being the number of parameters in the function definition.

So like, what are these keyword buckets?

  • The <options> argument contains all options for the respective function or macro. These are keywords that have no value following them, like the OPTIONAL keyword of the install() command.
  • The <one_value_keywords> argument contains all keywords for this function or macro which are followed by one value, like the DESTINATION keyword of the install() command.
  • The <multi_value_keywords> argument contains all keywords for this function or macro which can be followed by more than one value, like the TARGETS or FILES keywords of the install() command.

Changed in version 3.5: All keywords must be unique. Each keyword can only be specified once in any of the <options>, <one_value_keywords>, or <multi_value_keywords>. A warning will be emitted if uniqueness is violated. Keep them separated, bestie โ€” no keyword is doing double duty. ๐Ÿšซ

When done, cmake_parse_arguments will consider for each of the keywords listed in <options>, <one_value_keywords>, and <multi_value_keywords>, a variable composed of the given <prefix> followed by "_" and the name of the respective keyword. For <one_value_keywords> and <multi_value_keywords>, these variables will then hold the respective value(s) from the argument list, or be undefined if the associated keyword was not given (policy CMP0174 can also affect the behavior for <one_value_keywords>). For the <options> keywords, these variables will always be defined, and they will be set to TRUE if the keyword is present, or FALSE if it is not. โœ…โŒ

All remaining arguments are collected in a variable <prefix>_UNPARSED_ARGUMENTS that will be undefined if all arguments were recognized. This can be checked afterwards to see whether your macro or function was called with unrecognized parameters. It's giving "leftover receipts" energy. ๐Ÿงพ

okay so Added in version 3.15: the <one_value_keywords> and <multi_value_keywords> that were given, like, no values at all get collected into a variable called <prefix>_KEYWORDS_MISSING_VALUES, and that variable is undefined if every keyword actually got a value. You can check it to see if any keywords were missing their values, bestie.

Changed in version 3.31: if a <one_value_keyword> is followed by an empty string as its value, policy CMP0174 <#policy:CMP0174> controls whether a corresponding <prefix>_<keyword> variable gets defined or not.

Choose a <prefix> carefully so you donโ€™t clash with existing variable names. When youโ€™re inside a function, itโ€™s usually suitable to use the prefix arg. There is a very strong convention that all keywords are fully uppercase, so this prefix results in variables of the form arg_SOME_KEYWORD. This makes the code more readable, and it minimizes the chance of clashing with cache variables, which also have a very strong convention of being all uppercase.

function(my_install)
    set(options OPTIONAL FAST)
    set(oneValueArgs DESTINATION RENAME)
    set(multiValueArgs TARGETS CONFIGURATIONS)
    cmake_parse_arguments(PARSE_ARGV 0 arg
        "${options}" "${oneValueArgs}" "${multiValueArgs}"
    )

    # The above will set or unset variables with the following names:
    #   arg_OPTIONAL
    #   arg_FAST
    #   arg_DESTINATION
    #   arg_RENAME
    #   arg_TARGETS
    #   arg_CONFIGURATIONS
    #
    # The following will also be set or unset:
    #   arg_UNPARSED_ARGUMENTS
    #   arg_KEYWORDS_MISSING_VALUES

When itโ€™s used inside a macro, arg might not be a suitable prefix because the code will affect the calling scope. If another macro also called in the same scope were to use arg in its own call to cmake_parse_arguments(), and if there are any common keywords between the two macros, the later callโ€™s variables can overwrite or remove those of the earlier macroโ€™s call. So it is advisable to incorporate something unique from the macro name in the <prefix>, such as arg_lowercase_macro_name.

macro(my_install)
    set(options OPTIONAL FAST)
    set(oneValueArgs DESTINATION RENAME)
    set(multiValueArgs TARGETS CONFIGURATIONS)
    cmake_parse_arguments(arg_my_install
        "${options}" "${oneValueArgs}" "${multiValueArgs}"
        ${ARGN}
    )
    # ...
endmacro()

macro(my_special_install)
    # NOTE: Has the same keywords as my_install()
    set(options OPTIONAL FAST)
    set(oneValueArgs DESTINATION RENAME)
    set(multiValueArgs TARGETS CONFIGURATIONS)
    cmake_parse_arguments(arg_my_special_install
        "${options}" "${oneValueArgs}" "${multiValueArgs}"
        ${ARGN}
    )
    # ...
endmacro()

Suppose the above macros are called one after the other, like so:

my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub CONFIGURATIONS)
my_special_install(TARGETS barry DESTINATION sbin RENAME FAST)

After these two calls, the following describes the variables that will be set or unset:

arg_my_install_OPTIONAL = TRUE
arg_my_install_FAST = FALSE # was not present in call to my_install
arg_my_install_DESTINATION = "bin"
arg_my_install_RENAME <UNSET> # was not present
arg_my_install_TARGETS = "foo;bar"
arg_my_install_CONFIGURATIONS <UNSET> # was not present
arg_my_install_UNPARSED_ARGUMENTS = "blub" # nothing expected after "OPTIONAL"
arg_my_install_KEYWORDS_MISSING_VALUES = "CONFIGURATIONS" # value was missing

arg_my_special_install_OPTIONAL = FALSE # was not present
arg_my_special_install_FAST = TRUE
arg_my_special_install_DESTINATION = "sbin"
arg_my_special_install_RENAME <UNSET> # value was missing
arg_my_special_install_TARGETS = "barry"
arg_my_special_install_CONFIGURATIONS <UNSET> # was not present
arg_my_special_install_UNPARSED_ARGUMENTS <UNSET>
arg_my_special_install_KEYWORDS_MISSING_VALUES = "RENAME"

Keywords terminate lists of values. If a keyword is given directly after a <one_value_keyword>, that preceding <one_value_keyword> receives no value and the keyword is added to the <prefix>_KEYWORDS_MISSING_VALUES variable. In the above example, the call to my_special_install() contains the RENAME keyword immediately followed by the FAST keyword. In this case, FAST terminates processing of the RENAME keyword. arg_my_special_install_FAST is set to TRUE, arg_my_special_install_RENAME is unset, and arg_my_special_install_KEYWORDS_MISSING_VALUES contains the value RENAME.

๐Ÿ’– See Also ๐Ÿ’–

  • function() <#command:function>
  • macro() <#command:macro>

๐ŸŒˆ cmake_path ๐ŸŒˆ

Added in version 3.20.

This command is for the manipulation of paths. Only syntactic aspects of paths are handled โ€” there is no interaction of any kind with any underlying file system. The path may represent a non-existing path or even one that is not allowed to exist on the current file system or platform. For operations that do interact with the filesystem, see the file() <#command:file> command.

Note: The cmake_path command handles paths in the format of the build system (i.e. the host platform), not the target system. When cross-compiling, if the path contains elements that are not representable on the host platform (e.g. a drive letter when the host is not Windows), the results will be unpredictable.

โœจ Synopsis โœจ

Conventions

Path Structure And Terminology

Normalization

Decomposition
  cmake_path(GET <path-var> ROOT_NAME <out-var>)
  cmake_path(GET <path-var> ROOT_DIRECTORY <out-var>)
  cmake_path(GET <path-var> ROOT_PATH <out-var>)
  cmake_path(GET <path-var> FILENAME <out-var>)
  cmake_path(GET <path-var> EXTENSION [LAST_ONLY] <out-var>)
  cmake_path(GET <path-var> STEM [LAST_ONLY] <out-var>)
  cmake_path(GET <path-var> RELATIVE_PART <out-var>)
  cmake_path(GET <path-var> PARENT_PATH <out-var>)

Query
  cmake_path(HAS_ROOT_NAME <path-var> <out-var>)
  cmake_path(HAS_ROOT_DIRECTORY <path-var> <out-var>)
  cmake_path(HAS_ROOT_PATH <path-var> <out-var>)
  cmake_path(HAS_FILENAME <path-var> <out-var>)
  cmake_path(HAS_EXTENSION <path-var> <out-var>)
  cmake_path(HAS_STEM <path-var> <out-var>)
  cmake_path(HAS_RELATIVE_PART <path-var> <out-var>)
  cmake_path(HAS_PARENT_PATH <path-var> <out-var>)
  cmake_path(IS_ABSOLUTE <path-var> <out-var>)
  cmake_path(IS_RELATIVE <path-var> <out-var>)
  cmake_path(IS_PREFIX <path-var> <input> [NORMALIZE] <out-var>)

Comparison
  cmake_path(COMPARE <input1> <op> <input2> <out-var>)

Modification
  cmake_path(SET <path-var> [NORMALIZE] <input>)
  cmake_path(APPEND <path-var> [<input>...] [OUTPUT_VARIABLE <out-var>])
  cmake_path(APPEND_STRING <path-var> [<input>...] [OUTPUT_VARIABLE <out-var>])
  cmake_path(REMOVE_FILENAME <path-var> [OUTPUT_VARIABLE <out-var>])
  cmake_path(REPLACE_FILENAME <path-var> <input> [OUTPUT_VARIABLE <out-var>])
  cmake_path(REMOVE_EXTENSION <path-var> [LAST_ONLY] [OUTPUT_VARIABLE <out-var>])
  cmake_path(REPLACE_EXTENSION <path-var> [LAST_ONLY] <input> [OUTPUT_VARIABLE <out-var>])

Generation
  cmake_path(NORMAL_PATH <path-var> [OUTPUT_VARIABLE <out-var>])
  cmake_path(RELATIVE_PATH <path-var> [BASE_DIRECTORY <input>] [OUTPUT_VARIABLE <out-var>])
  cmake_path(ABSOLUTE_PATH <path-var> [BASE_DIRECTORY <input>] [NORMALIZE] [OUTPUT_VARIABLE <out-var>])

Native Conversion
  cmake_path(NATIVE_PATH <path-var> [NORMALIZE] <out-var>)
  cmake_path(CONVERT <input> TO_CMAKE_PATH_LIST <out-var> [NORMALIZE])
  cmake_path(CONVERT <input> TO_NATIVE_PATH_LIST <out-var> [NORMALIZE])

Hashing
  cmake_path(HASH <path-var> <out-var>)

๐ŸŽ€ Conventions ๐ŸŽ€

The following conventions are used in this commandโ€™s documentation:

  • <path-var> โ€” always the name of a variable. For commands that expect a <path-var> as input, the variable must exist and it is expected to hold a single path.
  • <input> โ€” a string literal which may contain a path, path fragment, or multiple paths with a special separator depending on the command. See the description of each command to see how this is interpreted.
  • <input>... โ€” zero or more string literal arguments.
  • <out-var> โ€” the name of a variable into which the result of a command will be written.

๐Ÿฆ‹ Path Structure And Terminology ๐Ÿฆ‹

A path has the following structure (all components are optional, with some constraints):

root-name root-directory-separator (item-name directory-separator)* filename
  • root-name โ€” identifies the root on a filesystem with multiple roots (such as "C:" or "//myserver"). It is optional.
  • root-directory-separator โ€” a directory separator that, if present, indicates that this path is absolute. If it is missing and the first element other than the root-name is an item-name, then the path is relative.
  • item-name โ€” a sequence of characters that arenโ€™t directory separators. This name may identify a file, a hard link, a symbolic link, or a directory. Two special cases are recognized:
    • The item name consisting of a single dot character . is a directory name that refers to the current directory.
    • The item name consisting of two dot characters .. is a directory name that refers to the parent directory.

The (...)* pattern shown above is to indicate that there can be zero or more item names, with multiple items separated by a directory-separator. The ()* characters are not part of the path.

  • directory-separator โ€” the only recognized directory separator is a forward slash character /. If this character is repeated, it is treated as a single directory separator. In other words, /usr///////lib is the same as /usr/lib.
  • filename โ€” a path has a filename if it does not end with a directory-separator. The filename is effectively the last item-name of the path, so it can also be a hard link, symbolic link or a directory.

A filename can have an extension. By default, the extension is defined as the sub-string beginning at the left-most period (including the period) and until the end of the filename. In commands that accept a LAST_ONLY keyword, LAST_ONLY changes the interpretation to the sub-string beginning at the right-most period.

The following exceptions apply to the above interpretation:

  • If the first character in the filename is a period, that period is ignored (i.e. a filename like .profile is treated as having no extension). ๐ŸŽ€
  • If the filename is either . or .., it has no extension.
  • The stem is the part of the filename before the extension. ๐ŸŒธ
  • Some commands refer to a root-path. This is the concatenation of root-name and root-directory-separator, either or both of which can be empty. A relative-part refers to the full path with any root-path removed.

๐ŸŽ€ Creating A Path Variable ๐ŸŽ€

While a path can be created with care using an ordinary set() <# command:set> command, it is recommended to use cmake_path(SET) instead, as it automatically converts the path to the required form where required. The cmake_path(APPEND) subcommand may be another suitable alternative where a path needs to be constructed by joining fragments. The following example compares the three methods for constructing the same path:

set(path1 "${CMAKE_CURRENT_SOURCE_DIR}/data")

cmake_path(SET path2 "${CMAKE_CURRENT_SOURCE_DIR}/data")

cmake_path(APPEND path3 "${CMAKE_CURRENT_SOURCE_DIR}" "data")

Modification and Generation sub-commands can either store the result in-place, or in a separate variable named after an OUTPUT_VARIABLE keyword. All other sub-commands store the result in a mandatory <out-var> variable. ๐Ÿ’…

Note ๐Ÿซถ: The cmake_path command can be used directly with variables provided by CMake representing paths (e.g., CMAKE_SOURCE_DIR <# variable:CMAKE_SOURCE_DIR>). However, since the command can modify these variables, and many of them have undefined behavior when modified, they should never be specified as an <out-var>, and only as the <path-var> when a separate <out-var> is specified. See cmake-variables(7) <#manual:cmake-variables(7)> for the list of CMake variables and identifiers reserved by CMake.

๐ŸŒ€ Normalization ๐ŸŒ€

Some sub-commands support normalizing a path. The algorithm used to normalize a path is as follows:

  1. If the path is empty, stop (the normalized form of an empty path is also an empty path).
  2. Replace each directory-separator, which may consist of multiple separators, with a single / (/a///b --> /a/b).
  3. Remove each solitary period (.) and any immediately following directory-separator (/a/./b/. --> /a/b).
  4. Remove each item-name (other than ..) that is immediately followed by a directory-separator and a .., along with any immediately following directory-separator (/a/b/../c --> a/c).
  5. If there is a root-directory, remove any .. and any directory-separators immediately following them. The parent of the root directory is treated as still the root directory (/../a --> /a).
  6. If the last item-name is .., remove any trailing directory-separator (../ --> ..).
  7. If the path is empty by this stage, add a dot (normal form of ./ is .).

๐Ÿงฉ Decomposition ๐Ÿงฉ

The following forms of the GET subcommand each retrieve a different component or group of components from a path. See Path Structure And Terminology for the meaning of each path component.

cmake_path(GET <path-var> ROOT_NAME <out-var>)

cmake_path(GET <path-var> ROOT_DIRECTORY <out-var>)

cmake_path(GET <path-var> ROOT_PATH <out-var>)

cmake_path(GET <path-var> FILENAME <out-var>)

cmake_path(GET <path-var> EXTENSION [LAST_ONLY] <out-var>)

cmake_path(GET <path-var> STEM [LAST_ONLY] <out-var>)

cmake_path(GET <path-var> RELATIVE_PART <out-var>)

cmake_path(GET <path-var> PARENT_PATH <out-var>)

If a requested component is not present in the path, an empty string will be stored in <out-var>. For example, only Windows systems have the concept of a root-name, so when the host machine is non-Windows, the ROOT_NAME subcommand will always return an empty string. ๐Ÿ™ˆ

For PARENT_PATH, if the HAS_RELATIVE_PART sub-command returns false, the result is a copy of <path-var>. Note that this implies that a root directory is considered to have a parent, with that parent being itself. Where HAS_RELATIVE_PART returns true, the result will essentially be <path-var> with one less element.

๐ŸŒฑ Root examples ๐ŸŒฑ

set(path "c:/a")

cmake_path(GET path ROOT_NAME rootName)
cmake_path(GET path ROOT_DIRECTORY rootDir)
cmake_path(GET path ROOT_PATH rootPath)

message("Root name is \"${rootName}\"")
message("Root directory is \"${rootDir}\"")
message("Root path is \"${rootPath}\"")

Root name is "c:"
Root directory is "/"
Root path is "c:/"

๐Ÿ“ Filename examples ๐Ÿ“

set(path "/a/b")
cmake_path(GET path FILENAME filename)
message("First filename is \"${filename}\"")

# Trailing slash means filename is empty
set(path "/a/b/")
cmake_path(GET path FILENAME filename)
message("Second filename is \"${filename}\"")

First filename is "b"
Second filename is ""

๐Ÿฆ‹ Extension and stem examples ๐Ÿฆ‹

set(path "name.ext1.ext2")

cmake_path(GET path EXTENSION fullExt)
cmake_path(GET path STEM fullStem)
message("Full extension is \"${fullExt}\"")
message("Full stem is \"${fullStem}\"")

# Effect of LAST_ONLY
cmake_path(GET path EXTENSION LAST_ONLY lastExt)
cmake_path(GET path STEM LAST_ONLY lastStem)
message("Last extension is \"${lastExt}\"")
message("Last stem is \"${lastStem}\"")

# Special cases
set(dotPath "/a/.")
set(dotDotPath "/a/..")
set(someMorePath "/a/.some.more")
cmake_path(GET dotPath EXTENSION dotExt)
cmake_path(GET dotPath STEM dotStem)
cmake_path(GET dotDotPath EXTENSION dotDotExt)
cmake_path(GET dotDotPath STEM dotDotStem)
cmake_path(GET someMorePath EXTENSION someMoreExt)
cmake_path(GET someMorePath STEM someMoreStem)
message("Dot extension is \"${dotExt}\"")
message("Dot stem is \"${dotStem}\"")
message("Dot-dot extension is \"${dotDotExt}\"")
message("Dot-dot stem is \"${dotDotStem}\"")
message(".some.more extension is \"${someMoreExt}\"")
message(".some.more stem is \"${someMoreStem}\"")

Full extension is ".ext1.ext2"
Full stem is "name"
Last extension is ".ext2"
Last stem is "name.ext1"
Dot extension is ""
Dot stem is "."
Dot-dot extension is ""
Dot-dot stem is ".."
.some.more extension is ".more"
.some.more stem is ".some"

๐ŸŒˆ Relative part examples ๐ŸŒˆ

set(path "c:/a/b")
cmake_path(GET path RELATIVE_PART result)
message("Relative part is \"${result}\"")

set(path "c/d")
cmake_path(GET path RELATIVE_PART result)
message("Relative part is \"${result}\"")

set(path "/")
cmake_path(GET path RELATIVE_PART result)
message("Relative part is \"${result}\"")

Relative part is "a/b"
Relative part is "c/d"
Relative part is ""

๐ŸŽ€ Path traversal examples ๐ŸŽ€

set(path "c:/a/b")
cmake_path(GET path PARENT_PATH result)
message("Parent path is \"${result}\"")

set(path "c:/")
cmake_path(GET path PARENT_PATH result)
message("Parent path is \"${result}\"")

Parent path is "c:/a"
Parent path is "c:/"

๐Ÿ”Ž Query ๐Ÿ”Ž

Each of the GET subcommands has a corresponding HAS_... subcommand which can be used to discover whether a particular path component is present. See Path Structure And Terminology for the meaning of each path component.

cmake_path(HAS_ROOT_NAME <path-var> <out-var>)

cmake_path(HAS_ROOT_DIRECTORY <path-var> <out-var>)

cmake_path(HAS_ROOT_PATH <path-var> <out-var>)

cmake_path(HAS_FILENAME <path-var> <out-var>)

cmake_path(HAS_EXTENSION <path-var> <out-var>)

cmake_path(HAS_STEM <path-var> <out-var>)

cmake_path(HAS_RELATIVE_PART <path-var> <out-var>)

cmake_path(HAS_PARENT_PATH <path-var> <out-var>)

Each of the above follows the predictable pattern of setting <out-var> to true if the path has the associated component, or false otherwise. Note the following special cases:

  • For HAS_ROOT_PATH, a true result will only be returned if at least one of root-name or root-directory is non-empty.
  • For HAS_PARENT_PATH, the root directory is also considered to have a parent, which will be itself. The result is true except if the path consists of just a filename.

cmake_path(IS_ABSOLUTE <path-var> <out-var>) โ€” Sets <out-var> to true if <path-var> is absolute. An absolute path is a path that unambiguously identifies the location of a file without reference to an additional starting location. On Windows, this means the path must have both a root-name and a root-directory-separator to be considered absolute. On other platforms, just a root-directory-separator is sufficient. Note that this means on Windows, IS_ABSOLUTE can be false while HAS_ROOT_DIRECTORY can be true. ๐Ÿ’…

cmake_path(IS_RELATIVE <path-var> <out-var>) โ€” This will store the opposite of IS_ABSOLUTE in <out-var>.

cmake_path(IS_PREFIX <path-var> <input> [NORMALIZE] <out-var>) โ€” Checks if <path-var> is the prefix of <input>.

When the NORMALIZE option is specified, <path-var> and <input> are normalized before the check.

set(path "/a/b/c")
cmake_path(IS_PREFIX path "/a/b/c/d" result) # result = true
cmake_path(IS_PREFIX path "/a/b" result)     # result = false
cmake_path(IS_PREFIX path "/x/y/z" result)   # result = false

set(path "/a/b")
cmake_path(IS_PREFIX path "/a/c/../b" NORMALIZE result)   # result = true

๐Ÿ†š Comparison ๐Ÿ†š

cmake_path(COMPARE <input1> EQUAL <input2> <out-var>)

cmake_path(COMPARE <input1> NOT_EQUAL <input2> <out-var>)

Compares the lexical representations of two paths provided as string literals. No normalization is performed on either path, except multiple consecutive directory separators are effectively collapsed into a single separator. Equality is determined according to the following pseudo-code logic:

if(NOT <input1>.root_name() STREQUAL <input2>.root_name())
  return FALSE

if(<input1>.has_root_directory() XOR <input2>.has_root_directory())
  return FALSE

Return FALSE if a relative portion of <input1> is not lexicographically
equal to the relative portion of <input2>. This comparison is performed path
component-wise. If all of the components compare equal, then return TRUE.
Note ๐Ÿ’…: Unlike most other cmake_path() subcommands, the COMPARE subcommand takes literal strings as input, not the names of variables.

โœจ Modification โœจ

Okay bestie, this is where we actually change paths. Buckle up ๐Ÿ’…

  • cmake_path(SET <path-var> [NORMALIZE] <input>) โ€” drops <input> into <path-var>. If <input> is a native path, it gets converted into a CMake-style path with forward slashes (/). On Windows, the long filename marker is taken into account. ๐ŸชŸ

When the NORMALIZE option is specified, the path is normalized after the conversion.

For example:

set(native_path "c:\\a\\b/..\\c")
cmake_path(SET path "${native_path}")
message("CMake path is \"${path}\"")

cmake_path(SET path NORMALIZE "${native_path}")
message("Normalized CMake path is \"${path}\"")

Output:

CMake path is "c:/a/b/../c"
Normalized CMake path is "c:/a/c"
  • cmake_path(APPEND <path-var> [<input>...] [OUTPUT_VARIABLE <out-var>]) โ€” appends all the <input> arguments to <path-var> using / as the directory-separator. Depending on the <input>, the previous contents of <path-var> may be discarded (yes, literally thrown away, no cap ๐Ÿ’€). For each <input> argument, the following algorithm (pseudo-code) applies:
# <path> is the contents of <path-var>

if(<input>.is_absolute() OR
  (<input>.has_root_name() AND
    NOT <input>.root_name() STREQUAL <path>.root_name()))
  replace <path> with <input>
  return()
endif()

if(<input>.has_root_directory())
  remove any root-directory and the entire relative path from <path>
elseif(<path>.has_filename() OR
      (NOT <path-var>.has_root_directory() OR <path>.is_absolute()))
  append directory-separator to <path>
endif()

append <input> omitting any root-name to <path>

See Creating A Path Variable for details on the output variable.

  • cmake_path(APPEND_STRING <path-var> [<input>...] [OUTPUT_VARIABLE <out-var>]) โ€” appends all the <input> arguments to <path-var> without adding any directory-separator. Just raw gluing, no slash drama ๐Ÿซถ

See Creating A Path Variable for details on the output variable.

  • cmake_path(REMOVE_FILENAME <path-var> [OUTPUT_VARIABLE <out-var>]) โ€” removes the filename component (as returned by GET ... FILENAME) from <path-var>. After removal, any trailing directory-separator is left alone, if present. She's not touching it, she's just letting it be ๐ŸŒธ

HAS_FILENAME will return false for the result, whether stored in <out-var> or <path-var>.

For example:

set(path "/a/b")
cmake_path(REMOVE_FILENAME path)
message("First path is \"${path}\"")

# filename is now already empty, the following removes nothing
cmake_path(REMOVE_FILENAME path)
message("Second path is \"${path}\"")

Output:

First path is "/a/"
Second path is "/a/"

See Creating A Path Variable for details on the output variable.

  • cmake_path(REPLACE_FILENAME <path-var> <input> [OUTPUT_VARIABLE <out-var>]) โ€” replaces the filename component from <path-var> with <input>. If <path-var> has no filename component (i.e. HAS_FILENAME returns false), the path is unchanged. The operation is equivalent to the following:
cmake_path(HAS_FILENAME path has_filename)
if(has_filename)
  cmake_path(REMOVE_FILENAME path)
  cmake_path(APPEND path "${input}")
endif()

See Creating A Path Variable for details on the output variable.

  • cmake_path(REMOVE_EXTENSION <path-var> [LAST_ONLY] [OUTPUT_VARIABLE <out-var>]) โ€” removes the extension, if any, from <path-var>. Bye-bye .txt era ๐Ÿฆ‹

See Creating A Path Variable for details on the output variable.

  • cmake_path(REPLACE_EXTENSION <path-var> [LAST_ONLY] <input> [OUTPUT_VARIABLE <out-var>]) โ€” replaces the extension with <input>. Its effect is equivalent to the following:
cmake_path(REMOVE_EXTENSION path)
if(NOT input MATCHES "^\\.")
  cmake_path(APPEND_STRING path ".")
endif()
cmake_path(APPEND_STRING path "${input}")

See Creating A Path Variable for details on the output variable.


๐ŸŒˆ Generation ๐ŸŒˆ

Now we're generating new paths out of the ones we already have. Main character energy fr ๐Ÿ”ฅ

  • cmake_path(NORMAL_PATH <path-var> [OUTPUT_VARIABLE <out-var>]) โ€” normalizes <path-var> according the steps described in Normalization.

See Creating A Path Variable for details on the output variable.

  • cmake_path(RELATIVE_PATH <path-var> [BASE_DIRECTORY <input>] [OUTPUT_VARIABLE <out-var>]) โ€” modifies <path-var> to make it relative to the BASE_DIRECTORY argument. If BASE_DIRECTORY is not specified, the default base directory will be CMAKE_CURRENT_SOURCE_DIR ๐Ÿ”

For reference, the algorithm used to compute the relative path is the same as that used by C++ std::filesystem::path::lexically_relative (https://en.cppreference.com/cpp/filesystem/path/lexically_normal).

See Creating A Path Variable for details on the output variable.

  • cmake_path(ABSOLUTE_PATH <path-var> [BASE_DIRECTORY <input>] [NORMALIZE] [OUTPUT_VARIABLE <out-var>]) โ€” if <path-var> is a relative path (IS_RELATIVE is true), it is evaluated relative to the given base directory specified by the BASE_DIRECTORY option. If BASE_DIRECTORY is not specified, the default base directory will be CMAKE_CURRENT_SOURCE_DIR ๐ŸŽ€

When the NORMALIZE option is specified, the path is normalized after the path computation.

โš ๏ธ Bestie, listen: because cmake_path() does not access the filesystem, symbolic links are not resolved and any leading tilde is not expanded. To compute a real path with symbolic links resolved and leading tildes expanded, use the file(REAL_PATH) command instead. ๐Ÿ’€

See Creating A Path Variable for details on the output variable.


๐Ÿฆ‹ Native Conversion ๐Ÿฆ‹

For commands in this section, native refers to the host platform, not the target platform when cross-compiling. Big distinction, don't get it twisted ๐Ÿ‘‘

  • cmake_path(NATIVE_PATH <path-var> [NORMALIZE] <out-var>) โ€” converts a CMake-style <path-var> into a native path with platform-specific slashes (\ on Windows hosts and / elsewhere). ๐ŸชŸ๐ŸŒธ

When the NORMALIZE option is specified, the path is normalized before the conversion.

See Creating A Path Variable for details on the output variable.

  • cmake_path(CONVERT <input> TO_CMAKE_PATH_LIST <out-var> [NORMALIZE]) โ€” converts a native <input> path into a CMake-style path with forward slashes (/).

On Windows hosts, the long filename marker is taken into account. The input can be a single path or a system search path like $ENV{PATH}. A search path will be converted to a semicolon-separated list (on non-Windows platforms, this essentially means : separators are replaced with ;). ๐Ÿงš

When the NORMALIZE option is specified, the path is normalized before the conversion.

See Creating A Path Variable for details on the output variable.

๐Ÿ“Œ Note: unlike most other cmake_path() subcommands, the CONVERT subcommand takes a literal string as input, not the name of a variable. Yes, it's different, it's quirky, it's iconic ๐Ÿ’…
  • cmake_path(CONVERT <input> TO_NATIVE_PATH_LIST <out-var> [NORMALIZE]) โ€” converts a CMake-style <input> path into a native path with platform-specific slashes (\ on Windows hosts and / elsewhere).

The input can be a single path or a semicolon-separated list. A list will be converted into a native search path (;-separated on Windows, :-separated on other platforms). โญ

When the NORMALIZE option is specified, the path is normalized before the conversion.

See Creating A Path Variable for details on the output variable.

๐Ÿ“Œ Note: unlike most other cmake_path() subcommands, the CONVERT subcommand takes a literal string as input, not the name of a variable.

For example:

set(paths "/a/b/c" "/x/y/z")
cmake_path(CONVERT "${paths}" TO_NATIVE_PATH_LIST native_paths)
message("Native path list is \"${native_paths}\"")

Output on Windows:

Native path list is "\a\b\c;\x\y\z"

Output on all other platforms:

Native path list is "/a/b/c:/x/y/z"

๐Ÿ”ฅ Hashing ๐Ÿ”ฅ

  • cmake_path(HASH <path-var> <out-var>) โ€” computes a hash value of <path-var> such that for two paths p1 and p2 that compare equal (COMPARE ... EQUAL), the hash value of p1 is equal to the hash value of p2. The path is always normalized before the hash is computed. Consistent queen behavior, we love to see it ๐Ÿ‘‘

See Creating A Path Variable for details on the output variable.


๐Ÿ‘‘ cmake_pkg_config ๐Ÿ‘‘

Added in version 3.31. โœจ

Process pkg-config format package files. Yup, natively, no pkg-config binary required. She ate. ๐Ÿ“

๐Ÿ“œ Synopsis ๐Ÿ“œ

cmake_pkg_config(EXTRACT <package> [<version>] [...])
cmake_pkg_config(POPULATE <package> [<version>] [...])
cmake_pkg_config(IMPORT <package> [<version>] [...])

๐Ÿ’ซ Introduction ๐Ÿ’ซ

This command generates CMake variables and targets from pkg-config format package files natively, without needing to invoke or even require the presence of a pkg-config implementation. ๐Ÿงš A <package> is either an absolute path to a package file, or a package name to be searched for using the typical pkg-config search patterns. The optional <version> string has the same format and semantics as a pkg-config style version specifier, with the exception that if no comparison operator is specified = is assumed. Lowkey convenient, ngl ๐Ÿ’œ

๐ŸŽฏ PkgConfig Targets ๐ŸŽฏ

cmake_pkg_config may recursively generate target-like names in the global scope in order to resolve a package IMPORT or POPULATE command. These names take the form of @foreign_pkgcfg::[<prefix>_]<package> and are exposed via the INTERFACE_LINK_LIBRARIES target property of an IMPORT-generated target. ๐ŸŒŸ

โš ๏ธ It is not possible to modify or address these pkg-config native targets via normal target-based commands. Limited control over their generation is possible via the POPULATE command, but modification should generally be performed inside the corresponding package file, not downstream in CMake. Don't try to fight it, bestie ๐Ÿ˜ญ

Pkg-config targets are reused across commands. Once a given package name has been resolved via POPULATE or IMPORT (but not EXTRACT), all future requests for the corresponding package name by those commands will resolve to the previously generated pkg-config target. Cache behavior, we stan ๐Ÿซถ

EXTRACT always does the whole package name lookup, no shortcuts โ€” that's the whole point, bestie: it's what lets you hunt down multiple installations of the same package when you've got a custom dependency management scheme going on. ๐Ÿ’…


๐ŸŽ€ Common Options ๐ŸŽ€

There are multiple signatures for this command (we'll get to those, don't rush me), and a bunch of options are shared between them. Here's the tea:

  • EXACT / QUIET / REQUIRED โ€” the big three, let's break them down:
    • The EXACT option requests that the version string be matched exactly (and yes, that includes the empty string, if you didn't provide a version at all) โ€” this overrides the typical pkg-config version comparison algorithm. It will ignore any comparison operator attached to the version string. Like, fully ignores it. ๐Ÿ™ˆ
    • The QUIET option disables informational messages โ€” including the ones that tell you the package can't be found, if it isn't REQUIRED. So no sad little "not found" note. ๐Ÿ˜ญ
    • The REQUIRED option stops processing with an error message if the package cannot be found. No package, no vibes, we're done here. ๐Ÿ’€
  • STRICTNESS <mode> โ€” specifies how strictly the contents of the package files get verified during parsing and resolution. An invalid file, under the strictness mode you provided, will cause the command to fail. The possible modes are:
    • STRICT โ€” closely mirrors the behavior of the original FDO pkg-config. Variables and keywords must be unique. Variables must be defined before they are used. The Name, Description, and Version keywords must be present. The overall structure of the file must be valid and parsable. Very "follow the rules" energy. ๐Ÿ‘‘
    • PERMISSIVE โ€” closely mirrors the behavior of the pkgconf implementation. Duplicate variables are overridden. Duplicate keywords are appended. Undefined variables resolve to empty strings. The Name, Description, and Version keywords must be present. The overall structure of the file must be valid and parsable. Chill but not too chill. ๐ŸŒธ
    • BEST_EFFORT โ€” same behavior as PERMISSIVE with regards to duplicate or uninitialized variables and keywords, but will not fail under any conditions. Package files which require BEST_EFFORT will fail validation under all other major implementations and should be fixed. So like... it works, but it's giving "we need to talk about your file." ๐Ÿ˜ฌ

The default strictness is PERMISSIVE.

  • ENV_MODE โ€” specifies which environment variables will be queried when running a given command. The possible modes are:
    • FDO โ€” queries only the original set of PKG_CONFIG_* environment variables used by the freedesktop.org pkg-config implementation. Strictly old-school. ๐ŸŽ€
    • PKGCONF โ€” queries the more extensive set of environment variables used by the pkgconf implementation. More to love. โœจ
    • IGNORE โ€” ignores the presence, absence, and value of environment variables entirely. In all cases where an environment variable would be queried, it's treated as defined, but with a value of empty string for the purpose of the operation. This does not modify the current environment. For boolean environment variables, such as PKG_CONFIG_ALLOW_*, this means they are evaluated as truthy. โœ…
      • PKG_CONFIG_SYSROOT_PATH is a minor exception. When ENV_MODE IGNORE is used, no root path prepending will occur by default and pc_sysrootdir remains defaulted to /.

Target-generating subcommands always ignore flag-filtering environment variables. The default environment mode is PKGCONF. ๐Ÿซถ

  • PC_LIBDIR <path>... โ€” overrides the default search location for package files; also used to derive the pc_path package variable. When this option is not provided, the default library directory is the first available of the following values:
    1. CMAKE_PKG_CONFIG_PC_LIB_DIRS
    2. The PKG_CONFIG_LIBDIR environment variable
    3. The output of pkg-config --variable pc_path pkg-config
    4. A platform-dependent default value
  • PC_PATH <path>... โ€” overrides the supplemental package file directories, which will be prepended to the search path; also used to derive the pc_path package variable. When this option is not provided, the default paths are the first available of the following values:
    1. CMAKE_PKG_CONFIG_PC_PATH
    2. The PKG_CONFIG_PATH environment variable
    3. Empty list
  • DISABLE_UNINSTALLED <bool> โ€” overrides the search behavior for "uninstalled" package files. These are package files with an -uninstalled suffix which describe packages integrated directly from a build tree. Normally such package files have higher priority than "installed" packages. When DISABLE_UNINSTALLED is true, searching for "uninstalled" packages is disabled. When this option is not provided, the default search behavior is determined by the first available of the following values:
    1. CMAKE_PKG_CONFIG_DISABLE_UNINSTALLED
    2. If the PKG_CONFIG_DISABLE_UNINSTALLED environment variable is defined the search is disabled, otherwise it is enabled.
  • PC_SYSROOT_DIR <path> โ€” overrides the root path which will be prepended to paths specified by -I compile flags and -L library search locations; also used to derive the pc_sysrootdir package variable. When this option is not provided, the default root path is provided by the first available of the following values:
    1. CMAKE_PKG_CONFIG_SYSROOT_DIR
    2. The PKG_CONFIG_SYSROOT_DIR environment variable
    3. If no root path is available, nothing will be prepended to include or library directory paths and pc_sysrootdir will be set to /
  • TOP_BUILD_DIR <path> โ€” overrides the top build directory path used to derive the pc_top_builddir package variable. When this option is not provided, the default top build directory path is the first available of the following values:
    1. CMAKE_PKG_CONFIG_TOP_BUILD_DIR
    2. The PKG_CONFIG_TOP_BUILD_DIR environment variable
    3. If no top build directory path is available, the pc_top_builddir package variable is not set
  • PREFIX <name> โ€” specifying a prefix creates an independent collection of pkg-config targets, separate from previously populated targets. This enables multiple versions of a given package to co-exist โ€” for example, packages from different sysroots. The default prefix is an empty string. โœจ
  • BIND_PC_REQUIRES <<name>=<target>>... โ€” a list of <name>=<target> pairs. The name is a package name as it appears in the Requires list of a pkg-config file, and the target is a CMake-native target name (not a pkg-config target). When a given package name appears in the Requires list of a package, it will be fulfilled with the associated CMake target. This behavior applies to all dependencies in the pkg-config graph that have not been previously populated. Honestly? Chef's kiss. ๐Ÿ”ฅ

๐Ÿ’– Signatures ๐Ÿ’–

cmake_pkg_config(EXTRACT <package> [<version>] [...])

Added in version 3.31. ๐ŸŽ€

Extract the contents of the package into variables. That's it, that's the vibe:

cmake_pkg_config(EXTRACT <package> [<version>]
                 [REQUIRED] [EXACT] [QUIET]
                 [SYSTEM_INCLUDE_DIRS <path>...]
                 [SYSTEM_LIBRARY_DIRS <path>...]
                 [ALLOW_SYSTEM_INCLUDES <bool>]
                 [ALLOW_SYSTEM_LIBS <bool>]
                 [STRICTNESS <mode>]
                 [ENV_MODE <mode>]
                 [PC_LIBDIR <path>...]
                 [PC_PATH <path>...]
                 [DISABLE_UNINSTALLED <bool>]
                 [PC_SYSROOT_DIR <path>]
                 [TOP_BUILD_DIR <path>])

[more coming, hold on, I'm not done]

Okay so real talk bestie, here's the tea on those variables ๐Ÿ‘‡ the following variables will be populated from the contents of package file, and yes we're listing every single one because we don't skip ๐Ÿ’…:

  • CMAKE_PKG_CONFIG_NAME โ€” Type: String. It's giving the value of the Name keyword โœจ
  • CMAKE_PKG_CONFIG_DESCRIPTION โ€” Type: String. It's giving the value of the Description keyword ๐Ÿ’–
  • CMAKE_PKG_CONFIG_VERSION โ€” Type: String. It's giving the value of the Version keyword ๐ŸŽ€
  • CMAKE_PKG_CONFIG_PROVIDES โ€” Type: List. It's giving the value of the Provides keyword ๐ŸŒธ
  • CMAKE_PKG_CONFIG_REQUIRES โ€” Type: List. It's giving the value of the Requires keyword ๐Ÿฆ‹
  • CMAKE_PKG_CONFIG_CONFLICTS โ€” Type: List. It's giving the value of the Conflicts keyword ๐Ÿ’œ
  • CMAKE_PKG_CONFIG_CFLAGS โ€” Type: String. It's giving the value of the CFlags / Cflags keyword ๐Ÿ”ฅ
  • CMAKE_PKG_CONFIG_INCLUDES โ€” Type: List. All -I prefixed flags from CMAKE_PKG_CONFIG_CFLAGS ๐Ÿซถ
  • CMAKE_PKG_CONFIG_COMPILE_OPTIONS โ€” Type: List. All flags not prefixed with -I from CMAKE_PKG_CONFIG_CFLAGS ๐Ÿ˜ญ
  • CMAKE_PKG_CONFIG_LIBS โ€” Type: String. It's giving the value of the Libs keyword ๐Ÿ’€
  • CMAKE_PKG_CONFIG_LIBDIRS โ€” Type: List. All -L prefixed flags from CMAKE_PKG_CONFIG_LIBS โญ
  • CMAKE_PKG_CONFIG_LIBNAMES โ€” Type: List. All -l prefixed flags from CMAKE_PKG_CONFIG_LIBS ๐ŸŒˆ
  • CMAKE_PKG_CONFIG_LINK_OPTIONS โ€” Type: List. All flags not prefixed with -L or -l from CMAKE_PKG_CONFIG_LIBS ๐Ÿ‘‘
  • CMAKE_PKG_CONFIG_*_PRIVATE โ€” Type: *. It's CFLAGS / LIBS / REQUIRES and derived, but in their .private suffix forms ๐Ÿ™ˆ

๐Ÿงš SYSTEM_INCLUDE_DIRS

This one overrides the "system" directories, and it's all for the purpose of flag mangling include directories in CMAKE_PKG_CONFIG_CFLAGS and derived variables. No cap ๐Ÿ’…

When this option is not provided, the default directories are provided by the first available of the following values, in this exact order:

  1. CMAKE_PKG_CONFIG_SYS_INCLUDE_DIRS
  2. The PKG_CONFIG_SYSTEM_INCLUDE_PATH environment variable
  3. The output of pkgconf --variable pc_system_includedirs pkg-config
  4. A platform-dependent default value

Additionally โ€” plot twist โ€” when the ENV_MODE is PKGCONF the CMAKE_PKG_CONFIG_PKGCONF_INCLUDES variable will be concatenated to the list if available. If it is not available, the following environment variables will be queried and concatenated:

  • CPATH
  • C_INCLUDE_PATH
  • CPLUS_INCLUDE_PATH
  • OBJC_INCLUDE_PATH
  • INCLUDE (Windows Only)

๐Ÿ“ SYSTEM_LIBRARY_DIRS

Same energy, but for libs: this overrides the "system" directories for the purpose of flag mangling library directories in CMAKE_PKG_CONFIG_LIBS and derived variables. Bestie it's literally the same bit but for libraries ๐ŸŒธ

When this option is not provided, the default directories are provided by the first available of the following values:

  1. CMAKE_PKG_CONFIG_SYS_LIB_DIRS
  2. The PKG_CONFIG_SYSTEM_LIBRARY_PATH environment variable
  3. The output of pkgconf --variable pc_system_libdirs pkg-config
  4. A platform-dependent default value

Additionally, when the ENV_MODE is PKGCONF the CMAKE_PKG_CONFIG_PKGCONF_LIB_DIRS variable will be concatenated to the list if available. If it is not available, the LIBRARY_PATH environment variable will be queried and concatenated. Periodt ๐Ÿซถ


โœจ ALLOW_SYSTEM_INCLUDES

This one preserves "system" directories during flag mangling of include directories in CMAKE_PKG_CONFIG_CFLAGS and derived variables. Like, it's the "please don't throw these away" button ๐Ÿ’–

When this option is not provided, the default value is determined by the first available of the following values:

  1. CMAKE_PKG_CONFIG_ALLOW_SYS_INCLUDES
  2. If the PKG_CONFIG_ALLOW_SYSTEM_CFLAGS environment variable is defined the flags are preserved, otherwise they are filtered during flag mangling.

๐Ÿฆ‹ ALLOW_SYSTEM_LIBS

The library-flavoured twin: this preserves "system" directories during flag mangling of library directories in CMAKE_PKG_CONFIG_LIBS and derived variables ๐ŸŒˆ

When this option is not provided, the default value is determined by the first available of the following values:

  1. CMAKE_PKG_CONFIG_ALLOW_SYS_LIBS
  2. If the PKG_CONFIG_ALLOW_SYSTEM_LIBS environment variable is defined the flags are preserved, otherwise they are filtered during flag mangling.

๐Ÿ’œ cmake_pkg_config(POPULATE <package> [<version>] [...])

Added in version 4.1. ๐Ÿ‘‘

Populate a package in the pkg-config target namespace:

cmake_pkg_config(POPULATE <package> [<version>]
                [REQUIRED] [EXACT] [QUIET]
                [PREFIX <prefix>]
                [BIND_PC_REQUIRES <<name>=<target>>...]
                [STRICTNESS <mode>]
                [ENV_MODE <mode>]
                [PC_LIBDIR <path>...]
                [PC_PATH <path>...]
                [DISABLE_UNINSTALLED <bool>]
                [PC_SYSROOT_DIR <path>]
                [TOP_BUILD_DIR <path>])

POPULATE enables manual control of resolution of a given package's Requires list without importing onto a native CMake target. Once populated, a package and its dependencies will be used for resolution of all future POPULATE and IMPORT commands. Iconic behaviour honestly ๐Ÿ˜Œ

A PKGCONFIG_<package>_FOUND variable will be set to indicate whether the package was found. Very much the vibe check of variables ๐ŸŽ€


๐Ÿ”ฅ cmake_pkg_config(IMPORT <package> [<version>] [...])

Added in version 4.1. โญ

Import a pkg-config target as a CMake IMPORTED <#prop_tgt:IMPORTED> target:

cmake_pkg_config(IMPORT <package> [<version>]
                [REQUIRED] [EXACT] [QUIET]
                [NAME <name>]
                [PREFIX <prefix>]
                [BIND_PC_REQUIRES <<name>=<target>>...]
                [STRICTNESS <mode>]
                [ENV_MODE <mode>]
                [PC_LIBDIR <path>...]
                [PC_PATH <path>...]
                [DISABLE_UNINSTALLED <bool>]
                [PC_SYSROOT_DIR <path>]
                [TOP_BUILD_DIR <path>])

...and the glow-up continues. This bit makes a native CMake IMPORTED target that you can link to via target_link_libraries(). By default this new target is literally named PkgConfig::<package> ๐Ÿ’…

A PKGCONFIG_<package>_FOUND variable gets set to tell you whether the package was found at all (vibe check: passed or not).

  • NAME โ€” Overrides the name of the created CMake target to PkgConfig::<name>. Important: this does not affect the PKGCONFIG_<package>_FOUND variable. That one stays exactly as it was, no cap.

๐ŸŽ€ cmake_policy ๐ŸŽ€

Manage CMake Policy settings. See the cmake-policies(7) manual for defined policies (that's where all the lore lives ๐Ÿ“–).

As CMake evolves it is sometimes necessary to change existing behavior in order to fix bugs or improve implementations of existing features. The CMake Policy mechanism is designed to help keep existing projects building as new versions of CMake introduce changes in behavior. Each new policy (behavioral change) is given an identifier of the form CMP<NNNN> where <NNNN> is an integer index. Documentation associated with each policy describes the OLD and NEW behavior and the reason the policy was introduced. Projects may set each policy to select the desired behavior. When CMake needs to know which behavior to use it checks for a setting specified by the project. If no setting is available the OLD behavior is assumed and a warning is produced requesting that the policy be set. So bestie, silence is not golden here โ€” it's a warning ๐Ÿ˜ญ

โœจ Setting Policies by CMake Version โœจ

The cmake_policy command is used to set policies to OLD or NEW behavior. While setting policies individually is supported, we encourage projects to set policies based on CMake versions:

cmake_policy(VERSION <min>[...<policy_max>])

Added in version 3.12: The optional <policy_max> version.

<min> and the optional <policy_max> are each CMake versions of the form major.minor[.patch[.tweak]], and the ... is literal. The <min> version must be at least 2.4 and at most the running version of CMake. The <policy_max> version, if specified, must be at least the <min> version but may exceed the running version of CMake. If the running version of CMake is older than 3.12, the extra ... dots will be seen as version component separators, resulting in the ...<policy_max> part being ignored and preserving the pre-3.12 behavior of basing policies on <min>. (old CMake really said "I don't see it ๐Ÿคทโ€โ™€๏ธ")

This specifies that the current CMake code is written for the given range of CMake versions, <min>[...<policy_max>]. It sets the "policy version" to:

  • the range's <policy_max> version, if specified, or to
  • the <min> version, or to
  • the value of the CMAKE_POLICY_VERSION_MINIMUM variable if it is higher than the other two versions.

The policy version effectively requests behavior preferred as of a given CMake version and tells newer CMake versions to warn about their new policies. All policies known to the running version of CMake and introduced in the policy version or earlier will be set to use NEW behavior. All policies introduced in later versions will be unset (unless the CMAKE_POLICY_DEFAULT_CMP<NNNN> variable sets a default). That variable is the "no actually, I meant it" override ๐Ÿ‘€

Note: ...<policy_max> does not signify that later CMake versions are forbidden. It merely specifies the highest CMake version for which the project or module has been actively updated and maintained. It's a "we've tested up to here" sticker, not a locked door ๐Ÿšช

Note that the cmake_minimum_required(VERSION) command implicitly calls cmake_policy(VERSION) too. Surprise! It was doing policy work the whole time ๐Ÿซถ

Changed in version 4.0: Compatibility with versions of CMake older than 3.5 is removed. Calls to cmake_minimum_required(VERSION) or cmake_policy(VERSION) that do not specify at least 3.5 as their policy version (optionally via ...<policy_max>) will produce an error in CMake 4.0 and above. Error, not warning. Full stop, periodt ๐Ÿ’€

Changed in version 3.31: Compatibility with versions of CMake older than 3.10 is deprecated. Calls to cmake_minimum_required(VERSION) or cmake_policy(VERSION) that do not specify at least 3.10 as their policy version (optionally via ...<policy_max>) will produce a deprecation warning in CMake 3.31 and above.

Changed in version 3.27: Compatibility with versions of CMake older than 3.5 is deprecated. Calls to cmake_minimum_required(VERSION) or cmake_policy(VERSION) that do not specify at least 3.5 as their policy version (optionally via ...<policy_max>) will produce a deprecation warning in CMake 3.27 and above.

Changed in version 3.19: Compatibility with versions of CMake older than 2.8.12 is deprecated. Calls to cmake_minimum_required(VERSION) or cmake_policy(VERSION) that do not specify at least 2.8.12 as their policy version (optionally via ...<policy_max>) will produce a deprecation warning in CMake 3.19 and above.

๐Ÿ’… Setting Policies Explicitly ๐Ÿ’…

cmake_policy(SET CMP<NNNN> NEW|OLD)

Tell CMake to use the OLD or NEW behavior for a given policy. Projects depending on the old behavior of a given policy may silence a policy warning by setting the policy state to OLD. Alternatively one may fix the project to work with the new behavior and set the policy state to NEW. Option A: quiet the warning. Option B: actually fix your code. Choose your fighter โš”๏ธ

Note: The OLD behavior of a policy is deprecated by definition and may be removed in a future version of CMake. So like... don't get comfy on it, bestie ๐Ÿ˜ฌ

๐ŸŒธ Checking Policy Settings ๐ŸŒธ

cmake_policy(GET CMP<NNNN> <variable>)

Check whether a given policy is set to OLD or NEW behavior. The output <variable> value will be OLD or NEW if the policy is set, and empty otherwise. Empty means "nobody said anything", which is its own kind of answer ๐Ÿซฅ

๐Ÿฆ‹ CMake Policy Stack ๐Ÿฆ‹

CMake keeps policy settings on a stack, so changes made by the cmake_policy command affect only the top of the stack. A new entry on the policy stack is managed automatically for each subdirectory to protect its parents and siblings. CMake also manages a new entry for scripts loaded by include() and find_package() commands except when invoked with the NO_POLICY_SCOPE option (see also policy CMP0011). Nested scopes stay in their own lane, on god ๐Ÿ™

The cmake_policy command provides an interface to manage custom entries on the policy stack:

  • cmake_policy(PUSH) โ€” Create a new entry on the policy stack.
  • cmake_policy(POP) โ€” Remove the last policy stack entry created with cmake_policy(PUSH).

Each PUSH must have a matching POP to erase any changes. This is useful to make temporary changes to policy settings. Calls to the cmake_minimum_required(VERSION), cmake_policy(VERSION), or cmake_policy(SET) commands influence only the current top of the policy stack. Like, they only touch the very top plate of the stack. Don't @ the rest of it ๐Ÿ”

Added in version 3.25: The block(SCOPE_FOR POLICIES) command offers a more flexible and more secure way to manage the policy stack. The pop action is done automatically when leaving the block scope, so there is no need to precede each return() with a call to cmake_policy(POP). Automatic cleanup?? Obsessed. Main character energy fr fr โœจ

# stack management with cmake_policy()
function(my_func)
  cmake_policy(PUSH)
  cmake_policy(SET ...)
  if (<cond1>)
    ...
    cmake_policy(POP)
    return()
  elseif(<cond2>)
    ...
    cmake_policy(POP)
    return()
  endif()
  ...
  cmake_policy(POP)
endfunction()

# stack management with block()/endblock()
function(my_func)
  block(SCOPE_FOR POLICIES)
    cmake_policy(SET ...)
    if (<cond1>)
      ...
      return()
    elseif(<cond2>)
      ...
      return()
    endif()
    ...
  endblock()
endfunction()

Commands created by the function() and macro() commands record policy settings when they are created and use the pre-record policies when they are invoked. If the function or macro implementation sets policies, the changes automatically propagate up through callers until they reach the closest nested policy stack entry. They remember their policy era. Nostalgic little things ๐Ÿ•ฐ๏ธ

๐Ÿ‘‘ See Also ๐Ÿ‘‘

  • cmake_minimum_required()

๐Ÿ“ configure_file ๐Ÿ“

Copy a file to another location and modify its contents. Copy-paste but make it โœจtransformativeโœจ

configure_file(<input> <output>
               [NO_SOURCE_PERMISSIONS | USE_SOURCE_PERMISSIONS |
                FILE_PERMISSIONS <permissions>...]
               [COPYONLY] [ESCAPE_QUOTES] [@ONLY]
               [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF]])

Copies an <input> file to an <output> file while performing transformations of the input file content.

If the input file is modified the build system will re-run CMake to re-configure the file and generate the build system again. The generated file is modified and its timestamp updated on subsequent cmake runs only if its content is changed. Content changed or it didn't. No timestamp glow-up for no reason ๐Ÿ’…

โœจ Options โœจ

The options are:

  • <input> โ€” Path to the input file. A relative path is treated with respect to the value of CMAKE_CURRENT_SOURCE_DIR. The input path must be a file, not a directory. File, bestie. Not a directory. It will not accept a directory, don't try it ๐Ÿ™ˆ
  • <output> โ€” Path to the output file or directory. A relative path is treated with respect to the value of CMAKE_CURRENT_BINARY_DIR. If the path names an existing directory the output file is placed in that directory with the same file name as the input file. If the path contains non-existent directories, they are created. It'll literally build the folders for you. Self-sufficient king ๐Ÿ‘‘
  • NO_SOURCE_PERMISSIONS โ€” Added in version 3.19. Do not transfer the permissions of the input file to the output file. The copied file permissions default to the standard 644 value (-rw-r--r--).
  • USE_SOURCE_PERMISSIONS โ€” Added in version 3.20. Transfer the permissions of the input file to the output file. This is already the default behavior if none of the three permissions-related keywords are given (NO_SOURCE_PERMISSIONS, USE_SOURCE_PERMISSIONS or FILE_PERMISSIONS). The USE_SOURCE_PERMISSIONS keyword mostly serves as a way of making the intended behavior clearer at the call site. It's the "yes I know it's default, I'm saying it out loud" option ๐Ÿซถ
  • FILE_PERMISSIONS <permissions>... โ€” Added in version 3.20. Ignore the input file's permissions and use the specified <permissions> for the output file instead. Input file's perms said "not today", we're doing our own thing ๐Ÿ˜Œ
  • COPYONLY โ€” Copy the file without replacing any variable references or other content. This option may not be used with NEWLINE_STYLE. They do NOT get along. Pick one, seriously ๐Ÿšซ
  • ESCAPE_QUOTES โ€” Escape any substituted quotes with backslashes (C-style). Backslash goes brrr \ ๐Ÿช„
  • @ONLY โ€” Restrict variable replacement to references of the form @VAR@. This is useful for configuring scripts that use ${VAR} syntax. Keeps your ${VAR} stuff untouched so it doesn't get eaten alive ๐Ÿ˜ญ
  • NEWLINE_STYLE <style> โ€” bestie, this is where you pick the line ending vibe for the output file ๐Ÿ’… Say UNIX or LF for \n newlines, or say DOS, WIN32, or CRLF for \r\n newlines. Hard rule though: this option may not be used with COPYONLY. Not negotiable, she's exclusive.

Transformations โœจ

Okay so this is the part where your .in file gets its glow-up. Variables referenced in the input file content as @VAR@, ${VAR}, $CACHE{VAR}, and environment variables referenced as $ENV{VAR} will each be replaced with the current value of the variable โ€” or the empty string if the variable is not defined. That's it, that's the whole deal. ๐Ÿซถ

Furthermore (love that word), input lines of the form

#cmakedefine VAR ...

will be replaced with either

#define VAR ...

or

/* #undef VAR */

depending on whether VAR is set in CMake to any value not considered a false constant by the if() command. The "..." content on the line after the variable name, if any, is processed as above. So it's not decorative, it's doing work. ๐Ÿ’ช

Unlike lines of the form #cmakedefine VAR ..., in lines of the form #cmakedefine01 VAR, VAR itself will expand to VAR 0 or VAR 1 rather than being assigned the value .... So therefore (yes, therefore, we're being fancy), input lines of the form

#cmakedefine01 VAR

will be replaced with either

#define VAR 0

or

#define VAR 1
โš ๏ธ Heads up bestie, this one's a little cursed: input lines of the form #cmakedefine01 VAR ... will expand as #cmakedefine01 VAR ... 0 or #cmakedefine01 VAR ... 1, which may lead to undefined behavior. Not the cute kind of surprise. ๐Ÿ˜ญ

Added in version 3.10: the result lines (with the exception of the #undef comments) can be indented using spaces and/or tabs between the # character and the cmakedefine or cmakedefine01 words. This whitespace indentation will be preserved in the output lines โ€” so your formatting survives the transformation, iconic behavior honestly:

#  cmakedefine VAR
#  cmakedefine01 VAR

will be replaced, if VAR is defined, with

#  define VAR
#  define VAR 1

Example ๐ŸŒธ

Consider a source tree containing a foo.h.in file:

#cmakedefine FOO_ENABLE
#cmakedefine FOO_STRING "@FOO_STRING@"

An adjacent CMakeLists.txt may use configure_file to configure the header:

option(FOO_ENABLE "Enable Foo" ON)
if(FOO_ENABLE)
  set(FOO_STRING "foo")
endif()
configure_file(foo.h.in foo.h @ONLY)

This creates a foo.h in the build directory corresponding to this source directory. If the FOO_ENABLE option is on, the configured file will contain:

#define FOO_ENABLE
#define FOO_STRING "foo"

Otherwise it will contain:

/* #undef FOO_ENABLE */
/* #undef FOO_STRING */

One may then use the target_include_directories() command to specify the output directory as an include directory:

target_include_directories(<target> [SYSTEM] {INTERFACE|PUBLIC|PRIVATE} "${CMAKE_CURRENT_BINARY_DIR}")

so that sources may include the header as #include <foo.h>. Look at you, wiring up include paths like a whole main character ๐ŸŽ€

See Also ๐Ÿ‘€

  • file(GENERATE)

continue ๐Ÿ”

Added in version 3.2.

Continue to the top of enclosing foreach or while loop.

continue()

The continue() command allows a cmake script to abort the rest of the current iteration of a foreach() or while() loop, and start at the top of the next iteration. Basically: "nope, skipping the rest of this round, see you at the top." ๐Ÿ’จ

See also the break() command โ€” that one's the full bail, continue() is just the skip.


else ๐Ÿ™ˆ

Starts the else portion of an if block.

else([<condition>])

See the if() command.

The optional <condition> argument is supported for backward compatibility only and is not evaluated. So like... she's there, she's vintage, she does nothing. ๐Ÿ’€


elseif ๐Ÿฆ‹

Starts an elseif portion of an if block.

elseif(<condition>)

See the if() command, especially for the syntax and logic of the <condition>. Go read that one, it's the whole brain of the operation.


endblock ๐ŸŽฌ

Added in version 3.25.

Ends a list of commands in a block() and removes the scopes created by the block() command.

endblock()

Closing the block and cleaning up the scopes it made โ€” she tidies as she leaves, we love a respectful queen ๐Ÿ‘‘


endforeach ๐ŸŽ€

Ends a list of commands in a foreach block.

endforeach([<loop_var>])

See the foreach() command.

The optional <loop_var> argument is supported for backward compatibility only. If used it must be a verbatim repeat of the <loop_var> argument of the opening foreach clause. Verbatim, bestie โ€” character for character, no creative liberties. โœ๏ธ


endfunction โญ

Ends a list of commands in a function block.

endfunction([<name>])

See the function() command.

The optional <name> argument is supported for backward compatibility only. If used it must be a verbatim repeat of the <name> argument of the opening function command.


endif ๐ŸŒˆ

Ends a list of commands in an if block.

endif([<condition>])

See the if() command.

The optional <condition> argument is supported for backward compatibility only. If used it must be a verbatim repeat of the argument of the opening if clause. Same energy as endforeach and endfunction โ€” legacy, keep it matching or keep it out. ๐Ÿซก


endmacro ๐Ÿ’œ

Ends a list of commands in a macro block.

endmacro([<name>])

See the macro() command.

The optional <name> argument is supported for backward compatibility only. If used it must be a verbatim repeat of the <name> argument of the opening macro command.


endwhile ๐Ÿ“

Ends a list of commands in a while block.

endwhile([<condition>])

See the while() command.

The optional <condition> argument is supported for backward compatibility only. If used it must be a verbatim repeat of the argument of the opening while clause.


execute_process ๐Ÿ”ฅ

Execute one or more child processes. This is the big one, gather round.

execute_process(COMMAND <cmd1> [<arguments>]
                [COMMAND <cmd2> [<arguments>]]...
                [WORKING_DIRECTORY <directory>]
                [TIMEOUT <seconds>]
                [RESULT_VARIABLE <variable>]
                [RESULTS_VARIABLE <variable>]
                [OUTPUT_VARIABLE <variable>]
                [ERROR_VARIABLE <variable>]
                [INPUT_FILE <file>]
                [OUTPUT_FILE <file>]
                [ERROR_FILE <file>]
                [OUTPUT_QUIET]
                [ERROR_QUIET]
                [COMMAND_ECHO <where>]
                [OUTPUT_STRIP_TRAILING_WHITESPACE]
                [ERROR_STRIP_TRAILING_WHITESPACE]
                [ENCODING <name>]
                [ENVIRONMENT <variable=value>...]
                [ENVIRONMENT_MODIFICATION <env-mod-op>...]
                [ECHO_OUTPUT_VARIABLE]
                [ECHO_ERROR_VARIABLE]
                [COMMAND_ERROR_IS_FATAL <ANY|LAST|NONE>])

Runs the given sequence of one or more commands.

Commands are executed concurrently as a pipeline, with the standard output of each process piped to the standard input of the next. A single standard error pipe is used for all processes. It's giving assembly line, it's giving relay race ๐Ÿƒโ€โ™€๏ธ

execute_process runs commands while CMake is configuring the project, prior to build system generation. Use the add_custom_target() and add_custom_command() commands to create custom commands that run at build time. Different times, different commands, don't mix them up bestie. โฐ

Options:

  • COMMAND โ€” A child process command line.

CMake executes the child process using operating system APIs directly:

  • On POSIX platforms, the command line is passed to the child process in an argv[] style array. No intermediate shell is executed, so shell operators such as > are treated as normal arguments. Yes, > is just text here. No redirect for you. ๐Ÿ™…โ€โ™€๏ธ
  • On Windows platforms, the command line is encoded as a string such that child processes using CommandLineToArgvW will decode the original arguments.

If the command runs a .exe, .com, or other executable, no intermediate command interpreter is executed, so shell operators such as > are treated as normal arguments.

If the command runs a .bat or .cmd script, it is executed through the cmd command interpreter. The command interpreter does not use CommandLineToArgvW, so some arguments may be received by the script with extra quoting. ๐Ÿ˜ฌ

Changed in version 4.0: .bat and .cmd scripts are now explicitly executed through the command interpreter by prepending cmd /c call to the command line. Previously, they were implicitly executed through cmd /c, without call, by undocumented behavior of CreateProcessW. Undocumented behavior, we do not miss you. ๐Ÿ’€

Use the INPUT_*, OUTPUT_*, and ERROR_* options to redirect stdin, stdout, and stderr.

For sequential execution of multiple commands use multiple execute_process calls each with a single COMMAND argument.

  • WORKING_DIRECTORY โ€” The named directory will be set as the current working directory of the child processes. If not given, the child processes' working directory is unspecified. So like... could be anywhere. Chaos. Specify it. ๐ŸŒ€
  • TIMEOUT โ€” After the specified number of seconds (fractions allowed), all unfinished child processes will be terminated, and the RESULT_VARIABLE will be set to a string mentioning the "timeout".
  • RESULT_VARIABLE โ€” The variable will be set to contain the result of last child process. This will be an integer return code from the last child or a string describing an error condition.
  • RESULTS_VARIABLE <variable> โ€” Added in version 3.10.

The variable will be set to contain the result of all processes as a semicolon-separated list, in order of the given COMMAND arguments. Each entry will be an integer return code from the corresponding child or a string describing an error condition.

  • INPUT_FILE <file> โ€” <file> is attached to the standard input pipe of the first COMMAND process.
  • OUTPUT_FILE <file> โ€” <file> is attached to the standard output pipe of the last COMMAND process.
  • ERROR_FILE <file> โ€” <file> is attached to the standard error pipe of all COMMAND processes.

Added in version 3.3: If the same <file> is named for both OUTPUT_FILE and ERROR_FILE then it will be used for both standard output and standard error pipes. Two pipes, one file, iconic efficiency โœจ

  • OUTPUT_QUIET, ERROR_QUIET โ€” The standard output on OUTPUT_VARIABLE or standard error on ERROR_VARIABLE are not connected (no variable content). The *_FILE and ECHO_*_VARIABLE options are not affected. So quiet means quiet for the variable only, the files still get everything. ๐Ÿ™Š
  • OUTPUT_VARIABLE, ERROR_VARIABLE โ€” bestie, these set the named variable to the contents of the standard output and standard error pipes, respectively. If you name the same variable for both pipes, their output gets merged in the order produced. ๐Ÿ’–
  • OUTPUT_STRIP_TRAILING_WHITESPACE, ERROR_STRIP_TRAILING_WHITESPACE โ€” removes whitespace characters from the end of the values stored in the corresponding OUTPUT_VARIABLE and ERROR_VARIABLE variables. โœจ
  • ECHO_OUTPUT_VARIABLE, ECHO_ERROR_VARIABLE โ€” Added in version 3.18. The standard output or standard error will not be exclusively redirected to the specified variables. The output will be duplicated into the specified variables and also onto standard output or standard error, analogous to the tee Unix command. ๐Ÿซถ
Note: If more than one OUTPUT_* or ERROR_* option is given for the same pipe, the precedence is not specified. If no OUTPUT_* or ERROR_* options are given, the output will be shared with the corresponding pipes of the CMake process itself. ๐Ÿ˜ญ
  • COMMAND_ECHO <where> โ€” Added in version 3.15. The command being run will be echo'ed to <where>, with <where> being set to one of STDERR, STDOUT or NONE. See the CMAKE_EXECUTE_PROCESS_COMMAND_ECHO variable for a way to control the default behavior when this option is not present. ๐Ÿ‘‘
  • ENCODING <name> โ€” Added in version 3.8. On Windows, the encoding that is used to decode output from the process. Ignored on other platforms. Valid encoding names are:
    • NONE โ€” perform no decoding. This assumes that the process output is encoded in the same way as CMake's internal encoding (UTF-8). This was the default in CMake 3.14 and older.
    • AUTO โ€” use the current active console's codepage or, if that isn't available, then use ANSI. This was the default in CMake 3.15 through 3.30.
    • ANSI โ€” use the ANSI codepage.
    • OEM โ€” use the original equipment manufacturer (OEM) code page.
    • UTF-8 โ€” Added in version 3.11. Use the UTF-8 codepage. This is the default since CMake 3.31. See policy CMP0176.
    • UTF8 โ€” use the UTF-8 codepage. Use of this name is discouraged in favor of UTF-8 to match the UTF-8 RFC (https://datatracker.ietf.org/doc/html/rfc3629) naming convention.
  • ENVIRONMENT <variable=value>... โ€” Added in version 4.4. Add environment variable definitions to the environment of the child processes. Each entry must be of the form NAME=value and is interpreted as setting NAME to value for the duration of the execute_process() call. These entries are processed in order. If the same variable name is given multiple times, the last value wins. ๐ŸŒˆ
  • ENVIRONMENT_MODIFICATION <env-mod-op>... โ€” Added in version 4.4. Apply one or more ENVIRONMENT_MODIFICATION operations to the environment of the child processes. Each operation is applied in the given order, and failures to parse an operation are treated as errors. Environment modifications are applied after ENVIRONMENT entries, if both options are specified. ๐Ÿ’…
  • COMMAND_ERROR_IS_FATAL <ANY|LAST|NONE> โ€” Added in version 3.19. The option following COMMAND_ERROR_IS_FATAL determines the behavior when an error is encountered:
    • ANY โ€” if any of the commands in the list of commands fail, the execute_process() command halts with an error.
    • LAST โ€” if the last command in the list of commands fails, the execute_process() command halts with an error. Commands earlier in the list will not cause a fatal error.
    • NONE โ€” Added in version 4.0. Regardless of any of the commands failing, the execute_process() command will not halt with an error.

Added in version 4.0: If not provided, the CMAKE_EXECUTE_PROCESS_COMMAND_ERROR_IS_FATAL variable is checked. If the variable is not set, the default is NONE. If RESULT_VARIABLE or RESULTS_VARIABLE is supplied, CMAKE_EXECUTE_PROCESS_COMMAND_ERROR_IS_FATAL is ignored. ๐Ÿ”ฅ


๐Ÿ“ file

File manipulation command. This one is dedicated to file and path manipulation that actually requires access to the filesystem. ๐Ÿ—‚๏ธ

For other path manipulation, handling only syntactic aspects, see the cmake_path() command.

Note: The sub-commands file(RELATIVE_PATH), file(TO_CMAKE_PATH), and file(TO_NATIVE_PATH) have been superseded, respectively, by the sub-commands cmake_path(RELATIVE_PATH), cmake_path(CONVERT ... TO_CMAKE_PATH_LIST), and cmake_path(CONVERT ... TO_NATIVE_PATH_LIST). ๐Ÿซถ

โœจ Synopsis

Reading
  file(READ <filename> <out-var> [...])
  file(STRINGS <filename> <out-var> [...])
  file(<HASH> <filename> <out-var>)
  file(TIMESTAMP <filename> <out-var> [...])

Writing
  file({WRITE | APPEND} <filename> <content>...)
  file({TOUCH | TOUCH_NOCREATE} <file>...)
  file(GENERATE OUTPUT <output-file> [...])
  file(CONFIGURE OUTPUT <output-file> CONTENT <content> [...])

Filesystem
  file({GLOB | GLOB_RECURSE} <out-var> [...] <globbing-expr>...)
  file(MAKE_DIRECTORY <directories>... [...])
  file({REMOVE | REMOVE_RECURSE } <files>...)
  file(RENAME <oldname> <newname> [...])
  file(COPY_FILE <oldname> <newname> [...])
  file({COPY | INSTALL} <file>... DESTINATION <dir> [...])
  file(SIZE <filename> <out-var>)
  file(READ_SYMLINK <linkname> <out-var> [...])
  file(CREATE_LINK <original> <linkname> [...])
  file(CHMOD <files>... <directories>... PERMISSIONS <permissions>... [...])
  file(CHMOD_RECURSE <files>... <directories>... PERMISSIONS <permissions>... [...])

Path Conversion
  file(REAL_PATH <path> <out-var> [BASE_DIRECTORY <dir>] [EXPAND_TILDE])
  file(RELATIVE_PATH <out-var> <directory> <file>)
  file({TO_CMAKE_PATH | TO_NATIVE_PATH} <path> <out-var>)

Transfer
  file(DOWNLOAD <url> [<file>] [...])
  file(UPLOAD <file> <url> [...])

Locking
  file(LOCK <path> [...])

Archiving
  file(ARCHIVE_CREATE OUTPUT <archive> PATHS <paths>... [...])
  file(ARCHIVE_EXTRACT INPUT <archive> [...])

Handling Runtime Binaries
  file(GET_RUNTIME_DEPENDENCIES [...])

๐Ÿ“– Reading

file(READ <filename> <variable> [OFFSET <offset>] [LIMIT <max-in>] [HEX]) โ€” read content from a file called <filename> and store it in a <variable>. Optionally start from the given <offset> and read at most <max-in> bytes. The HEX option causes data to be converted to a hexadecimal representation (useful for binary data). If the HEX option is specified, letters in the output (a through f) are in lowercase. ๐Ÿ“

file(STRINGS <filename> <variable> <options>...) โ€” parse a list of ASCII strings from <filename> and store it in <variable>. Binary data in the file are ignored. Carriage return (\r, CR) characters are ignored. The options are: ๐ŸŒธ

  • LENGTH_MAXIMUM <max-len> โ€” consider only strings of at most a given length.
  • LENGTH_MINIMUM <min-len> โ€” consider only strings of at least a given length.
  • LIMIT_COUNT <max-num> โ€” limit the number of distinct strings to be extracted.
  • LIMIT_INPUT <max-in> โ€” limit the number of input bytes to read from the file.
  • LIMIT_OUTPUT <max-out> โ€” limit the number of total bytes to store in the <variable>.
  • NEWLINE_CONSUME โ€” treat newline characters (\n, LF) as part of string content instead of terminating at them.
  • NO_HEX_CONVERSION โ€” Intel Hex and Motorola S-record files are automatically converted to binary while reading unless this option is given.
  • REGEX <regex> โ€” consider only strings that match the given regular expression, as described under string(REGEX). Changed in version 3.29: Capture groups from the last match in the file are stored in CMAKE_MATCH_<n>, similar to string(REGEX MATCHALL). See policy CMP0159.
  • ENCODING <encoding-type> โ€” Added in version 3.1. Consider strings of a given encoding. Currently supported encodings are: UTF-8, UTF-16LE, UTF-16BE, UTF-32LE, UTF-32BE. If the ENCODING option is not provided and the file has a Byte Order Mark, the ENCODING option will be defaulted to respect the Byte Order Mark.

Added in version 3.2: Added the UTF-16LE, UTF-16BE, UTF-32LE, UTF-32BE encodings. ๐Ÿ’œ

For example, the code

file(STRINGS myfile.txt myfile)

stores a list in the variable myfile in which each item is a line from the input file. โœจ

file(<HASH> <filename> <variable>) โ€” compute a cryptographic hash of the content of <filename> and store it in a <variable>. The supported <HASH> algorithm names are those listed by the string(<HASH>) command. ๐Ÿ”ฅ

file(TIMESTAMP <filename> <variable> [<format>] [UTC]) โ€” compute a string representation of the modification time of <filename> and store it in <variable>. Should the command be unable to obtain a timestamp, variable will be set to the empty string (""). See the string(TIMESTAMP) command for documentation of the <format> and UTC options. ๐Ÿ’…

โœ๏ธ Writing

file(WRITE <filename> <content>...)

file(APPEND <filename> <content>...)

Writes <content> into a file called <filename>. If the file doesn't exist yet, it gets created โ€” no cap. If it does already exist, then: WRITE mode overwrites it, and APPEND mode appends to the end. And any directories in the path you gave in <filename> that don't exist yet? Yeah, they get created too. Convenient bestie behavior ๐Ÿ’…

If the file is a build input, use the configure_file() command to update the file only when its content actually changes. That's the move. โœจ

file(TOUCH <files>...)

file(TOUCH_NOCREATE <files>...)

Added in version 3.12.

Creates a file with no content if it doesn't already exist. If the file does exist, its access and/or modification time gets bumped to whenever the function call runs. Classic touch, iconic behavior. ๐ŸŽ€

Use TOUCH_NOCREATE to touch a file only if it exists but never create it. If a file doesn't exist, it's just silently ignored โ€” no drama, no error, she simply moves on ๐Ÿ™ˆ

With both TOUCH and TOUCH_NOCREATE, the contents of an existing file will not be modified. We're touching, not rewriting, okay?

Changed in version 3.30: <files> can be an empty list. CMake 3.29 and earlier required at least one file to be given. Glow-up fr fr ๐ŸŒธ


file(GENERATE [...])

Generates an output file for each build configuration supported by the current CMake Generator. It evaluates generator expressions from the input content to produce the output content. It's giving very demure, very configuration-aware ๐Ÿ’–

file(GENERATE OUTPUT <output-file>
     <INPUT <input-file>|CONTENT <content>>
     [CONDITION <expression>] [TARGET <target>]
     [NO_SOURCE_PERMISSIONS | USE_SOURCE_PERMISSIONS |
      FILE_PERMISSIONS <permissions>...]
     [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF]])

Okay so the options are:

  • CONDITION <condition> โ€” generates the output file for a particular configuration only if the condition is true. And heads up: the condition must be either 0 or 1 after evaluating generator expressions. Binary queen. No in-betweens ๐Ÿ‘‘
  • CONTENT <content> โ€” use the content given explicitly as input.
  • INPUT <input-file> โ€” use the content from a given file as input.

Changed in version 3.10: a relative path is treated with respect to the value of CMAKE_CURRENT_SOURCE_DIR. See policy CMP0070.

  • OUTPUT <output-file> โ€” specify the output file name to generate. Use generator expressions such as $<CONFIG> to specify a configuration-specific output file name. Multiple configurations may generate the same output file only if the generated content is identical. Otherwise, the <output-file> must evaluate to a unique name for each configuration. Don't be out here colliding, bestie ๐Ÿ˜ญ

Changed in version 3.10: a relative path (after evaluating generator expressions) is treated with respect to the value of CMAKE_CURRENT_BINARY_DIR. See policy CMP0070.

  • TARGET <target> โ€” added in version 3.19. Specifies which target to use when evaluating generator expressions that require a target for evaluation (e.g. $<COMPILE_FEATURES:...>, $<TARGET_PROPERTY:prop>).
  • NO_SOURCE_PERMISSIONS โ€” added in version 3.20. The generated file permissions default to the standard 644 value (-rw-r--r--).
  • USE_SOURCE_PERMISSIONS โ€” added in version 3.20. Transfers the file permissions of the INPUT file to the generated file. This is already the default behavior if none of the three permissions-related keywords are given (NO_SOURCE_PERMISSIONS, USE_SOURCE_PERMISSIONS or FILE_PERMISSIONS). The USE_SOURCE_PERMISSIONS keyword mostly serves as a way of making the intended behavior clearer at the call site โ€” it's basically wearing a little name tag so everyone knows what's going on ๐Ÿซถ It is an error to specify this option without INPUT.
  • FILE_PERMISSIONS <permissions>... โ€” added in version 3.20. Use the specified permissions for the generated file.
  • NEWLINE_STYLE <style> โ€” added in version 3.20. Specify the newline style for the generated file. Specify UNIX or LF for \n newlines, or specify DOS, WIN32, or CRLF for \r\n newlines. Pick your fighter โญ

Exactly one CONTENT or INPUT option must be given. A specific OUTPUT file may be named by at most one invocation of file(GENERATE). Generated files are modified and their timestamp updated on subsequent cmake runs only if their content is changed. Lowkey respectful of your build times, honestly.

Note also that file(GENERATE) does not create the output file until the generation phase. The output file will not yet have been written when the file(GENERATE) command returns โ€” it is written only after processing all of a project's CMakeLists.txt files. So don't go looking for it immediately and then panic, bestie ๐Ÿ’€

file(CONFIGURE OUTPUT <output-file> CONTENT <content> [ESCAPE_QUOTES] [@ONLY] [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF]])

Added in version 3.18.

Generates an output file using the input given by CONTENT and substitutes variable values referenced as @VAR@ or ${VAR} contained therein. The substitution rules behave the same as the configure_file() command. And to match configure_file()'s behavior, generator expressions are not supported for both OUTPUT and CONTENT, and the output file is only modified (with its timestamp updated) if the content is changed or the file previously didn't exist. Consistency is the vibe โœจ

The arguments are:

  • OUTPUT <output-file> โ€” specify the output file name to generate. A relative path is treated with respect to the value of CMAKE_CURRENT_BINARY_DIR. <output-file> does not support generator expressions.
  • CONTENT <content> โ€” use the content given explicitly as input. <content> does not support generator expressions.
  • ESCAPE_QUOTES โ€” escape any substituted quotes with backslashes (C-style).
  • @ONLY โ€” restrict variable replacement to references of the form @VAR@. This is useful for configuring scripts that use ${VAR} syntax. Very thoughtful, very demure ๐Ÿ™ˆ
  • NEWLINE_STYLE <style> โ€” specify the newline style for the output file. Specify UNIX or LF for \n newlines, or specify DOS, WIN32, or CRLF for \r\n newlines.

๐Ÿ—‚ Filesystem

file(GLOB ...)

file(GLOB_RECURSE ...)

file(GLOB <variable> [LIST_DIRECTORIES true|false] [RELATIVE <path>]
     [CONFIGURE_DEPENDS] <globbing-expressions>...)

file(GLOB_RECURSE <variable> [FOLLOW_SYMLINKS]
     [LIST_DIRECTORIES true|false] [RELATIVE <path>] [CONFIGURE_DEPENDS]
     <globbing-expressions>...)

Generates a list of files that match the <globbing-expressions> and stores it into the <variable>. Globbing expressions are similar to regular expressions but much simpler โ€” regex's chiller little sister ๐ŸŒˆ If the RELATIVE flag is specified, the results will be returned as relative paths to the given path.

Changed in version 3.6: the results will be ordered lexicographically. Organized queen.

On Windows and macOS, globbing is case-insensitive even if the underlying filesystem is case-sensitive (both filenames and globbing expressions are converted to lowercase before matching). On other platforms, globbing is case-sensitive. Platform-dependent behavior, love that for us ๐Ÿ’€

Added in version 3.3: by default GLOB lists directories. Directories are omitted in the result if LIST_DIRECTORIES is set to false.

Added in version 3.12: if the CONFIGURE_DEPENDS flag is specified, CMake will add logic to the main build system check target to rerun the flagged GLOB commands at build time. If any of the outputs change, CMake will regenerate the build system. She's checking in on you ๐Ÿซถ

Note: we do not recommend using GLOB to collect a list of source files from your source tree. If no CMakeLists.txt file changes when a source is added or removed, then the generated build system cannot know when to ask CMake to regenerate. The CONFIGURE_DEPENDS flag may not work reliably on all generators, or if a new generator is added in the future that cannot support it, projects using it will be stuck. Even if CONFIGURE_DEPENDS works reliably, there is still a cost to performing the check on every rebuild. It's giving "cheap now, expensive later" and we don't stan ๐Ÿ˜ญ

Examples of globbing expressions include:

  • *.cxx โ€” match all files with extension cxx
  • *.vt? โ€” match all files with extension vta, ..., vtz
  • f[3-5].txt โ€” match files f3.txt, f4.txt, f5.txt

The GLOB_RECURSE mode will traverse all the subdirectories of the matched directory and match the files. Subdirectories that are symlinks are only traversed if FOLLOW_SYMLINKS is given or policy CMP0009 is not set to NEW. Respect the boundaries, bestie ๐Ÿฆ‹

  • LIST_DIRECTORIES โ€” ok so added in version 3.3: by default GLOB_RECURSE be leaving directories out of the result list entirely (rude but true ๐Ÿ’…). Set LIST_DIRECTORIES to true and directories get added in. But bestie, listen: if FOLLOW_SYMLINKS is given, or policy CMP0009 <#policy:CMP0009> is not set to NEW, then LIST_DIRECTORIES treats symlinks as directories. That's the whole vibe check there ๐Ÿฆ‹

Examples of recursive globbing include:

  • /dir/*.py โ€” match all python files in /dir and subdirectories โœจ

file(MAKE_DIRECTORY <directories>... [RESULT <result>])

Create the given directories and their parents as needed โ€” like, it'll build the whole family tree for you, no cap ๐ŸŽ€ Relative input paths are evaluated with respect to the current source directory.

The options are:

  • RESULT <result> โ€” added in version 3.31. Sets the <result> variable to 0 on success or an error message otherwise. If RESULT is not specified and the operation fails, an error is emitted. So like, either you catch the drama or the drama catches you ๐Ÿ’€

Changed in version 3.30: <directories> can be an empty list. CMake 3.29 and earlier required at least one directory to be given โ€” those were stricter times, we don't talk about it ๐Ÿ™ˆ


file(REMOVE <files>...)

file(REMOVE_RECURSE <files>...)

Remove the given files. The REMOVE_RECURSE mode will remove the given files and directories, including non-empty directories โ€” yes bestie, it goes all the way in ๐Ÿ”ฅ No error is emitted if a given file does not exist (it's very "no thoughts, head empty, whatever" about missing files). Relative input paths are evaluated with respect to the current source directory.

Changed in version 3.15: empty input paths are ignored with a warning. Previous versions of CMake interpreted empty strings as a relative path with respect to the current directory and removed its contents. So likeโ€ฆ the old behaviour was lowkey a jumpscare. Do not recreate it. โœ‹

file(RENAME <oldname> <newname> [RESULT <result>] [NO_REPLACE])

Move a file or directory within a filesystem from <oldname> to <newname>, replacing the destination atomically โ€” slay, no half-finished situations ๐Ÿ’…

The options are:

  • RESULT <result> โ€” added in version 3.21. Sets the <result> variable to 0 on success or an error message otherwise. If RESULT is not specified and the operation fails, an error is emitted.
  • NO_REPLACE โ€” added in version 3.21. If the <newname> path already exists, do not replace it. If RESULT <result> is used, the result variable will be set to NO_REPLACE. Otherwise, an error is emitted. She said "not today" ๐Ÿซถ

file(COPY_FILE <oldname> <newname> [RESULT <result>] [ONLY_IF_DIFFERENT] [INPUT_MAY_BE_RECENT])

Added in version 3.21.

Copy a file from <oldname> to <newname>. Directories are not supported (sorry bestie, file only, this is a file-only zone). Symlinks are ignored and <oldfile>'s content is read and written to <newname> as a new file โ€” so the link's whole identity gets left behind ๐Ÿ˜ญ

The options are:

  • RESULT <result> โ€” sets the <result> variable to 0 on success or an error message otherwise. If RESULT is not specified and the operation fails, an error is emitted.
  • ONLY_IF_DIFFERENT โ€” if the <newname> path already exists, do not replace it if the file's contents are already the same as <oldname> (this avoids updating <newname>'s timestamp). Basically "if it's already iconic, why touch it" โœจ
  • INPUT_MAY_BE_RECENT โ€” added in version 3.26. Tell CMake that the input file may have been recently created. This is meaningful only on Windows, where files may be inaccessible for a short time after they are created. With this option, if permission is denied, CMake will retry reading the input a few times. Patience is a virtue, iykyk ๐Ÿงš

This sub-command has some similarities to configure_file() <#command:configure_file> with the COPYONLY option. An important difference is that configure_file() <#command:configure_file> creates a dependency on the source file, so CMake will be re-run if it changes. The file(COPY_FILE) sub-command does not create such a dependency. Different vibes entirely, don't confuse them ๐Ÿ’œ

See also the file(COPY) sub-command just below which provides further file-copying capabilities.


file(COPY [...])

file(INSTALL [...])

The COPY signature copies files, directories, and symlinks to a destination folder. Relative input paths are evaluated with respect to the current source directory, and a relative destination is evaluated with respect to the current build directory. Copying preserves input file timestamps, and optimizes out a file if it exists at the destination with the same timestamp (efficient queen behavior ๐Ÿ‘‘). Copying preserves input permissions unless explicit permissions or NO_SOURCE_PERMISSIONS are given โ€” and the default is USE_SOURCE_PERMISSIONS.

file(<COPY|INSTALL> <files>... DESTINATION <dir>
     [NO_SOURCE_PERMISSIONS | USE_SOURCE_PERMISSIONS]
     [FILE_PERMISSIONS <permissions>...]
     [DIRECTORY_PERMISSIONS <permissions>...]
     [FOLLOW_SYMLINK_CHAIN]
     [FILES_MATCHING]
     [[PATTERN <pattern> | REGEX <regex>]
      [EXCLUDE] [PERMISSIONS <permissions>...]] [...])
Note: for a simple file copying operation, the file(COPY_FILE) sub-command just above may be easier to use. Shortcut era ๐Ÿ’…

Added in version 3.15: if FOLLOW_SYMLINK_CHAIN is specified, COPY will recursively resolve the symlinks at the paths given until a real file is found, and install a corresponding symlink in the destination for each symlink encountered. For each symlink that is installed, the resolution is stripped of the directory, leaving only the filename, meaning that the new symlink points to a file in the same directory as the symlink. This feature is useful on some Unix systems, where libraries are installed as a chain of symlinks with version numbers, with less specific versions pointing to more specific versions. FOLLOW_SYMLINK_CHAIN will install all of these symlinks and the library itself into the destination directory. For example, if you have the following directory structure:

  • /opt/foo/lib/libfoo.so.1.2.3
  • /opt/foo/lib/libfoo.so.1.2 -> libfoo.so.1.2.3
  • /opt/foo/lib/libfoo.so.1 -> libfoo.so.1.2
  • /opt/foo/lib/libfoo.so -> libfoo.so.1

and you do:

file(COPY /opt/foo/lib/libfoo.so DESTINATION lib FOLLOW_SYMLINK_CHAIN)

This will install all of the symlinks and libfoo.so.1.2.3 itself into lib. The whole friend group comes along ๐Ÿซถ

See the install(DIRECTORY) <#directory> command for documentation of permissions, FILES_MATCHING, PATTERN, REGEX, and EXCLUDE options. Copying directories preserves the structure of their content even if options are used to select a subset of files.

The INSTALL signature differs slightly from COPY: it prints status messages, and NO_SOURCE_PERMISSIONS is default. Installation scripts generated by the install() <#command:install> command use this signature (with some undocumented options for internal use โ€” mysterious, we love a secret ๐Ÿคซ).

Changed in version 3.22: the environment variable CMAKE_INSTALL_MODE <#envvar:CMAKE_INSTALL_MODE> can override the default copying behavior of file(INSTALL). She has range ๐ŸŒˆ


file(SIZE <filename> <variable>)

Added in version 3.14.

Determine the file size of the <filename> and put the result in <variable> variable. Requires that <filename> is a valid path pointing to a file and is readable โ€” so like, it has to actually exist and let you look at it, bestie. No fake files ๐Ÿ˜ค


Added in version 3.14.

Query the symlink <linkname> and stores the path it points to in the result <variable>.

The options are:

  • RESULT <result> โ€” added in version 4.4. Capture the status of the operation in a <result> variable. The variable is set to 0 on success or an error message otherwise. If not specified, and the operation fails, a fatal error is emitted. Dramatic but fair ๐Ÿ’€

Note that this command returns the raw symlink path and does not resolve a relative path. The following is an example of how to ensure that an absolute path is obtained:

set(linkname "/path/to/foo.sym")
file(READ_SYMLINK "${linkname}" result)
if(NOT IS_ABSOLUTE "${result}")
  get_filename_component(dir "${linkname}" DIRECTORY)
  set(result "${dir}/${result}")
endif()

Do the little manual labor yourself, she said ๐Ÿ’…


Added in version 3.14.

Create a link <linkname> that points to <original>. It will be a hard link by default, but providing the SYMBOLIC option results in a symbolic link instead. Hard links require that <original> exists and is a file, not a directory. If <linkname> already exists, it will be overwritten. No hesitation, she just goes for it ๐Ÿ”ฅ

The <result> variable, if specified, receives the status of the operation. It is set to 0 upon success or an error message otherwise. If RESULT is not specified and the operation fails, a fatal error is emitted.

Specifying COPY_ON_ERROR enables copying the file as a fallback if creating the link fails. It can be useful for handling situations such as <original> and <linkname> being on different drives or mount points, which would make them unable to support a hard link. Backup plan energy, we stan ๐ŸŒธ

Changed in version 4.3: if the source is a directory, CMake versions prior to 4.3 will create the destination directory if it does not exist, but not copy any files. With CMake 4.3 and above, the contents of the source directory will be copied recursively to the destination. See policy CMP0205 <#policy:CMP0205>. Glow-up of the century tbh โœจ


file(CHMOD <files>... <directories>... [PERMISSIONS <permissions>...] [FILE_PERMISSIONS <permissions>...] [DIRECTORY_PERMISSIONS <permissions>...])

Added in version 3.19.

  • OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ, GROUP_WRITE, GROUP_EXECUTE, WORLD_READ, WORLD_WRITE, WORLD_EXECUTE, SETUID, SETGID โ€” those are the valid permissions, bestie, and if you type anything else the vibe check fails, ok.

The valid keyword combos are like, the whole point, so pay attention:

  • PERMISSIONS โ€” literally every item gets changed. All of them. Periodt.
  • FILE_PERMISSIONS โ€” only the files change, directories get to chill.
  • DIRECTORY_PERMISSIONS โ€” only the directories change, files stay unbothered.
  • PERMISSIONS + FILE_PERMISSIONS โ€” FILE_PERMISSIONS overrides PERMISSIONS for files. Like, PERMISSIONS is the general rule and FILE_PERMISSIONS is the exception that ate.
  • PERMISSIONS + DIRECTORY_PERMISSIONS โ€” DIRECTORY_PERMISSIONS overrides PERMISSIONS for directories. Same energy, different target ๐ŸŽฏ
  • FILE_PERMISSIONS + DIRECTORY_PERMISSIONS โ€” use FILE_PERMISSIONS for files and DIRECTORY_PERMISSIONS for directories. Slay, they each got their own lane.

โœจ CHMOD_RECURSE, the girlypop who goes deeper โœจ

file(CHMOD_RECURSE <files>... <directories>...
     [PERMISSIONS <permissions>...] [FILE_PERMISSIONS <permissions>...]
     [DIRECTORY_PERMISSIONS <permissions>...])
๐ŸŒธ Added in version 3.19.

It's giving "same as CHMOD" but make it recurse โ€” it changes the permissions of the files and directories present in the <directories>... recursively. Yes she goes through everything. Obsessed ๐Ÿ’…


Path Conversion ๐Ÿฆ‹

file(REAL_PATH <path> <out-var> [BASE_DIRECTORY <dir>] [EXPAND_TILDE])
โญ Added in version 3.19.

Computes the absolute path to an existing file or directory, with symlinks resolved. The options are:

  • BASE_DIRECTORY <dir> โ€” if the <path> you handed over is a relative path, it gets evaluated relative to this base directory <dir>. If you didn't provide a base directory, the default base directory is CMAKE_CURRENT_SOURCE_DIR. That's just the house rules, no cap.
  • EXPAND_TILDE
๐ŸŽ€ Added in version 3.21.

If <path> is ~ or starts with ~/, the ~ gets replaced by the user's home directory. And where does the home directory come from? Environment variables, obviously. On Windows the USERPROFILE environment variable is used, falling back to the HOME environment variable if USERPROFILE is not defined. On all other platforms, only HOME is used. Windows stays extra, we been knew ๐Ÿ’€

๐Ÿ”ฅ Changed in version 3.28: All symlinks are resolved before collapsing ../ components. See policy CMP0152. That ordering is the main character here, don't sleep on it.
file(RELATIVE_PATH <variable> <directory> <file>)

Computes the relative path from a <directory> to a <file> and stores it in the <variable>. Cute, straightforward, no drama ๐ŸŒธ

file(TO_CMAKE_PATH "<path>" <variable>)
file(TO_NATIVE_PATH "<path>" <variable>)

Okay so these two share a brain cell, here's the tea:

The TO_CMAKE_PATH mode converts a native <path> into a CMake-style path with forward-slashes (/). The input can be a single path or a system search path like $ENV{PATH}. A search path gets converted to a semicolon-separated list. Yep, that's the CMake list life ๐Ÿซถ

The TO_NATIVE_PATH mode converts a CMake-style <path> into a native path with platform-specific slashes (\ on Windows hosts and / elsewhere).

๐Ÿ’€ Always use double quotes around the <path> to be sure it is treated as a single argument to this command. Like, always. I'm begging.

Transfer ๐ŸŒˆ

file(DOWNLOAD <url> [<file>] <options>...)
file(UPLOAD <file> <url> <options>...)

The DOWNLOAD subcommand downloads the given <url> to a local <file>. The UPLOAD mode uploads a local <file> to a given <url>. Two directions, one command, iconic behavior honestly ๐Ÿ’–

โœจ Added in version 3.19: If <file> is not specified for file(DOWNLOAD), the file is not saved. This can be useful if you want to know if a file can be downloaded (for example, to check that it exists) without actually saving it anywhere. Ghost mode ๐Ÿ‘ป โ€” you get the info, keep none of the baggage.

Options to both DOWNLOAD and UPLOAD are:

  • INACTIVITY_TIMEOUT <seconds> โ€” terminates the operation after a period of inactivity. Girl gets bored, she leaves ๐Ÿšช
  • LOG <variable> โ€” stores a human-readable log of the operation in a variable.
  • SHOW_PROGRESS โ€” prints progress information as status messages until the operation is complete. Little updates, we love to see them ๐Ÿ“Š
  • STATUS <variable> โ€” stores the resulting status of the operation in a variable. The status is a ; separated list of length 2. The first element is the numeric return value for the operation, and the second element is a string value for the error. A 0 numeric error means no error in the operation. Zero equals everything's fine, we stan a clear signal ๐ŸŽฏ
  • TIMEOUT <seconds> โ€” terminates the operation after a given total time has elapsed.
  • USERPWD <username>:<password>
๐Ÿ” Added in version 3.7.

Sets username and password for the operation.

  • HTTPHEADER <HTTP-header>
๐ŸŽ€ Added in version 3.7.

HTTP header for DOWNLOAD and UPLOAD operations. HTTPHEADER can be repeated for multiple options:

file(DOWNLOAD <url>
     HTTPHEADER "Authorization: Bearer <auth-token>"
     HTTPHEADER "UserAgent: Mozilla/5.0")

Stack them headers, she's collecting them like stickers โœจ

  • NETRC <level>
๐ŸŒธ Added in version 3.11.

Specifies whether the .netrc file is to be used for the operation. If this option is not specified, the value of the CMAKE_NETRC variable will be used instead.

Valid levels are:

  • IGNORED โ€” the .netrc file is ignored. This is the default. Not invited to the party ๐Ÿ™ˆ
  • OPTIONAL โ€” the .netrc file is optional, and information in the URL is preferred. The file will be scanned to find whichever information is not specified in the URL. Filler energy, but helpful.
  • REQUIRED โ€” the .netrc file is required, and information in the URL is ignored. It's the main character, the URL gets โœจnothingโœจ
  • NETRC_FILE <file>
๐Ÿฆ‹ Added in version 3.11.

Specifies an alternative .netrc file to the one in your home directory, if the NETRC level is OPTIONAL or REQUIRED. If this option is not specified, the value of the CMAKE_NETRC_FILE variable will be used instead.

  • TLS_VERSION <min>
๐Ÿ‘‘ Added in version 3.30.

Specifies minimum TLS version for https:// URLs. If this option is not specified, the value of the CMAKE_TLS_VERSION variable or CMAKE_TLS_VERSION environment variable will be used instead. See CMAKE_TLS_VERSION for allowed values.

โญ Changed in version 3.31: The default is TLS 1.2. Previously, no minimum version was enforced by default. Glow up for security, fr fr.
  • TLS_VERIFY <ON|OFF> โ€” specifies whether to verify the server certificate for https:// URLs. If this option is not specified, the value of the CMAKE_TLS_VERIFY variable or CMAKE_TLS_VERIFY environment variable will be used instead. If neither is set, the default is on.
๐Ÿ”ฅ Changed in version 3.31: The default is on. Previously, the default was off. Users may set the CMAKE_TLS_VERIFY environment variable to 0 to restore the old default.
๐ŸŽ€ Added in version 3.18: Added support to file(UPLOAD).
  • TLS_CAINFO <file> โ€” specifies a custom Certificate Authority file for https:// URLs. If this option is not specified, the value of the CMAKE_TLS_CAINFO variable will be used instead.
๐ŸŽ€ Added in version 3.18: Added support to file(UPLOAD).
โš ๏ธ For https:// URLs CMake must be built with SSL/TLS support. No support, no vibes, the operation is just not happening.

Additional options to DOWNLOAD are:

  • EXPECTED_HASH <algorithm>=<value> โ€” verifies that the downloaded content hash matches the expected value, where <algorithm> is one of the algorithms supported by <HASH>. If the file already exists and matches the hash, the download is skipped. If the file already exists and does not match the hash, the file is downloaded again. If after download the file does not match the hash, the operation fails with an error. It is an error to specify this option if DOWNLOAD is not given a <file>. She checks the receipts, always ๐Ÿ’…
  • EXPECTED_MD5 <value> โ€” historical short-hand for EXPECTED_HASH MD5=<value>. It is an error to specify this if DOWNLOAD is not given a <file>. Vintage, but still valid.
  • RANGE_START <value>
๐ŸŒˆ Added in version 3.24.

Offset of the start of the range in the file in bytes. Could be omitted to download up to the specified RANGE_END.

  • RANGE_END <value>
๐ŸŒˆ Added in version 3.24.

Offset of the end of the range in the file in bytes. Could be omitted to download everything from the specified RANGE_START to the end of the file. Snippets only, we don't need the whole thing bestie โœ‚๏ธ


Locking ๐Ÿ”’

file(LOCK <path> [DIRECTORY] [RELEASE] [GUARD <FUNCTION|FILE|PROCESS>]
     [RESULT_VARIABLE <variable>] [TIMEOUT <seconds>])
๐ŸŽ€ Added in version 3.2.

Lock a file specified by <path> if there's no DIRECTORY option, and the file <path>/cmake.lock otherwise. It stays locked for the scope set by the GUARD option (and the default value is PROCESS, bestie). The RELEASE option is your unlock button โ€” use it to explicitly free the file. If you don't throw in a TIMEOUT option, CMake will just sit there waiting until the lock succeeds or something fatally explodes. If TIMEOUT is set to 0, the lock is tried exactly once and the result gets reported immediately. If TIMEOUT isn't 0, CMake keeps trying to grab the lock for the period given by the TIMEOUT <seconds> value. Any errors get treated as fatal if there's no RESULT_VARIABLE option โ€” but if you do pass one, the result lands in <variable> and it'll be 0 on success or an error message on failure. ๐Ÿ’…

Ok so important vibe check: this lock is advisory โ€” there is literally no guarantee other processes will respect it. It synchronizes two or more CMake instances that are sharing some modifiable resources, that's it. Same energy applies to the DIRECTORY option; locking a parent directory doesn't stop other LOCK commands from locking any child directory or file.

Trying to lock the same file twice? Not allowed, periodt. Any intermediate directories and the file itself get created if they don't already exist. And the GUARD and TIMEOUT options are just ignored on the RELEASE operation, like they're not even invited to the party. ๐Ÿ™ˆ

๐Ÿ—„๏ธ Archiving

file(ARCHIVE_CREATE OUTPUT <archive> PATHS <paths>... [FORMAT <format>]
     [COMPRESSION <compression> [COMPRESSION_LEVEL <compression-level>]]
     [ENCODING <encoding>] [MTIME <mtime>] [THREADS <number>]
     [WORKING_DIRECTORY <dir>] [VERBOSE])

Added in version 3.18. โœจ

This creates the <archive> file you named, stuffed with the files and directories listed in <paths>. Heads up though: <paths> has to list actual files or directories โ€” wildcards are not supported, sorry bestie. ๐Ÿ˜ญ

The options are:

  • FORMAT <format> โ€” specifies the archive format. Supported values for <format> are 7zip, gnutar, pax, paxr, raw and zip. If FORMAT isn't given, the default format is paxr.

The default compression method depends on the format:

  • 7zip uses LZMA compression
  • zip uses Deflate compression
  • others use no compression by default
  • COMPRESSION <compression> โ€” some archive formats let you pick the type of compression. The 7zip and zip formats already imply a specific type of compression. The other formats use no compression by default, but you can direct them to do it with the COMPRESSION option. Valid values for <compression> are:
    • None
    • BZip2
    • Deflate โ€” added in version 4.3. This is an alias for GZip.
    • GZip
    • LZMA โ€” added in version 4.3.
    • LZMA2 โ€” added in version 4.3. This is an alias for XZ.
    • PPMd โ€” added in version 4.3. This compression method is only supported by the 7zip archive format.
    • XZ
    • Zstd
Note: with FORMAT set to raw, only one file will be compressed with the compression type specified by COMPRESSION. ๐Ÿซถ

Added in version 4.3: the 7zip and zip formats support changing the default compression.

  • COMPRESSION_LEVEL <compression-level> โ€” added in version 3.19. The compression level gets set with the COMPRESSION_LEVEL option. The <compression-level> should be between 0-9, with the default being 0. The COMPRESSION option must be present when COMPRESSION_LEVEL is given โ€” no solo missions allowed.

The value 0 is used to specify the default compression level. It's selected automatically by the archive library backend and not directly set by CMake itself, so the default compression level may vary between archive formats, platforms, etc.

Added in version 3.26: the <compression-level> of the Zstd algorithm can be set between 0-19.

Added in version 4.3: the <compression-level> can be specified for the 7zip and zip formats too. The Zstd algorithm compression level can be set between 0-19, except for zip format.

  • ENCODING <encoding> โ€” added in version 4.4. Specifies the pathname character encoding used in the archive.

The <encoding> may be one of:

  • UTF-8 โ€” archive pathnames are encoded as UTF-8. In CMake 4.4 and below, this is the default. See policy CMP0213 for CMake 4.3 and below compatibility details.
  • OEM โ€” on Windows platforms, pathnames are encoded as using the original equipment manufacturer (OEM) code page. On non-Windows platforms, pathnames are encoded according to the current locale. In CMake 4.3 and below, the OEM encoding (current locale) was always used.
  • UTF-16LE, UTF-16BE โ€” archive pathnames are encoded as UTF-16 little-endian or big-endian.
  • ... โ€” any encoding name supported by iconv on the current platform. On Windows, code page names may be specified.
Note: 7zip archives always encode paths as UTF-16LE, so this option is silently ignored for that format. ๐Ÿ’€
  • MTIME <mtime> โ€” specifies the modification time recorded in tarball entries.
  • THREADS <number> โ€” added in version 4.3. Use the <number> threads to operate on the archive. The number of available cores on the machine will be used if set to 0. Note that not all compression modes support threading in all environments.
  • WORKING_DIRECTORY <dir> โ€” added in version 3.31. Specifies the directory in which the archive creation operation will be executed. Paths in the <paths> argument can be relative to this directory. If this option isn't provided, the current working directory is used by default.
  • VERBOSE โ€” enables verbose output from the archive operation. ๐Ÿ”ฅ

file(ARCHIVE_EXTRACT INPUT <archive> [DESTINATION <dir>]
     [ENCODING <encoding>] [PATTERNS <pattern>...] [LIST_ONLY] [VERBOSE]
     [TOUCH])

Added in version 3.18. Extracts or lists the content of the specified <archive>. ๐ŸŒธ

The options are:

  • DESTINATION <dir> โ€” specifies the directory under which the content of the archive will be extracted. If the directory doesn't exist, it will be created. If DESTINATION isn't given, the current binary directory will be used.
  • ENCODING <encoding> โ€” added in version 4.4. Specifies the pathname character encoding used in the archive.

The <encoding> may be one of:

  • UTF-8 โ€” archive pathnames are encoded as UTF-8. In CMake 4.3 and below, this is the default if the CMP0213 policy is NEW.
  • OEM โ€” on Windows platforms, pathnames are encoded as using the original equipment manufacturer (OEM) code page. On non-Windows platforms, pathnames are encoded according to the current locale. In CMake 4.3 and below, the OEM encoding (current locale) was always used. In CMake 4.4 and above, the OEM encoding (current locale) is only used if the CMP0213 policy is not NEW.
  • UTF-16LE, UTF-16BE โ€” archive pathnames are encoded as UTF-16 little-endian or big-endian.
  • ... โ€” any encoding name supported by iconv on the current platform. On Windows, code page names may be specified.
Note: 7zip archives always encode paths as UTF-16LE, so this option is silently ignored for that format. ๐Ÿ™ˆ
  • PATTERNS <pattern>... โ€” extract/list only files and directories that match one of the given patterns. Wildcards are supported. If the PATTERNS option isn't given, the entire archive gets listed or extracted.
  • LIST_ONLY โ€” lists the files in the archive rather than extracting them.
  • TOUCH โ€” added in version 3.24. Gives extracted files a current local timestamp instead of extracting file timestamps from the archive.
  • VERBOSE โ€” enables verbose output from the extraction operation.
Changed in version 4.3: archive entries containing path traversal sequences (..), or absolute paths, are rejected for security. ๐Ÿ”’ Not up for debate, bestie.
Note: the working directory for this subcommand is the DESTINATION directory (provided or computed) except when LIST_ONLY is specified. Therefore, outside of script mode, it may be best to provide absolute paths to INPUT archives, as they're unlikely to be extracted where a relative path works. ๐Ÿซถ

๐Ÿงš Handling Runtime Binaries

file(GET_RUNTIME_DEPENDENCIES [...])

Added in version 3.16. Recursively get the list of libraries depended on by the given files:

                 file(GET_RUNTIME_DEPENDENCIES
                   [RESOLVED_DEPENDENCIES_VAR <deps_var>]
                   [UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>]
                   [CONFLICTING_DEPENDENCIES_PREFIX <conflicting_deps_prefix>]
                   [EXECUTABLES <executable_files>...]
                   [LIBRARIES <library_files>...]
                   [MODULES <module_files>...]
                   [DIRECTORIES <directories>...]
                   [BUNDLE_EXECUTABLE <bundle_executable_file>]
                   [PRE_INCLUDE_REGEXES <regexes>...]
                   [PRE_EXCLUDE_REGEXES <regexes>...]
                   [POST_INCLUDE_REGEXES <regexes>...]
                   [POST_EXCLUDE_REGEXES <regexes>...]
                   [POST_INCLUDE_FILES <files>...]
                   [POST_EXCLUDE_FILES <files>...]
                   )

Okay so bestie, real talk before you go sprinting with this: this sub-command is not meant for project mode. Like, don't just drop it into your normal CMakeLists.txt and act surprised when it gets weird. It's built for install time โ€” either from code that the install(RUNTIME_DEPENDENCY_SET) command generates, or from code your project hands over via install(CODE) or install(SCRIPT). โœจ

Like, for example:

                 install(CODE [[
                   file(GET_RUNTIME_DEPENDENCIES
                     # ...
                     )
                   ]])

๐Ÿ’… The arguments, one by one

  • RESOLVED_DEPENDENCIES_VAR <deps_var> โ€” the name of the variable where the list of resolved dependencies gets stashed. That's your good-stuff variable. ๐ŸŽ€
  • UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var> โ€” the name of the variable holding the list of unresolved dependencies. Lowkey important: if you don't specify this one and there happen to be any unresolved dependencies, an error gets issued. So likeโ€ฆ she's optional but also she's not. ๐Ÿ’€
  • CONFLICTING_DEPENDENCIES_PREFIX <conflicting_deps_prefix> โ€” the variable prefix for conflicting-dependency info. A dependency counts as conflicting when two files with the same name turn up in two different directories. The list of conflicting filenames lands in <conflicting_deps_prefix>_FILENAMES, and for each of those filenames, the paths that were found for it get stored in <conflicting_deps_prefix>_<filename>. Bookkeeping slay. ๐Ÿ—‚๏ธ
  • EXECUTABLES <executable_files>... โ€” list of executable files to read for dependencies. These are the executables you'd typically make with add_executable(), but ngl they don't have to come from CMake. On Apple platforms, the paths to these files decide the value of @executable_path when libraries get recursively resolved. โš ๏ธ And heads up: passing any kind of library here (STATIC, MODULE, or SHARED) gives you undefined behavior. Don't do that. ๐Ÿ™ˆ
  • LIBRARIES <library_files>... โ€” list of library files to read for dependencies. Typically the ones from add_library(SHARED), but again, not required to be CMake-born. Passing STATIC libraries, MODULE libraries, or executables here? Undefined behavior. Periodt. ๐Ÿ’…
  • MODULES <module_files>... โ€” list of loadable module files to read for dependencies. These are the modules you'd usually make with add_library(MODULE), but they don't have to be from CMake. They're the kind you load with dlopen() at runtime instead of linking at link time with ld -l. And yep โ€” passing STATIC libraries, SHARED libraries, or executables here is undefined behavior. ๐ŸŒธ
  • DIRECTORIES <directories>... โ€” extra directories to search for dependencies. The vibes differ per platform: on Linux, these get searched only if the dependency isn't found in any of the other usual paths โ€” and if it is found in one of these, a warning gets issued, because that means the file is incomplete (it doesn't list all the directories that contain its dependencies). On Windows, they're searched if the dependency isn't found in the other search paths, but no warning gets issued, because searching other paths is just a normal part of Windows dependency resolution. On Apple platforms, this argument has no effect at all. ๐ŸŽ
  • BUNDLE_EXECUTABLE <bundle_executable_file> โ€” the executable to treat as the "bundle executable" when resolving libraries. On Apple platforms, this argument decides the value of @executable_path when recursively resolving libraries for LIBRARIES and MODULES files. It has no effect on EXECUTABLES files. On other platforms, it has no effect, period. Typically (but not always!) it's one of the executables in the EXECUTABLES argument that designates the package's "main" executable. ๐Ÿ‘‘

๐Ÿฆ‹ The filter arguments

The following arguments are filters for including or excluding libraries to be resolved. Full description of how they work is right below โ€” and remember, directory separators in file paths may be matched using forward slashes unless policy CMP0207 is not set to NEW. โœจ

  • PRE_INCLUDE_REGEXES <regexes>... โ€” list of pre-include regexes that filter the names of not-yet-resolved dependencies.
  • PRE_EXCLUDE_REGEXES <regexes>... โ€” list of pre-exclude regexes that filter the names of not-yet-resolved dependencies.
  • POST_INCLUDE_REGEXES <regexes>... โ€” list of post-include regexes that filter the names of resolved dependencies.
  • POST_EXCLUDE_REGEXES <regexes>... โ€” list of post-exclude regexes that filter the names of resolved dependencies.
  • POST_INCLUDE_FILES <files>... โ€” Added in version 3.21. List of post-include filenames that filter the names of resolved dependencies. Symlinks get resolved when attempting to match these filenames. ๐Ÿ”—
  • POST_EXCLUDE_FILES <files>... โ€” Added in version 3.21. List of post-exclude filenames that filter the names of resolved dependencies. Symlinks get resolved when attempting to match these filenames too. ๐Ÿ”—

These arguments are how you exclude unwanted system libraries when resolving dependencies, or include libraries from a specific directory. The filtering works like this:

  1. If the not-yet-resolved dependency matches any of the PRE_INCLUDE_REGEXES, steps 2 and 3 get skipped, and dependency resolution proceeds straight to step 4. โญ๏ธ
  2. If the not-yet-resolved dependency matches any of the PRE_EXCLUDE_REGEXES, dependency resolution stops for that dependency. ๐Ÿ›‘
  3. Otherwise, dependency resolution proceeds.
  4. file(GET_RUNTIME_DEPENDENCIES) searches for the dependency according to the linking rules of the platform (see below). ๐Ÿ”
  5. If the dependency is found, and its full path matches one of the POST_INCLUDE_REGEXES or POST_INCLUDE_FILES, the full path is added to the resolved dependencies, and file(GET_RUNTIME_DEPENDENCIES) recursively resolves that library's own dependencies. Otherwise, resolution proceeds to step 6. ๐Ÿซถ
  6. If the dependency is found, but its full path matches one of the POST_EXCLUDE_REGEXES or POST_EXCLUDE_FILES, it is not added to the resolved dependencies, and dependency resolution stops for that dependency.
  7. If the dependency is found, and its full path matches none of POST_INCLUDE_REGEXES, POST_INCLUDE_FILES, POST_EXCLUDE_REGEXES, or POST_EXCLUDE_FILES, the full path is added to the resolved dependencies, and file(GET_RUNTIME_DEPENDENCIES) recursively resolves that library's own dependencies. ๐Ÿ’–

๐ŸŒˆ Platform specifics

Different platforms have different rules for how dependencies get resolved. The specifics are described right here, iykyk.

๐Ÿง On Linux platforms

Library resolution works as follows:

  1. If the depending file doesn't have any RUNPATH entries, and the library exists in one of the depending file's RPATH entries, or its parents', in that order, the dependency is resolved to that file.
  2. Otherwise, if the depending file has any RUNPATH entries, and the library exists in one of those entries, the dependency is resolved to that file.
  3. Otherwise, if the library exists in one of the directories listed by ldconfig, the dependency is resolved to that file.
  4. Otherwise, if the library exists in one of the DIRECTORIES entries, the dependency is resolved to that file. In this case, a warning is issued, because finding a file in one of the DIRECTORIES means that the depending file isn't complete (it doesn't list all the directories from which it pulls dependencies).
  5. Otherwise, the dependency is unresolved. ๐Ÿ˜ญ

Changed in version 3.31: resolution of each encountered library file name occurs at most once while processing a given root ELF file (executable or shared object). If a library file name is encountered again in the dependency tree, the original resolution is assumed. This behavior more closely matches the dynamic loader's behavior on Linux. โญ

๐ŸชŸ On Windows platforms

Library resolution works as follows:

  1. DLL dependency names get lowercased for filter matching โœจ Windows DLL names are case-insensitive, and some linkers just straight-up mangle the case of DLL dependency names (rude, honestly). But heads up โ€” this makes it harder for PRE_INCLUDE_REGEXES, PRE_EXCLUDE_REGEXES, POST_INCLUDE_REGEXES, and POST_EXCLUDE_REGEXES to properly filter DLL names, because every regex would have to check both uppercase and lowercase letters ๐Ÿ˜ญ Like, for example:
file(GET_RUNTIME_DEPENDENCIES
  # ...
  PRE_INCLUDE_REGEXES "^[Mm][Yy][Ll][Ii][Bb][Rr][Aa][Rr][Yy]\\.[Dd][Ll][Ll]$"
  )

Converting the DLL name to lowercase lets the regexes only match lowercase names, which simplifies the regex so much. For example:

file(GET_RUNTIME_DEPENDENCIES
  # ...
  PRE_INCLUDE_REGEXES "^mylibrary\\.dll$"
  )

This regex will match mylibrary.dll regardless of how it's cased, either on disk or in the depending file. (Like, it'll match mylibrary.dll, MyLibrary.dll, and MYLIBRARY.DLL โ€” all of them, slay ๐Ÿ’…)

Changed in version 3.27: The conversion to lowercase only applies while matching filters. Results reported after filtering case-preserve each DLL name as it is found on disk, if resolved, and otherwise as it is referenced by the dependent binary.

Prior to CMake 3.27, the results were reported with lowercase DLL file names, but the directory portion retained its casing ๐Ÿ‘€

  1. (Not yet implemented) If the depending file is a Windows Store app, and the dependency is listed as a dependency in the application's package manifest, the dependency is resolved to that file.
  2. Otherwise, if the library exists in the same directory as the depending file, the dependency is resolved to that file.
  3. Otherwise, if the library exists in either the operating system's system32 directory or the Windows directory, in that order, the dependency is resolved to that file.
  4. Otherwise, if the library exists in one of the directories specified by DIRECTORIES, in the order they are listed, the dependency is resolved to that file. In this case, a warning is not issued, because searching other directories is a normal part of Windows library resolution ๐Ÿซถ
  5. Otherwise, the dependency is unresolved.

On Apple platforms, library resolution works like this:

  1. If the dependency starts with @executable_path/, and an EXECUTABLES argument is in the process of being resolved, and replacing @executable_path/ with the directory of the executable yields an existing file, the dependency is resolved to that file.
  2. Otherwise, if the dependency starts with @executable_path/, and there is a BUNDLE_EXECUTABLE argument, and replacing @executable_path/ with the directory of the bundle executable yields an existing file, the dependency is resolved to that file.
  3. Otherwise, if the dependency starts with @loader_path/, and replacing @loader_path/ with the directory of the depending file yields an existing file, the dependency is resolved to that file.
  4. Otherwise, if the dependency starts with @rpath/, and replacing @rpath/ with one of the RPATH entries of the depending file yields an existing file, the dependency is resolved to that file. Note that RPATH entries that start with @executable_path/ or @loader_path/ also have these items replaced with the appropriate path ๐Ÿ‘‘
  5. Otherwise, if the dependency is an absolute file that exists, the dependency is resolved to that file.
  6. Otherwise, the dependency is unresolved.

This function accepts several variables that determine which tool is used for dependency resolution:

๐Ÿ› ๏ธ CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM

Determines which operating system and executable format the files are built for. This could be one of several values:

  • linux+elf
  • windows+pe
  • macos+macho

If this variable is not specified, it is determined automatically by system introspection ๐Ÿ”

๐Ÿ› ๏ธ CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL

Determines the tool to use for dependency resolution. It could be one of several values, depending on the value of CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM:

  • linux+elf โ†’ objdump
  • windows+pe โ†’ objdump or dumpbin
  • macos+macho โ†’ otool

If this variable is not specified, it is determined automatically by system introspection.

๐Ÿ› ๏ธ CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND

Determines the path to the tool to use for dependency resolution. This is the actual path to objdump, dumpbin, or otool.

If this variable is not specified, it is determined by the value of CMAKE_OBJDUMP <#variable:CMAKE_OBJDUMP> variable if set, else by system introspection.

Added in version 3.18: Uses CMAKE_OBJDUMP <#variable:CMAKE_OBJDUMP> if set.


๐ŸŒธ find_file ๐ŸŒธ

A short-hand signature is:

find_file (<VAR> <name> [<path>...])

The general signature is:

find_file (
          <VAR>
          {<name> | NAMES <name>...}
          [HINTS {<path> | ENV <var>}...]
          [PATHS {<path> | ENV <var>}...]
          [REGISTRY_VIEW {64|32|64_32|32_64|HOST|TARGET|BOTH}]
          [PATH_SUFFIXES <suffix>...]
          [VALIDATOR <function>]
          [DOC "cache documentation string"]
          [NO_CACHE]
          [REQUIRED|OPTIONAL]
          [NO_DEFAULT_PATH]
          [NO_PACKAGE_ROOT_PATH]
          [NO_CMAKE_PATH]
          [NO_CMAKE_ENVIRONMENT_PATH]
          [NO_SYSTEM_ENVIRONMENT_PATH]
          [NO_CMAKE_SYSTEM_PATH]
          [NO_CMAKE_INSTALL_PREFIX]
          [CMAKE_FIND_ROOT_PATH_BOTH |
           ONLY_CMAKE_FIND_ROOT_PATH |
           NO_CMAKE_FIND_ROOT_PATH]
         )

This command is used to find a full path to a named file ๐Ÿ’…

Prior to searching, find_file checks if variable <VAR> is defined. If the variable is not defined, the search will be performed. If the variable is defined and its value is NOTFOUND, or ends in -NOTFOUND, the search will be performed. If the variable contains any other value the search is not performed ๐Ÿ‘€

Note: VAR is considered defined if it is available in the current scope. See the cmake-language(7) variables <#cmake-language-variables> documentation for details on scopes, and the interaction of normal variables and cache entries.

The results of the search will be stored in a cache entry named <VAR>. Future calls to find_file will inspect this cache entry when specifying the same <VAR>. This optimization ensures successful searches will not be repeated unless the cache entry is unset() <#command:unset> ๐ŸŽ€

If the full path to a file is found the recorded value in cache entry <VAR> will be the result of the search. If nothing is found, the recorded value will be <VAR>-NOTFOUND.

Options include:

  • NAMES โ€” Specify one or more possible names for the full path to a file.

When using this to specify names with and without a version suffix, we recommend specifying the unversioned name first so that locally-built packages can be found before those provided by distributions ๐Ÿซถ

  • HINTS, PATHS โ€” Specify directories to search in addition to the default locations. The ENV var sub-option reads paths from a system environment variable.

Changed in version 3.24: On Windows platform, it is possible to include registry queries as part of the directories, using a dedicated syntax <#find-using-windows-registry>. Such specifications will be ignored on all other platforms.

  • REGISTRY_VIEW โ€” Added in version 3.24.

Specify which registry views must be queried. This option is only meaningful on Windows platforms and will be ignored on other ones. When not specified, the TARGET view is used when the CMP0134 <#policy:CMP0134> policy is NEW. Refer to CMP0134 <#policy:CMP0134> for the default view when the policy is OLD.

  • 64 โ€” Query the 64-bit registry. On 32-bit Windows, it always returns the string /REGISTRY-NOTFOUND ๐Ÿ’€
  • 32 โ€” Query the 32-bit registry.
  • 64_32 โ€” Query both views (64 and 32) and generate a path for each.
  • 32_64 โ€” Query both views (32 and 64) and generate a path for each.
  • HOST โ€” Query the registry matching the architecture of the host: 64 on 64-bit Windows and 32 on 32-bit Windows.
  • TARGET โ€” queries the registry matching whatever architecture CMAKE_SIZEOF_VOID_P says we're on. If that variable isn't defined, no stress โ€” it just falls back to the HOST view. ๐Ÿ’…
  • BOTH โ€” queries both views (32 and 64). The order it picks depends on these rules, bestie, so pay attention:

If CMAKE_SIZEOF_VOID_P is defined, the view follows whatever's inside it:

  • 8: โ†’ 64_32
  • 4: โ†’ 32_64

If CMAKE_SIZEOF_VOID_P is not defined, it just goes off the host's architecture:

  • 64-bit host: โ†’ 64_32
  • 32-bit host: โ†’ 32
  • PATH_SUFFIXES โ€” extra subdirectories to check below each directory location that's otherwise considered. Little bonus folders, yk? โœจ
  • VALIDATOR โ€” added in version 3.25. Hand it a function() to be called for every candidate item found (a macro() is not allowed โ€” pass one and you get an error, sorry babe). Two arguments get passed to your validator: the name of a result variable, and the absolute path to the candidate item. The item is accepted and the search ends โ€” unless the function sets that result variable's value to false in the calling scope. When the validator function is entered, the result variable is holding a true value. ๐Ÿซถ
function(my_check validator_result_var item)
  if(NOT item MATCHES ...)
    set(${validator_result_var} FALSE PARENT_SCOPE)
  endif()
endfunction()

find_file (result NAMES ... VALIDATOR my_check)
Note: if a cached result gets used, the search is skipped and any VALIDATOR is ignored entirely. And no, the cached result isn't required to pass the validation function. ๐Ÿ™ˆ
  • DOC โ€” the documentation string for the <VAR> cache entry. Give your cache variable a little glow-up description. ๐ŸŽ€
  • NO_CACHE โ€” added in version 3.21. The search result gets stored in a normal variable instead of a cache entry.
Note: find_file will still check for <VAR> as usual โ€” first looking for a variable, then a cache entry. If either one indicates a previous successful search, the search just doesn't happen.
Warning: use this one with caution, fr fr โ€” it can greatly increase the cost of repeated configure steps. ๐Ÿ˜ญ
  • REQUIRED โ€” added in version 3.18. Stops processing with an error message if nothing's found; otherwise the search gets attempted again the next time find_file is invoked with the same variable.

Added in version 4.1: every find_file command is treated as REQUIRED when the CMAKE_FIND_REQUIRED variable is enabled.

  • OPTIONAL โ€” added in version 4.1. Ignores the value of CMAKE_FIND_REQUIRED and keeps going without an error message if nothing is found. Incompatible with REQUIRED โ€” pick a lane. ๐Ÿ’€

If NO_DEFAULT_PATH is specified, then no additional paths get added to the search. If NO_DEFAULT_PATH is not specified, the search process goes like this, in order:

  1. If called from within a find module or any other script loaded by a call to find_package(<PackageName>), it searches prefixes unique to the current package being found. See policy CMP0074. ๐Ÿฆ‹

Added in version 3.12.

Specifically, it searches paths specified by the following variables, in order:

  1. <PackageName>_ROOT CMake variable, where <PackageName> is the case-preserved package name.
  2. <PACKAGENAME>_ROOT CMake variable, where <PACKAGENAME> is the upper-cased package name. See policy CMP0144.

Added in version 3.27.

  1. <PackageName>_ROOT environment variable, where <PackageName> is the case-preserved package name.
  2. <PACKAGENAME>_ROOT environment variable, where <PACKAGENAME> is the upper-cased package name. See policy CMP0144.

Added in version 3.27.

The package root variables are maintained as a stack, so if you're called from nested find modules or config packages, the root paths from the parent's find module or config package get searched after the paths from the current module or package. In other words, the search order is <CurrentPackage>_ROOT, ENV{<CurrentPackage>_ROOT}, <ParentPackage>_ROOT, ENV{<ParentPackage>_ROOT}, etc. You can skip this whole thing if NO_PACKAGE_ROOT_PATH is passed, or by setting CMAKE_FIND_USE_PACKAGE_ROOT_PATH to FALSE.

  • <prefix>/include/<arch> if CMAKE_LIBRARY_ARCHITECTURE is set, and <prefix>/include for each <prefix> in the <PackageName>_ROOT CMake variable and the <PackageName>_ROOT environment variable, if called from within a find module loaded by find_package(<PackageName>). ๐ŸŒธ
  1. Search paths specified in cmake-specific cache variables. These are meant to be used on the command line with a -DVAR=value. The values get interpreted as semicolon-separated lists. Skippable if NO_CMAKE_PATH is passed, or by setting CMAKE_FIND_USE_CMAKE_PATH to FALSE.
    • <prefix>/include/<arch> if CMAKE_LIBRARY_ARCHITECTURE is set, and <prefix>/include for each <prefix> in CMAKE_PREFIX_PATH
    • CMAKE_INCLUDE_PATH
    • CMAKE_FRAMEWORK_PATH
  2. Search paths specified in cmake-specific environment variables. These are meant to be set in the user's shell config, so they use the host's native path separator (; on Windows and : on UNIX). Skippable if NO_CMAKE_ENVIRONMENT_PATH is passed, or by setting CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH to FALSE.
    • <prefix>/include/<arch> if CMAKE_LIBRARY_ARCHITECTURE is set, and <prefix>/include for each <prefix> in CMAKE_PREFIX_PATH
    • CMAKE_INCLUDE_PATH
    • CMAKE_FRAMEWORK_PATH
  3. Search the paths specified by the HINTS option. These should be paths computed by system introspection โ€” like a hint from the location of another item you already found. Hard-coded guesses belong in the PATHS option, okay? ๐Ÿ’–
  4. Search the standard system environment variables. Skippable if NO_SYSTEM_ENVIRONMENT_PATH is passed, or by setting CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH to FALSE.
    • The directories in INCLUDE and PATH.

On Windows hosts, CMake 3.3 through 3.27 searched additional paths: <prefix>/include/<arch> if CMAKE_LIBRARY_ARCHITECTURE is set, and <prefix>/include for each <prefix>/[s]bin in PATH, plus <entry>/include for other entries in PATH. This behavior was removed by CMake 3.28. rip to that era. ๐Ÿ’€

  1. Search cmake variables defined in the Platform files for the current system. The searching of CMAKE_INSTALL_PREFIX and CMAKE_STAGING_PREFIX can be skipped if NO_CMAKE_INSTALL_PREFIX is passed, or by setting CMAKE_FIND_USE_INSTALL_PREFIX to FALSE. All these locations can be skipped if NO_CMAKE_SYSTEM_PATH is passed, or by setting CMAKE_FIND_USE_CMAKE_SYSTEM_PATH to FALSE.
    • <prefix>/include/<arch> if CMAKE_LIBRARY_ARCHITECTURE is set, and <prefix>/include for each <prefix> in CMAKE_SYSTEM_PREFIX_PATH
    • CMAKE_SYSTEM_INCLUDE_PATH
    • CMAKE_SYSTEM_FRAMEWORK_PATH

The platform paths these variables contain are locations that typically include installed software โ€” /usr/local on UNIX-based platforms is the classic example. ๐Ÿ‘‘

  1. Search the paths specified by the PATHS option or in the short-hand version of the command. These are typically hard-coded guesses. ๐Ÿคทโ€โ™€๏ธ

The CMAKE_IGNORE_PATH, CMAKE_IGNORE_PREFIX_PATH, CMAKE_SYSTEM_IGNORE_PATH and CMAKE_SYSTEM_IGNORE_PREFIX_PATH variables can also cause some of the locations above to be ignored. Not me ignoring stuff โ€” that's literally what these do. ๐Ÿ™ˆ

Added in version 3.16: added CMAKE_FIND_USE_<CATEGORY>_PATH variables to globally disable various search locations.

On macOS, the CMAKE_FIND_FRAMEWORK and CMAKE_FIND_APPBUNDLE variables decide the order of preference between Apple-style and unix-style package components. ๐ŸŽ


The CMake variable CMAKE_FIND_ROOT_PATH specifies one or more directories to be prepended to all other search directories. It effectively "re-roots" the entire search under those locations. Paths that are descendants of CMAKE_STAGING_PREFIX are excluded from this re-rooting, because that variable is always a path on the host system. By default, CMAKE_FIND_ROOT_PATH is empty. ๐Ÿซถ

The CMAKE_SYSROOT variable can also be used to specify exactly one directory to use as a prefix. Heads up: setting CMAKE_SYSROOT also has other effects โ€” check the docs for that variable for more. โœจ

So these are especially the vibe when you're cross-compiling and you need to point at the root dir of the target environment โ€” CMake will go look there too, bestie. ๐Ÿ’–

By default, the order is: first the directories listed in CMAKE_FIND_ROOT_PATH get searched, then the CMAKE_SYSROOT directory, and then the non-rooted directories. You can adjust that default by setting CMAKE_FIND_ROOT_PATH_MODE_INCLUDE. And if you're a control freak like me, you can override the whole thing manually on a per-call basis with these options:

  • CMAKE_FIND_ROOT_PATH_BOTH โ€” search in the order described above.
  • NO_CMAKE_FIND_ROOT_PATH โ€” do not use the CMAKE_FIND_ROOT_PATH variable.
  • ONLY_CMAKE_FIND_ROOT_PATH โ€” search only the re-rooted directories and directories below CMAKE_STAGING_PREFIX.

The default search order is designed to go most-specific โ†’ least-specific for common use cases. And if a project wants a different order? Literally just call the command multiple times and use the NO_* options. Obsessed with the simplicity:

find_file (<VAR> NAMES name PATHS <paths>... NO_DEFAULT_PATH)
find_file (<VAR> NAMES name)

Once one of those calls succeeds, the result variable is set and stored in the cache, so no call will search again. Slay. ๐Ÿ’…


๐ŸŒธ find_library ๐ŸŒธ

Short-hand signature, for when you're being chill:

find_library (<VAR> <name> [<path>...])

The general signature, for when you're being thorough:

find_library (
          <VAR>
          {<name> | NAMES <name>... [NAMES_PER_DIR]}
          [HINTS {<path> | ENV <var>}...]
          [PATHS {<path> | ENV <var>}...]
          [REGISTRY_VIEW {64|32|64_32|32_64|HOST|TARGET|BOTH}]
          [PATH_SUFFIXES <suffix>...]
          [VALIDATOR <function>]
          [DOC "cache documentation string"]
          [NO_CACHE]
          [REQUIRED|OPTIONAL]
          [NO_DEFAULT_PATH]
          [NO_PACKAGE_ROOT_PATH]
          [NO_CMAKE_PATH]
          [NO_CMAKE_ENVIRONMENT_PATH]
          [NO_SYSTEM_ENVIRONMENT_PATH]
          [NO_CMAKE_SYSTEM_PATH]
          [NO_CMAKE_INSTALL_PREFIX]
          [CMAKE_FIND_ROOT_PATH_BOTH |
           ONLY_CMAKE_FIND_ROOT_PATH |
           NO_CMAKE_FIND_ROOT_PATH]
         )

Bestie. This command finds a library. That's the whole assignment. ๐ŸŽ€

Before it searches, find_library checks whether the variable <VAR> is defined:

  • Not defined โ†’ the search happens.
  • Defined and its value is NOTFOUND, or ends in -NOTFOUND โ†’ the search happens.
  • Any other value โ†’ the search does not happen.
Note: <VAR> counts as defined if it's available in the current scope. Peep the cmake-language(7) variables docs for the tea on scopes and how normal variables and cache entries interact. ๐Ÿซถ

The results of the search get stored in a cache entry named <VAR>. Future calls to find_library will inspect that cache entry when the same <VAR> comes up. This optimization means successful searches don't get repeated โ€” unless you unset() that cache entry. ๐Ÿ˜Œ

If the library is found, the recorded value in cache entry <VAR> is the result of the search. If nothing is found, the recorded value is <VAR>-NOTFOUND. Simple as.

Options include:

  • NAMES โ€” specify one or more possible names for the library. When you use this to specify names with and without a version suffix, we recommend putting the unversioned name first, so locally-built packages get found before the ones distributions provide. Order matters, periodt.
  • HINTS, PATHS โ€” specify directories to search in addition to the default locations. The ENV var sub-option reads paths from a system environment variable. ๐Ÿฆ‹
Changed in version 3.24: on Windows platform you can include registry queries as part of the directories, using a dedicated syntax. Such specifications are ignored on all other platforms.
  • REGISTRY_VIEW โ€” added in version 3.24. Specify which registry views must be queried. This option is only meaningful on Windows platforms and gets ignored on other ones. When not specified, the TARGET view is used when the CMP0134 policy is NEW. Refer to CMP0134 for the default view when the policy is OLD. ๐Ÿ‘‘
    • 64 โ€” query the 64-bit registry. On 32-bit Windows, it always returns the string /REGISTRY-NOTFOUND. ๐Ÿ’€
    • 32 โ€” query the 32-bit registry.
    • 64_32 โ€” query both views (64 and 32) and generate a path for each.
    • 32_64 โ€” query both views (32 and 64) and generate a path for each.
    • HOST โ€” query the registry matching the architecture of the host: 64 on 64-bit Windows and 32 on 32-bit Windows.
    • TARGET โ€” query the registry matching the architecture specified by the CMAKE_SIZEOF_VOID_P variable. If it's not defined, fall back to the HOST view.
    • BOTH โ€” query both views (32 and 64). The order depends on these rules: if the CMAKE_SIZEOF_VOID_P variable is defined, use the following view depending on its content:
      • 8: 64_32
      • 4: 32_64

If the CMAKE_SIZEOF_VOID_P variable is not defined, rely on the architecture of the host:

  • 64-bit: 64_32
  • 32-bit: 32
  • PATH_SUFFIXES โ€” specify extra subdirectories to check below each directory location otherwise considered. Little bonus levels. โœจ
  • VALIDATOR โ€” added in version 3.25. Specify a function() to be called for each candidate item found (a macro() can't be provided โ€” that results in an error). Two arguments get passed to the validator function: the name of a result variable, and the absolute path to the candidate item. The item is accepted and the search ends โ€” unless the function sets the value in the result variable to false in the calling scope. The result variable holds a true value when the validator function is entered. ๐ŸŒˆ
function(my_check validator_result_var item)
  if(NOT item MATCHES ...)
    set(${validator_result_var} FALSE PARENT_SCOPE)
  endif()
endfunction()

find_library (result NAMES ... VALIDATOR my_check)

Note that if a cached result is used, the search is skipped and any VALIDATOR is ignored. And the cached result is not required to pass the validation function. ๐Ÿ™ˆ

  • DOC โ€” specify the documentation string for the <VAR> cache entry.
  • NO_CACHE โ€” added in version 3.21. The result of the search gets stored in a normal variable rather than a cache entry. ๐Ÿ“
Note: find_library will still check for <VAR> as usual โ€” checking first for a variable, then a cache entry. If either one indicates a previous successful search, the search won't be performed.
Warning: use this option with caution, because it can greatly increase the cost of repeated configure steps. Not me warning you twice ๐Ÿ’€
  • REQUIRED โ€” added in version 3.18. Stop processing with an error message if nothing is found; otherwise the search will be attempted again the next time find_library is invoked with the same variable. Added in version 4.1: every find_library command will be treated as REQUIRED when the CMAKE_FIND_REQUIRED variable is enabled.
  • OPTIONAL โ€” added in version 4.1. Ignore the value of CMAKE_FIND_REQUIRED and continue without an error message if nothing is found. Incompatible with REQUIRED. (they cannot both be present, pick a lane ๐ŸŽ€)

If NO_DEFAULT_PATH is specified, then no additional paths get added to the search. If NO_DEFAULT_PATH is not specified, the search process goes like this:

  1. If called from within a find module or any other script loaded by a call to find_package(<PackageName>), search prefixes unique to the current package being found. See policy CMP0074. Added in version 3.12. ๐Ÿ’œ

Specifically, search paths specified by the following variables, in order:

a. <PackageName>_ROOT CMake variable, where <PackageName> is the case-preserved package name.

b. <PACKAGENAME>_ROOT CMake variable, where <PACKAGENAME> is the upper-cased package name. See policy CMP0144. Added in version 3.27.

c. <PackageName>_ROOT environment variable, where <PackageName> is the case-preserved package name.

d. <PACKAGENAME>_ROOT environment variable, where <PACKAGENAME> is the upper-cased package name. See policy CMP0144. Added in version 3.27.

The package root variables are maintained as a stack, so if you're called from nested find modules or config packages, root paths from the parent's find module or config package get searched after paths from the current module or package. In other words, the search order would be <CurrentPackage>_ROOT, ENV{<CurrentPackage>_ROOT}, <ParentPackage>_ROOT, ENV{<ParentPackage>_ROOT}, etc. You can skip this by passing NO_PACKAGE_ROOT_PATH or by setting CMAKE_FIND_USE_PACKAGE_ROOT_PATH to FALSE.

  • <prefix>/lib/<arch> if CMAKE_LIBRARY_ARCHITECTURE is set, and <prefix>/lib for each <prefix> in the <PackageName>_ROOT CMake variable and the <PackageName>_ROOT environment variable if called from within a find module loaded by find_package(<PackageName>)
  1. Search paths that come from cmake-specific cache variables. These are the ones you're meant to set on the command line with a -DVAR=value, bestie. The values get read as semicolon-separated lists. You can skip this whole step if NO_CMAKE_PATH is passed, or by setting the CMAKE_FIND_USE_CMAKE_PATH variable to FALSE.
    • <prefix>/lib/<arch> if CMAKE_LIBRARY_ARCHITECTURE is set, and <prefix>/lib for each <prefix> in CMAKE_PREFIX_PATH
    • CMAKE_LIBRARY_PATH
    • CMAKE_FRAMEWORK_PATH
  2. Search paths that come from cmake-specific environment variables ๐ŸŒฑ. These are the ones you'd set in your shell config, so they use your host's native path separator (; on Windows, : on UNIX โ€” yes the eternal feud continues). Skip this step if NO_CMAKE_ENVIRONMENT_PATH is passed, or by setting CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH to FALSE.
    • <prefix>/lib/<arch> if CMAKE_LIBRARY_ARCHITECTURE is set, and <prefix>/lib for each <prefix> in CMAKE_PREFIX_PATH
    • CMAKE_LIBRARY_PATH
    • CMAKE_FRAMEWORK_PATH
  3. Search the paths you handed over with the HINTS option. These are supposed to be paths your build figured out by looking at the system โ€” like a hint from where some other thing was already found. Hard-coded guesses? Those go in PATHS, not here. Keep the vibes correct ๐Ÿ’….
  4. Search the standard system environment variables. Skip it if NO_SYSTEM_ENVIRONMENT_PATH is passed, or by setting CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH to FALSE.
    • The directories in LIB and PATH.

On Windows hosts, CMake 3.3 through 3.27 went ahead and searched extra paths too: <prefix>/lib/<arch> if CMAKE_LIBRARY_ARCHITECTURE is set, and <prefix>/lib for each <prefix>/[s]bin in PATH, plus <entry>/lib for other entries in PATH. That behavior was removed by CMake 3.28 โ€” it's gone, it's not coming back, we don't talk about it ๐Ÿซก.

  1. Search cmake variables defined in the Platform files for whatever system you're on. The searching of CMAKE_INSTALL_PREFIX and CMAKE_STAGING_PREFIX can be skipped if NO_CMAKE_INSTALL_PREFIX is passed, or by setting CMAKE_FIND_USE_INSTALL_PREFIX to FALSE. And all of these locations can be skipped if NO_CMAKE_SYSTEM_PATH is passed, or by setting CMAKE_FIND_USE_CMAKE_SYSTEM_PATH to FALSE.
    • <prefix>/lib/<arch> if CMAKE_LIBRARY_ARCHITECTURE is set, and <prefix>/lib for each <prefix> in CMAKE_SYSTEM_PREFIX_PATH
    • CMAKE_SYSTEM_LIBRARY_PATH
    • CMAKE_SYSTEM_FRAMEWORK_PATH

The platform paths those variables hold are the spots that typically have installed software living in them. Think /usr/local on UNIX-based platforms โ€” iconic behavior honestly.

  1. Search the paths you gave with the PATHS option, or in the short-hand version of the command. These are typically hard-coded guesses. Yeah. We've all been there ๐Ÿ˜ญ.

The CMAKE_IGNORE_PATH, CMAKE_IGNORE_PREFIX_PATH, CMAKE_SYSTEM_IGNORE_PATH and CMAKE_SYSTEM_IGNORE_PREFIX_PATH variables can also cause some of the locations above to just... not be searched. Blocked. Ignored. Uninvited from the party ๐Ÿ™ˆ.

Added in version 3.16: Added the CMAKE_FIND_USE_<CATEGORY>_PATH variables so you can globally disable various search locations. One switch, so much power โœจ.

On macOS, the CMAKE_FIND_FRAMEWORK and CMAKE_FIND_APPBUNDLE variables decide the order of preference between Apple-style and unix-style package components. Pick your fighter ๐ŸŽ.

The CMake variable CMAKE_FIND_ROOT_PATH specifies one or more directories to be prepended to all other search directories. Basically it "re-roots" the entire search underneath the locations you give it. Paths that are descendants of CMAKE_STAGING_PREFIX are excluded from this re-rooting, because that variable is always a path on the host system. By default CMAKE_FIND_ROOT_PATH is empty (she's low maintenance).

The CMAKE_SYSROOT variable can also be used to specify exactly one directory to use as a prefix. Setting CMAKE_SYSROOT also has other effects though โ€” see that variable's documentation for the full tea โ˜•.

These variables are especially useful when you're cross-compiling and want to point at the root directory of the target environment, so CMake searches there too. By default the order goes: first the directories listed in CMAKE_FIND_ROOT_PATH get searched, then the CMAKE_SYSROOT directory, and then the non-rooted directories. You can adjust that default by setting CMAKE_FIND_ROOT_PATH_MODE_LIBRARY, or override it manually per-call with these options:

  • CMAKE_FIND_ROOT_PATH_BOTH โ€” Search in the order described above.
  • NO_CMAKE_FIND_ROOT_PATH โ€” Do not use the CMAKE_FIND_ROOT_PATH variable.
  • ONLY_CMAKE_FIND_ROOT_PATH โ€” Search only the re-rooted directories and directories below CMAKE_STAGING_PREFIX.

The default search order is designed to go most-specific to least-specific for the common cases โ€” a whole gradient, gorgeous ๐ŸŒˆ. But projects can override the order whenever they want by just calling the command multiple times and using the NO_* options:

find_library (<VAR> NAMES name PATHS <paths>... NO_DEFAULT_PATH)
find_library (<VAR> NAMES name)

Once one of those calls succeeds, the result variable gets set and stuffed into the cache so that no later call will search again. Found it once, found it forever ๐Ÿ’–.

When you give more than one value to the NAMES option, this command by default considers one name at a time and searches every directory for it. The NAMES_PER_DIR option flips that: it considers one directory at a time and searches for all the names inside it. Different strategy, same goal ๐ŸŽ€.

Each library name you hand to NAMES is first considered as-is if it contains a library suffix, and then considered with platform-specific prefixes (like lib) and suffixes (like .so), as defined by the CMAKE_FIND_LIBRARY_PREFIXES and CMAKE_FIND_LIBRARY_SUFFIXES variables. So yes, you can absolutely just specify library filenames like libfoo.a directly โ€” super useful for locating static libraries on UNIX-like systems ๐ŸŒธ.

If the library found is a framework, then <VAR> gets set to the full path to the framework, <fullPath>/A.framework. And when a full path to a framework is used as a library, CMake will use a -framework A and a -F<fullPath> to link the framework to the target. Neat, right?

Added in version 3.28: The library found can now be a .xcframework folder. Upgrade szn ๐Ÿฆ‹.

If the CMAKE_FIND_LIBRARY_CUSTOM_LIB_SUFFIX variable is set, all search paths get tested as normal โ€” with the suffix appended, and with all matches of lib/ replaced with lib${CMAKE_FIND_LIBRARY_CUSTOM_LIB_SUFFIX}/. This variable overrides the FIND_LIBRARY_USE_LIB32_PATHS, FIND_LIBRARY_USE_LIBX32_PATHS, and FIND_LIBRARY_USE_LIB64_PATHS global properties. She's the boss ๐Ÿ‘‘.

If the FIND_LIBRARY_USE_LIB32_PATHS global property is set, all search paths get tested as normal with 32/ appended, and with all matches of lib/ replaced with lib32/. This property is automatically set for platforms that are known to need it, if at least one of the languages supported by the project() command is enabled.

If the FIND_LIBRARY_USE_LIBX32_PATHS global property is set, all search paths get tested as normal with x32/ appended, and with all matches of lib/ replaced with libx32/. Same deal โ€” automatically set for platforms known to need it, if at least one language supported by the project() command is enabled.

If the FIND_LIBRARY_USE_LIB64_PATHS global property is set, all search paths get tested as normal with 64/ appended, and with all matches of lib/ replaced with lib64/. Also automatically set for the platforms that need it when at least one language supported by the project() command is enabled. Three siblings, three suffixes, one very thorough search ๐Ÿ”ฅ.

find_package

Note: The Using Dependencies Guide gives you a high-level introduction to this whole general topic. It serves up a broader overview of where the find_package() command fits into the bigger picture โ€” including its relationship to the FetchContent module. The guide is recommended pre-reading before you dive into the details below. Do your homework bestie, it's worth it ๐Ÿ“š.

Find a package (usually something provided by something external to the project), and load its package-specific details. Calls to this command can also be intercepted by dependency providers ๐Ÿ‘€.

Typical Usage

Most calls to find_package() look like this:

find_package(<PackageName> [<version>] [REQUIRED] [COMPONENTS <components>...])

The <PackageName> is the only mandatory argument โ€” everything else is optional and that's the vibe. The <version> is often omitted, and REQUIRED should be given if the project literally cannot configure successfully without the package. Some more complicated packages support components, which you can select with the COMPONENTS keyword, but most packages don't have that level of complexity. Keep it simple, slay ๐Ÿ’….

The above is a reduced form of the basic signature. Wherever possible, projects should find packages using this form โ€” it reduces complexity and maximizes the ways the package can be found or provided. Win-win ๐Ÿซถ.

Understanding the basic signature should be enough for general usage of find_package(). But project maintainers who intend to provide a package configuration file should understand the bigger picture, as explained in Full Signature and all the subsequent sections on this page. Big main character energy required for that one โœจ.

Search Modes

The command has a few modes by which it searches for packages:

๐Ÿ’… Module mode

okay so in this mode, CMake goes looking for a file called Find<PackageName>.cmake. It checks the locations listed in CMAKE_MODULE_PATH first, then it rummages through the Find Modules that ship with your CMake installation. If it finds the file, CMake reads it and processes it.

That file is the one doing all the work โ€” it's responsible for finding the package, checking the version, and producing any messages that need producing. Heads up: some Find modules give you limited version support or literally no version support at all, so go read that Find module's documentation, bestie. ๐Ÿ”

Here's the tea though ๐Ÿ“: the Find<PackageName>.cmake file is not typically provided by the package itself. Nope โ€” it's normally supplied by something external to the package, like the operating system, CMake itself, or even the project from which the find_package() command was called. And because it's externally provided, Find Modules tend to be heuristic in nature (read: vibes-based detective work) and they're super susceptible to becoming out-of-date. ๐Ÿ˜ญ They typically search for certain libraries, files, and other package artifacts.

Also, key detail: Module mode is only supported by the basic command signature. periodt.

๐Ÿ” Config mode

In this mode, CMake searches for a file matching any of these:

  • <PackageName>.cps
  • <lowercasePackageName>.cps
  • <lowercasePackageName>-config.cmake
  • <PackageName>Config.cmake

If one of the latter two gets found, CMake will also look โ€” respectively โ€” for <lowercasePackageName>-config-version.cmake or <PackageName>ConfigVersion.cmake, but only if version details were specified (peep Config Mode Version Selection for the whole explanation of how these separate version files get used). ๐Ÿ‘€

So the last two are CMake-script package descriptions. The first two are Common Package Specification <https://cps-org.github.io/cps/> (CPS) package descriptions, which are more portable and include version information in the 'base' file. And aside from any explicitly noted exceptions, any references to "config files", "config mode", "package configuration files", and so on refer equally to both CPS and CMake-script files.

Note: Search is implemented in a manner that will tend to prefer Common Package Specification files over CMake-script config files in most cases. Specifying CONFIGS suppresses consideration of CPS files. ๐Ÿซถ

In config mode, the command can be given a list of names to search for as package names. The locations where CMake searches for the config and version files is considerably more complicated than for Module mode (see Config Mode Search Procedure โ€” it's giving whole questline).

The config and version files are typically installed as part of the package, so they tend to be way more reliable than Find modules. They usually contain direct knowledge of the package contents, so no searching or heuristics are needed inside the config or version files themselves. icon behavior honestly. ๐Ÿ‘‘

Config mode is supported by both the basic and full command signatures.

๐Ÿงš FetchContent redirection mode

Added in version 3.24: A call to find_package() can be redirected internally to a package provided by the FetchContent module. To the caller, the behavior will appear similar to Config mode โ€” except the search logic is by-passed and the component information is not used. See FetchContent_Declare() and FetchContent_MakeAvailable() for further details. โœจ


When not redirected to a package provided by FetchContent, the command arguments determine whether Module or Config mode is used. When the basic signature is used, the command searches in Module mode first. If the package is not found, the search falls back to Config mode. ๐Ÿฆ‹

A user may set the CMAKE_FIND_PACKAGE_PREFER_CONFIG variable to true to reverse that priority and direct CMake to search using Config mode first before falling back to Module mode. The basic signature can also be forced to use only Module mode with a MODULE keyword. And if the full signature is used? The command only searches in Config mode. no fallbacks, no take-backs. ๐Ÿ’€

๐Ÿ“œ Basic Signature

find_package(<PackageName> [<version>] [EXACT] [QUIET] [MODULE]
             [REQUIRED|OPTIONAL] [[COMPONENTS] <component>...]
             [OPTIONAL_COMPONENTS <component>...]
             [REGISTRY_VIEW {64|32|64_32|32_64|HOST|TARGET|BOTH}]
             [GLOBAL]
             [NO_POLICY_SCOPE]
             [BYPASS_PROVIDER]
             [UNWIND_INCLUDE])

The basic signature is supported by both Module and Config modes. The MODULE keyword implies that only Module mode can be used to find the package, with no fallback to Config mode. ๐Ÿšซ

Regardless of the mode used, a <PackageName>_FOUND variable will be set to indicate whether the package was found. When the package is found, package-specific information may be provided through other variables and Imported Targets documented by the package itself. ๐ŸŒˆ

The QUIET option disables informational messages โ€” including those indicating that the package cannot be found if it is not REQUIRED. The REQUIRED option stops processing with an error message if the package cannot be found. ๐Ÿ˜ค

A package-specific list of required components may be listed after the COMPONENTS keyword. If any of these components are not able to be satisfied, the package overall is considered to be not found. If the REQUIRED option is also present, this is treated as a fatal error; otherwise execution still continues. As a form of shorthand, if the REQUIRED option is present, the COMPONENTS keyword can be omitted and the required components can be listed directly after REQUIRED. ๐ŸŽ€

The CMAKE_FIND_REQUIRED variable can be enabled to make this call REQUIRED by default. This behavior can be overridden by providing the OPTIONAL keyword. As with the REQUIRED option, a list of components can be listed directly after OPTIONAL, which is equivalent to listing them after the COMPONENTS keyword. When the OPTIONAL keyword is given, the warning output when a package is not found is suppressed. ๐Ÿคซ

Additional optional components may be listed after OPTIONAL_COMPONENTS. If these cannot be satisfied, the package overall can still be considered found โ€” as long as all required components are satisfied. slay. โœจ

The set of available components and their meaning are defined by the target package:

  • For CMake-script package configuration files, it is formally up to the target package how to interpret the component information given to it, but it should follow the expectations stated above. For calls where no components are specified, there is no single expected behavior and target packages should clearly define what occurs in such cases. Common arrangements include assuming it should find all components, no components, or some well-defined subset of the available components. ๐Ÿค”
  • Common Package Specification packages consist of a root configuration file and zero or more appendices, each of which provide components and may have dependencies. CMake always attempts to load the root configuration file. Appendices are only loaded if their dependencies can be satisfied, and if they either provide requested components or if no components were requested. If the dependencies of an appendix providing a required component cannot be satisfied, the package is considered not found. Otherwise, that appendix is ignored. ๐Ÿ’œ

Added in version 3.24: The REGISTRY_VIEW keyword specifies which registry views should be queried. This keyword is only meaningful on Windows platforms and will be ignored on all others. Formally, it is up to the target package how to interpret the registry view information given to it.

Added in version 3.24: Specifying the GLOBAL keyword will promote all imported targets to a global scope in the importing project. Alternatively, this functionality can be enabled by setting the CMAKE_FIND_PACKAGE_TARGETS_GLOBAL variable. ๐ŸŒ

The <version> argument requests a version with which the package found should be compatible. There are two possible forms in which it may be specified:

  • A single version with the format major[.minor[.patch[.tweak]]], where each component is a numeric value.
  • A version range with the format versionMin...[<]versionMax where versionMin and versionMax have the same format and constraints on components being integers as the single version. By default, both end points are included. By specifying <, the upper end point will be excluded. Version ranges are only supported with CMake 3.19 or later. ๐Ÿ”ฅ
Note: With the exception of CPS packages, version support is currently provided only on a package-by-package basis. When a version range is specified but the package is only designed to expect a single version, the package will ignore the upper end point of the range and only take the single version at the lower end of the range into account. Non-CPS packages that do support version ranges do so in a manner that is determined by the individual package. See the Version Selection section below for details and important caveats. ๐Ÿ˜ญ

The EXACT option requests that the version be matched exactly. This option is incompatible with the specification of a version range. ๐ŸŽฏ

If no <version> and/or component list is given to a recursive invocation inside a find-module, the corresponding arguments are forwarded automatically from the outer call (including the EXACT flag for <version>). ๐ŸŒ€

See the cmake_policy() command documentation for discussion of the NO_POLICY_SCOPE option.

Added in version 3.24: The BYPASS_PROVIDER keyword is only allowed when find_package() is being called by a dependency provider. It can be used by providers to call the built-in find_package() implementation directly and prevent that call from being re-routed back to itself. Future versions of CMake may detect attempts to use this keyword from places other than a dependency provider and halt with a fatal error. ๐Ÿ’€ So like, don't be weird about it.

Added in version 4.2: okay so the UNWIND_INCLUDE keyword is only allowed when find_package() is being called from inside a parent find_package() call โ€” that's the whole vibe check it has to pass ๐Ÿ’…

And when a find_package(UNWIND_INCLUDE) call fails to find the package it was looking for, it enters what we're calling an "unwind" state. Bestie, in that state it is so over for further searching: any more calls to find_package() and include() are forbidden ๐Ÿ˜ญ, and all the parent include() commands will immediately invoke return() the moment their scope is reached. That unwinding keeps going until we get all the way back up to the parent find_package().

Fr fr, UNWIND_INCLUDE is only intended to be used by calls to find_package() generated by install(EXPORT_PACKAGE_DEPENDENCIES) โ€” but ngl, if you're the type of icon who wants to manually manage your dependencies in a similar manner, it may be useful to you too ๐Ÿซถ


๐Ÿ’– Full Signature

find_package(<PackageName> [version] [EXACT] [QUIET]
             [REQUIRED|OPTIONAL] [[COMPONENTS] <component>...]
             [OPTIONAL_COMPONENTS <component>...]
             [CONFIG|NO_MODULE]
             [GLOBAL]
             [NO_POLICY_SCOPE]
             [BYPASS_PROVIDER]
             [NAMES <name>...]
             [CONFIGS <config>...]
             [HINTS <path>...]
             [PATHS <path>...]
             [REGISTRY_VIEW {64|32|64_32|32_64|HOST|TARGET|BOTH}]
             [PATH_SUFFIXES <suffix>...]
             [NO_DEFAULT_PATH]
             [NO_PACKAGE_ROOT_PATH]
             [NO_CMAKE_PATH]
             [NO_CMAKE_ENVIRONMENT_PATH]
             [NO_SYSTEM_ENVIRONMENT_PATH]
             [NO_CMAKE_PACKAGE_REGISTRY]
             [NO_CMAKE_BUILDS_PATH] # Deprecated; does nothing.
             [NO_CMAKE_SYSTEM_PATH]
             [NO_CMAKE_INSTALL_PREFIX]
             [NO_CMAKE_SYSTEM_PACKAGE_REGISTRY]
             [CMAKE_FIND_ROOT_PATH_BOTH |
              ONLY_CMAKE_FIND_ROOT_PATH |
              NO_CMAKE_FIND_ROOT_PATH]

So here's the tea โ˜• โ€” the CONFIG option, its synonymous little twin NO_MODULE, or honestly just using any of the options that aren't in the basic signature, all force pure Config mode. And in pure Config mode the command skips Module mode search entirely and goes straight to Config mode search. No detours, no side quests, periodt โœจ

Config mode search is out here trying to locate a configuration file that the package itself provides. A cache entry called <PackageName>_DIR gets created to hold the directory where that file lives ๐ŸŽ€ By default the command searches for a package named <PackageName>. But if you pass the NAMES option, the names after it get used instead of <PackageName>. Those names also matter when deciding whether to redirect the call to a package provided by FetchContent.

For each package name you specify, the command is hunting for a file whose name matches any of these ๐Ÿ‘‡

  • <PackageName>Config.cmake
  • <lowercasePackageName>-config.cmake
  • <PackageName>.cps
  • <lowercasePackageName>.cps

You can swap in your own set of possible config file names with the CONFIGS option. The Config Mode Search Procedure is spelled out below. Once a file is found, any version constraint gets checked, and if it's satisfied the config file is read and processed by CMake. And since the package itself provided that file, it already knows where its contents live โ€” iconic, honestly ๐Ÿ‘‘

Note: because CPS files aren't allowed to have names that don't match the package name, specifying CONFIGS will suppress searching for CPS files. ๐Ÿšซ

Every configuration file CMake has considered while searching for the package with a suitable version gets stashed in the <PackageName>_CONSIDERED_CONFIGS variable, with their associated versions living in <PackageName>_CONSIDERED_VERSIONS ๐Ÿ—‚๏ธ

If the package configuration file just cannot be found, CMake generates an error describing the problem โ€” unless you specified the QUIET argument ๐Ÿคซ. And if REQUIRED is specified and the package isn't found? Fatal error, configure step stops executing, we're done here, lights out ๐Ÿ’€. Also: if <PackageName>_DIR has been set to a directory that doesn't contain a configuration file, or the requested version isn't compatible with the package found in that directory (peep Config Mode Version Selection), CMake will ignore it and search from scratch. Fresh start, new me ๐ŸŒธ

Package maintainers out there providing package configuration files โ€” you're encouraged to name and install them so that the Config Mode Search Procedure below finds them without needing any extra options. Do it for the plot ๐Ÿซถ


๐Ÿฆ‹ Config Mode Search Procedure

Note: when Config mode is used, this search procedure applies regardless of whether you used the full signature or the basic one. Same energy either way ๐Ÿ’…

Added in version 3.24: literally all calls to find_package() โ€” even in Module mode โ€” first look for a config package file in the CMAKE_FIND_PACKAGE_REDIRECTS_DIR directory ๐Ÿ‘€. The FetchContent module, or even your project itself, can write files to that location to redirect find_package() calls to content the project already provides. If no config package file turns up in there, the search carries on with the logic described below โญ

Okay so CMake builds up a whole little โœจsetโœจ of possible installation prefixes for the package, and under each one it goes rummaging through several directories looking for a config file. The lists below are literally the directories it searches. Every entry is marked for install trees that follow Windows (W), UNIX (U), or Apple (A) conventions:

  • <prefix>/<name>/cps/ [2] โ€” W ๐Ÿ’…
  • <prefix>/<name>/*/cps/ [2] โ€” W ๐Ÿ’…
  • <prefix>/cps/<name>/ [2] โ€” W ๐Ÿ’…
  • <prefix>/cps/<name>/*/ [2] โ€” W ๐Ÿ’…
  • <prefix>/cps/ [2] โ€” W ๐Ÿ’…
  • <prefix>/ โ€” W ๐Ÿ’…
  • <prefix>/(cmake|CMake)/ โ€” W ๐Ÿ’…
  • <prefix>/<name>*/ โ€” W ๐Ÿ’…
  • <prefix>/<name>*/(cmake|CMake)/ โ€” W ๐Ÿ’…
  • <prefix>/<name>*/(cmake|CMake)/<name>*/ [1] โ€” W ๐Ÿ’…
  • <prefix>/(lib/<arch>|lib*|share)/cps/<name>/ [2] โ€” U ๐Ÿง
  • <prefix>/(lib/<arch>|lib*|share)/cps/<name>/*/ [2] โ€” U ๐Ÿง
  • <prefix>/(lib/<arch>|lib*|share)/cps/ [2] โ€” U ๐Ÿง
  • <prefix>/(lib/<arch>|lib*|share)/cmake/<name>*/ โ€” U ๐Ÿง
  • <prefix>/(lib/<arch>|lib*|share)/<name>*/ โ€” U ๐Ÿง
  • <prefix>/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/ โ€” U ๐Ÿง
  • <prefix>/<name>*/(lib/<arch>|lib*|share)/cmake/<name>*/ โ€” W/U ๐Ÿซถ
  • <prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/ โ€” W/U ๐Ÿซถ
  • <prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/ โ€” W/U ๐Ÿซถ
๐Ÿ“Œ [1] โ€” added in version 3.25. she's new-ish, be nice to her ๐Ÿ’–
๐Ÿ“Œ [2] โ€” added in version 4.3. even newer, iconic behaviour โœจ

๐ŸŽ okay but what about Apple stuff

On systems that support the FRAMEWORK and BUNDLE target properties, CMake also goes looking in these directories for Frameworks or Application Bundles that contain a configuration file:

  • <prefix>/<name>.framework/Versions/*/Resources/CPS/ [3] โ€” A ๐Ÿ“
  • <prefix>/<name>.framework/Resources/CPS/ [3] โ€” A ๐Ÿ“
  • <prefix>/<name>.framework/Resources/ โ€” A ๐Ÿ“
  • <prefix>/<name>.framework/Resources/CMake/ โ€” A ๐Ÿ“
  • <prefix>/<name>.framework/Versions/*/Resources/ โ€” A ๐Ÿ“
  • <prefix>/<name>.framework/Versions/*/Resources/CMake/ โ€” A ๐Ÿ“
  • <prefix>/<name>.app/Contents/Resources/CPS/ [3] โ€” A ๐Ÿ“
  • <prefix>/<name>.app/Contents/Resources/ โ€” A ๐Ÿ“
  • <prefix>/<name>.app/Contents/Resources/CMake/ โ€” A ๐Ÿ“
๐Ÿ“Œ [3] โ€” added in version 4.3. yes another 4.3 baby, they came in a batch ๐Ÿ’

๐Ÿซง the .cps vs .cmake rule (iykyk)

So when find_package is walking through all those paths up there, it's lowkey picky about file extensions ngl:

  • it will only look for .cps files in search paths that contain /cps/
  • and it will only look for .cmake files everywhere else

And bestie, that rule applies strictly to the paths as specified โ€” it does not go peeking at what's actually inside <prefix> or <name> to make up its mind. no cap ๐Ÿ™ˆ


And in literally all of the cases above, <name> is treated as case-insensitive and it matches any of the names you specified โ€” so that's <PackageName>, or whatever names you handed over via NAMES. slay ๐Ÿ’…โœจ

So like, if at least one compiled language has been enabled, CMake may go snooping through the architecture-specific lib/<arch> and lib* directories, all based on the compiler's target architecture โ€” and it does it in this exact order, bestie:

  • lib/<arch> โ€” probed if the CMAKE_LIBRARY_ARCHITECTURE variable is set. ๐Ÿ’…
  • lib64 โ€” probed on 64-bit platforms (CMAKE_SIZEOF_VOID_P is 8) and when the FIND_LIBRARY_USE_LIB64_PATHS property is set to TRUE.
  • lib32 โ€” probed on 32-bit platforms (CMAKE_SIZEOF_VOID_P is 4) and when the FIND_LIBRARY_USE_LIB32_PATHS property is set to TRUE.
  • libx32 โ€” probed on platforms using the x32 ABI if the FIND_LIBRARY_USE_LIBX32_PATHS property is set to TRUE.
  • lib โ€” always searched, no questions asked. ๐Ÿ‘‘

Changed in version 3.24: on Windows, you can totally sneak registry queries into the directories you specify through the HINTS and PATHS keywords, using a dedicated syntax. On every other platform those specifications just get ignored, respectfully. ๐Ÿ™ˆ

Added in version 3.24: REGISTRY_VIEW can be specified to manage the Windows registry queries you passed as part of PATHS and HINTS.

๐Ÿ’ป REGISTRY_VIEW โ€” pick your registry view

Okay so this one specifies which registry views must be queried. It's only meaningful on Windows and gets ignored everywhere else, iykyk. When you don't specify it, the TARGET view is used when the CMP0134 policy is NEW. And for the default view when that policy is OLD, go read CMP0134, bestie โ€” it's all laid out there.

  • 64 โ€” query the 64-bit registry. On 32-bit Windows it always returns the string /REGISTRY-NOTFOUND. ๐Ÿ’€
  • 32 โ€” query the 32-bit registry.
  • 64_32 โ€” query both views (64 and 32) and generate a path for each.
  • 32_64 โ€” query both views (32 and 64) and generate a path for each.
  • HOST โ€” query the registry matching the architecture of the host: 64 on 64-bit Windows, 32 on 32-bit Windows.
  • TARGET โ€” query the registry matching the architecture specified by the CMAKE_SIZEOF_VOID_P variable. If that's not defined, it falls back to the HOST view.
  • BOTH โ€” query both views (32 and 64). The order depends on these rules, so pay attention:
    • If the CMAKE_SIZEOF_VOID_P variable is defined, use the following view depending on what's inside it:
      • 8: 64_32
      • 4: 32_64
    • If the CMAKE_SIZEOF_VOID_P variable is not defined, rely on the architecture of the host:
      • 64-bit: 64_32
      • 32-bit: 32

If PATH_SUFFIXES is specified, the suffixes get appended to each (W) or (U) directory entry, one-by-one. ๐ŸŽ€

This whole set of directories is designed to work in cooperation with projects that provide configuration files in their installation trees. Directories marked with (W) are intended for installations on Windows, where the prefix may point at the top of an application's installation directory. Those marked with (U) are intended for installations on UNIX platforms, where the prefix is shared by multiple packages. Ngl it's merely a convention โ€” so all the (W) and (U) directories still get searched on all platforms. โœจ Directories marked with (A) are intended for installations on Apple platforms. The CMAKE_FIND_FRAMEWORK and CMAKE_FIND_APPBUNDLE variables decide the order of preference.

Warning, bestie: setting CMAKE_FIND_FRAMEWORK or CMAKE_FIND_APPBUNDLE to anything other than FIRST (the default) will cause CMake to search for Common Package Specification files in an order that's different from the order set forth in the specification. So like... be careful. ๐Ÿ˜ญ

The set of installation prefixes is constructed using the following steps. And heads up: if NO_DEFAULT_PATH is specified, all the NO_* options are enabled.

  1. Search prefixes unique to the current <PackageName> being found. See policy CMP0074. Added in version 3.12. Specifically, search prefixes specified by these variables, in order:
    • a. <PackageName>_ROOT CMake variable, where <PackageName> is the case-preserved package name.
    • b. <PACKAGENAME>_ROOT CMake variable, where <PACKAGENAME> is the upper-cased package name. See policy CMP0144. Added in version 3.27.
    • c. <PackageName>_ROOT environment variable, where <PackageName> is the case-preserved package name.
    • d. <PACKAGENAME>_ROOT environment variable, where <PACKAGENAME> is the upper-cased package name. See policy CMP0144. Added in version 3.27.

The package root variables are maintained as a stack, so if it's called from within a find module, root paths from the parent's find module will also be searched โ€” after the paths for the current package. You can skip this whole thing if NO_PACKAGE_ROOT_PATH is passed, or by setting CMAKE_FIND_USE_PACKAGE_ROOT_PATH to FALSE. ๐Ÿซถ

  1. Search paths specified in CMake-specific cache variables. These are meant to be used on the command line with -DVAR=VALUE. The values are interpreted as semicolon-separated lists. You can skip this if NO_CMAKE_PATH is passed or by setting CMAKE_FIND_USE_CMAKE_PATH to FALSE:
    • CMAKE_PREFIX_PATH
    • CMAKE_FRAMEWORK_PATH
    • CMAKE_APPBUNDLE_PATH
  2. Search paths specified in CMake-specific environment variables. These are meant to be set in the user's shell configuration, so they use the host's native path separator (; on Windows and : on UNIX). Skip it if NO_CMAKE_ENVIRONMENT_PATH is passed or by setting CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH to FALSE:
    • <PackageName>_DIR
    • CMAKE_PREFIX_PATH
    • CMAKE_FRAMEWORK_PATH
    • CMAKE_APPBUNDLE_PATH
  3. Search paths specified by the HINTS option. These should be paths computed by system introspection, like a hint provided by the location of another item you already found. Hard-coded guesses go with the PATHS option instead, bestie.
  4. Search the standard system environment variables. Skip it if NO_SYSTEM_ENVIRONMENT_PATH is passed or by setting CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH to FALSE. Path entries ending in /bin or /sbin are automatically converted to their parent directories:
    • PATH
  5. Search paths stored in the CMake User Package Registry. Skip it if NO_CMAKE_PACKAGE_REGISTRY is passed, or by setting the variable CMAKE_FIND_USE_PACKAGE_REGISTRY to FALSE, or the deprecated variable CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY to TRUE. For the details on the user package registry, see the cmake-packages(7) manual. ๐Ÿฆ‹
  6. Search CMake variables defined in the Platform files for the current system. The searching of CMAKE_INSTALL_PREFIX and CMAKE_STAGING_PREFIX can be skipped if NO_CMAKE_INSTALL_PREFIX is passed or by setting CMAKE_FIND_USE_INSTALL_PREFIX to FALSE. All these locations can be skipped if NO_CMAKE_SYSTEM_PATH is passed or by setting CMAKE_FIND_USE_CMAKE_SYSTEM_PATH to FALSE:
    • CMAKE_SYSTEM_PREFIX_PATH
    • CMAKE_SYSTEM_FRAMEWORK_PATH
    • CMAKE_SYSTEM_APPBUNDLE_PATH

The platform paths these variables contain are locations that typically include installed software โ€” an example being /usr/local for UNIX based platforms. ๐Ÿ’œ

  1. Search paths stored in the CMake System Package Registry. Skip it if NO_CMAKE_SYSTEM_PACKAGE_REGISTRY is passed, or by setting the CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY variable to FALSE, or the deprecated variable CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY to TRUE. See the cmake-packages(7) manual for details on the system package registry.
  2. Search paths specified by the PATHS option. These are typically hard-coded guesses. ๐Ÿ™ˆ

The CMAKE_IGNORE_PATH, CMAKE_IGNORE_PREFIX_PATH, CMAKE_SYSTEM_IGNORE_PATH and CMAKE_SYSTEM_IGNORE_PREFIX_PATH variables can also cause some of the above locations to be ignored. So like, those are your blocklist, basically. โœจ

Paths are searched in the order described above. The first viable package configuration file found is used, even if a newer version of the package resides later in the list of search paths. No cap. ๐Ÿ”ฅ

For search paths which contain glob expressions (*), directories matching the glob are searched in natural, descending order by default. You can override that behavior by setting the variables CMAKE_FIND_PACKAGE_SORT_ORDER and CMAKE_FIND_PACKAGE_SORT_DIRECTION accordingly. Those variables determine the order in which CMake considers glob matches. For example, if the file system contains these package configuration files:

<prefix>/example-1.2/example-config.cmake
<prefix>/example-1.10/example-config.cmake
<prefix>/share/example-2.0/example-config.cmake

like okay so find_package(example) will โ€” when those variables up there are unset โ€” pick example-1.10 (assuming both example-1.2 and example-1.10 are viable). But bestie, note that find_package will not find example-2.0, because one of the other two gets found first. that's just how the queue works ๐Ÿ’…

So if you want to actually control the order in which find_package searches directories matching a glob expression, you use CMAKE_FIND_PACKAGE_SORT_ORDER and CMAKE_FIND_PACKAGE_SORT_DIRECTION. For instance, to make the example above select example-1.2, you just set:

set(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)
set(CMAKE_FIND_PACKAGE_SORT_DIRECTION ASC)

...before calling find_package. that's it, that's the whole trick โœจ

Added in version 3.16: they added the CMAKE_FIND_USE_<CATEGORY> variables so you can globally disable various search locations. very slay of them.

Changed in version 4.0: the variables CMAKE_FIND_PACKAGE_SORT_ORDER and CMAKE_FIND_PACKAGE_SORT_DIRECTION now also control the order in which find_package searches directories matching the glob expression in the search paths <prefix>/<name>.framework/Versions/*/Resources/ and <prefix>/<name>.framework/Versions/*/Resources/CMake. In previous versions of CMake, this order was just... unspecified ๐Ÿซ 

Changed in version 4.2: when it hits multiple viable matches, find_package now picks the one with the most recent version by default. In previous versions of CMake, the result was unspecified ๐Ÿ˜ญ accordingly, the default of CMAKE_FIND_PACKAGE_SORT_ORDER has changed from NONE to NATURAL, and CMAKE_FIND_PACKAGE_SORT_DIRECTION now defaults to DEC (descending) instead of ASC (ascending). glow-up fr fr.


The CMake variable CMAKE_FIND_ROOT_PATH specifies one or more directories to be prepended to all other search directories. This effectively "re-roots" the entire search under given locations. Paths which are descendants of the CMAKE_STAGING_PREFIX are excluded from this re-rooting, because that variable is always a path on the host system. By default the CMAKE_FIND_ROOT_PATH is empty โ€” iconic, minimal, nothing to see here.

The CMAKE_SYSROOT variable can also be used to specify exactly one directory to use as a prefix. Setting CMAKE_SYSROOT also has other effects, so go read the documentation for that variable for more ๐Ÿ‘€

These variables are especially useful when cross-compiling to point to the root directory of the target environment โ€” and CMake will search there too. By default: first the directories listed in CMAKE_FIND_ROOT_PATH are searched, then the CMAKE_SYSROOT directory is searched, and then the non-rooted directories will be searched. You can adjust that default behavior by setting CMAKE_FIND_ROOT_PATH_MODE_PACKAGE. And you can manually override it per-call using these options:

  • CMAKE_FIND_ROOT_PATH_BOTH โ€” search in the order described above.
  • NO_CMAKE_FIND_ROOT_PATH โ€” do not use the CMAKE_FIND_ROOT_PATH variable.
  • ONLY_CMAKE_FIND_ROOT_PATH โ€” search only the re-rooted directories and directories below CMAKE_STAGING_PREFIX.

The default search order is designed to be most-specific to least-specific for common use cases. Projects may override the order by simply calling the command multiple times and using the NO_* options:

find_package (<PackageName> PATHS <paths>... NO_DEFAULT_PATH)
find_package (<PackageName>)

Once one of the calls succeeds, the result variable will be set and stored in the cache so that no call will search again. one and done, bestie ๐ŸŽ€

By default the value stored in the result variable will be the path at which the file is found. The CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS variable may be set to TRUE before calling find_package in order to resolve symbolic links and store the real path to the file.

Every non-REQUIRED find_package call can be disabled or made REQUIRED:

  • Setting the CMAKE_DISABLE_FIND_PACKAGE_<PackageName> variable to TRUE disables the package. This also disables redirection to a package provided by FetchContent.
  • Setting the CMAKE_REQUIRE_FIND_PACKAGE_<PackageName> variable to TRUE makes the package REQUIRED.
Setting both variables to TRUE simultaneously is an error ๐Ÿ˜ค pick a lane.

The CMAKE_REQUIRE_FIND_PACKAGE_<PackageName> variable takes priority over the OPTIONAL keyword in determining whether a package is required. so like, it wins. periodt.


โœจ Config Mode Version Selection โœจ

Note: when Config mode is used, this version selection process is applied regardless of whether the full or basic signature was given. no loopholes, bestie.

When the <version> argument is given, Config mode will only find a version of the package that claims compatibility with the requested version (see format specification). If the EXACT option is given, only a version of the package claiming an exact match of the requested version may be found. CMake does not establish any convention for the meaning of version numbers. it's giving "your vibes, your rules" ๐Ÿ’ซ


๐Ÿ“œ CMake-script

For CMake-script package configuration files, package version numbers are checked by "version" files provided by the packages themselves or by FetchContent. For a candidate package configuration file <config-file>.cmake, the corresponding version file is located next to it and named either <config-file>-version.cmake or <config-file>Version.cmake. If no such version file is available then the configuration file is assumed to not be compatible with any requested version ๐Ÿซถ A basic version file containing generic version matching code can be created using the CMakePackageConfigHelpers module. When a version file is found it is loaded to check the requested version number. The version file is loaded in a nested scope in which the following variables have been defined:

  • PACKAGE_FIND_NAME โ€” the <PackageName>
  • PACKAGE_FIND_VERSION โ€” full requested version string
  • PACKAGE_FIND_VERSION_MAJOR โ€” major version if requested, else 0
  • PACKAGE_FIND_VERSION_MINOR โ€” minor version if requested, else 0
  • PACKAGE_FIND_VERSION_PATCH โ€” patch version if requested, else 0
  • PACKAGE_FIND_VERSION_TWEAK โ€” tweak version if requested, else 0
  • PACKAGE_FIND_VERSION_COUNT โ€” number of version components, 0 to 4

When a version range is specified, the above version variables will hold values based on the lower end of the version range. This is to preserve compatibility with packages that have not been implemented to expect version ranges. In addition, the version range will be described by the following variables:

  • PACKAGE_FIND_VERSION_RANGE โ€” full requested version range string
  • PACKAGE_FIND_VERSION_RANGE_MIN โ€” this specifies whether the lower end point of the version range should be included or excluded. Currently, the only supported value for this variable is INCLUDE.
  • PACKAGE_FIND_VERSION_RANGE_MAX โ€” this specifies whether the upper end point of the version range should be included or excluded. The supported values for this variable are INCLUDE and EXCLUDE.
  • PACKAGE_FIND_VERSION_MIN โ€” full requested version string of the lower end point of the range
  • PACKAGE_FIND_VERSION_MIN_MAJOR โ€” major version of the lower end point if requested, else 0
  • PACKAGE_FIND_VERSION_MIN_MINOR โ€” minor version of the lower end point if requested, else 0
  • PACKAGE_FIND_VERSION_MIN_PATCH โ€” patch version of the lower end point if requested, else 0
  • PACKAGE_FIND_VERSION_MIN_TWEAK โ€” tweak version of the lower end point if requested, else 0
  • PACKAGE_FIND_VERSION_MIN_COUNT โ€” number of version components of the lower end point, 0 to 4
  • PACKAGE_FIND_VERSION_MAX โ€” full requested version string of the upper end point of the range
  • PACKAGE_FIND_VERSION_MAX_MAJOR โ€” major version of the upper end point if requested, else 0
  • PACKAGE_FIND_VERSION_MAX_MINOR โ€” minor version of the upper end point if requested, else 0
  • PACKAGE_FIND_VERSION_MAX_PATCH โ€” patch version of the upper end point if requested, else 0
  • PACKAGE_FIND_VERSION_MAX_TWEAK โ€” tweak version of the upper end point if requested, else 0
  • PACKAGE_FIND_VERSION_MAX_COUNT โ€” number of version components of the upper end point, 0 to 4

Regardless of whether a single version or a version range is specified, the variable PACKAGE_FIND_VERSION_COMPLETE will be defined and will hold the full requested version string as specified. the receipts, basically ๐Ÿงพ


The version file checks whether it satisfies the requested version and sets these variables:

  • PACKAGE_VERSION โ€” full provided version string
  • PACKAGE_VERSION_EXACT โ€” true if version is exact match
  • PACKAGE_VERSION_COMPATIBLE โ€” true if version is compatible
  • PACKAGE_VERSION_UNSUITABLE โ€” true if unsuitable as any version

These variables are checked by the find_package command to determine whether the configuration file provides an acceptable version. They are not available after the find_package call returns. If the version is acceptable, the following variables are set:

  • <PackageName>_VERSION โ€” full provided version string
  • <PackageName>_VERSION_MAJOR โ€” major version if provided, else 0
  • <PackageName>_VERSION_MINOR โ€” minor version if provided, else 0
  • <PackageName>_VERSION_PATCH โ€” patch version if provided, else 0
  • <PackageName>_VERSION_TWEAK โ€” tweak version if provided, else 0
  • <PackageName>_VERSION_COUNT โ€” number of version components, 0 to 4

...and the corresponding package configuration file is loaded. we love a happy ending ๐ŸŒธ

Note: while the exact behavior of version matching is determined by the individual package, many packages use write_basic_package_version_file() to supply this logic. The version check scripts this produces have some notable caveats with respect to version ranges:

โ€ฆso picking up right where the range talk left off, bestie ๐Ÿซถ โ€” two more little rules and then we're free:

  • The upper end of a version range acts as a hard limit on what versions will be accepted. So like, a request for version 1.4.0 might be satisfied by a package whose version is 1.6.0 and which advertises 'same major version' compatibility โ€” but that exact same package gets rejected if the requested version range is 1.4.0...1.5.0. The ceiling is the ceiling, periodt ๐Ÿ’…
  • Both ends of the version range have to match the package's advertised compatibility level. Example: if a package advertises 'same major and minor version' compatibility, then requesting the version range 1.4.0...<1.5.5 or 1.4.0...1.5.0 will result in that package being rejected, even if the package version is 1.4.1. ๐Ÿ˜ญ

As a result โ€” and this is the takeaway, write it on your hand โ€” it is not possible to use a version range to extend the range of compatible package versions that will be accepted. Nope. Not a thing. ๐Ÿšซ

๐ŸŒธ Common Package Specification ๐ŸŒธ

For Common Package Specification package configuration files, CMake checks package version numbers according to the set of recognized version schemas. Right now, these are the schemas it knows about:

  • simple โ€” version numbers are a tuple of integers followed by an optional trailing segment which gets ignored when it comes to version comparisons.
  • custom โ€” the mechanism for interpreting version numbers is unspecified. The version strings must match exactly for the package to be accepted. No vibes-based matching here, it's literally exact or nothing ๐Ÿ’€

Refer to version_schema <https://cps-org.github.io/cps/schema.html#version-schema> for a more detailed explanation of each schema and how comparisons for each are performed. Note that the specification may include schemas that are not supported by CMake โ€” so don't come crying to CMake about those, k?

On top of the package's version, CPS lets packages optionally specify a compat_version <https://cps-org.github.io/cps/schema.html#compat-version>, which is the oldest version for which the package provides compatibility. Basically the package is warranting that a consumer expecting the compat_version should be able to use the package, even if the package's actual version is newer. Iconic of it, honestly ๐Ÿ‘‘ If compat_version isn't specified, it's implicitly equal to the package version โ€” meaning no backwards compatibility is provided at all.

When a package uses a recognized schema, CMake decides whether it's acceptable according to these rules:

  • If EXACT was specified, or if the package does not supply a compat_version, the package's version must equal the requested version.
  • Otherwise:
    • The package's version must be greater than or equal to the requested (minimum) version, and
    • the package's compat_version must be less than or equal to the requested (minimum) version, and
    • if a requested maximum version was given, it must be greater than (or equal to, depending on whether the maximum version is specified as inclusive or exclusive) the package's version.
Note: this implementation of range matching was chosen in order to most closely match the behavior of write_basic_package_version_file() <#command:write_basic_package_version_file>, albeit without the case where an overly broad range matches nothing. Cute of them to keep the receipts ๐Ÿงพ

For packages using the simple version schema, if the version is acceptable, these variables get set:

  • <PackageName>_VERSION โ€” full provided version string
  • <PackageName>_VERSION_MAJOR โ€” major version if provided, else 0
  • <PackageName>_VERSION_MINOR โ€” minor version if provided, else 0
  • <PackageName>_VERSION_PATCH โ€” patch version if provided, else 0
  • <PackageName>_VERSION_TWEAK โ€” tweak version if provided, else 0
  • <PackageName>_VERSION_COUNT โ€” number of version components, non-negative

๐ŸŽ€ Package File Interface Variables ๐ŸŽ€

When it's loading a find module or a CMake-script package configuration file, find_package defines a whole lil family of variables so the file knows about the call arguments (and it restores their original state before returning, so tidy โœจ):

  • CMAKE_FIND_PACKAGE_NAME โ€” the <PackageName> which is searched for
  • <PackageName>_FIND_REQUIRED โ€” true if the REQUIRED option was given
  • <PackageName>_FIND_QUIETLY โ€” true if the QUIET option was given
  • <PackageName>_FIND_REGISTRY_VIEW โ€” the requested view if the REGISTRY_VIEW option was given
  • <PackageName>_FIND_VERSION โ€” full requested version string
  • <PackageName>_FIND_VERSION_MAJOR โ€” major version if requested, else 0
  • <PackageName>_FIND_VERSION_MINOR โ€” minor version if requested, else 0
  • <PackageName>_FIND_VERSION_PATCH โ€” patch version if requested, else 0
  • <PackageName>_FIND_VERSION_TWEAK โ€” tweak version if requested, else 0
  • <PackageName>_FIND_VERSION_COUNT โ€” number of version components, 0 to 4
  • <PackageName>_FIND_VERSION_EXACT โ€” true if the EXACT option was given
  • <PackageName>_FIND_COMPONENTS โ€” list of specified components (required and optional)
  • <PackageName>_FIND_REQUIRED_<c> โ€” true if component <c> is required, false if component <c> is optional

When a version range is specified, all those version variables above hold values based on the lower end of the range. Why? To preserve compatibility with packages that were never implemented to expect version ranges โ€” they get to keep living their peaceful little lives ๐Ÿ•Š๏ธ On top of that, the version range itself gets described by these:

  • <PackageName>_FIND_VERSION_RANGE โ€” full requested version range string
  • <PackageName>_FIND_VERSION_RANGE_MIN โ€” specifies whether the lower end point of the version range is included or excluded. Currently, INCLUDE is the only supported value.
  • <PackageName>_FIND_VERSION_RANGE_MAX โ€” specifies whether the upper end point of the version range is included or excluded. Possible values are INCLUDE or EXCLUDE.
  • <PackageName>_FIND_VERSION_MIN โ€” full requested version string of the lower end point of the range
  • <PackageName>_FIND_VERSION_MIN_MAJOR โ€” major version of the lower end point if requested, else 0
  • <PackageName>_FIND_VERSION_MIN_MINOR โ€” minor version of the lower end point if requested, else 0
  • <PackageName>_FIND_VERSION_MIN_PATCH โ€” patch version of the lower end point if requested, else 0
  • <PackageName>_FIND_VERSION_MIN_TWEAK โ€” tweak version of the lower end point if requested, else 0
  • <PackageName>_FIND_VERSION_MIN_COUNT โ€” number of version components of the lower end point, 0 to 4
  • <PackageName>_FIND_VERSION_MAX โ€” full requested version string of the upper end point of the range
  • <PackageName>_FIND_VERSION_MAX_MAJOR โ€” major version of the upper end point if requested, else 0
  • <PackageName>_FIND_VERSION_MAX_MINOR โ€” minor version of the upper end point if requested, else 0
  • <PackageName>_FIND_VERSION_MAX_PATCH โ€” patch version of the upper end point if requested, else 0
  • <PackageName>_FIND_VERSION_MAX_TWEAK โ€” tweak version of the upper end point if requested, else 0
  • <PackageName>_FIND_VERSION_MAX_COUNT โ€” number of version components of the upper end point, 0 to 4

And regardless of whether a single version or a version range was specified, the variable <PackageName>_FIND_VERSION_COMPLETE will be defined, and it holds the full requested version string as specified. Every time. No exceptions, bestie ๐Ÿ’–

In Module mode, the loaded find module is the one responsible for honoring the request detailed by these variables โ€” go read the find module for details, it's the main character of that story. In Config mode, find_package handles the REQUIRED, QUIET, and <version> options automatically, but it leaves it to the package configuration file to handle components in a way that makes sense for that package. The package configuration file may set <PackageName>_FOUND to false to tell find_package that component requirements are not satisfied. Snitching on itself, honestly, we love the honesty ๐Ÿ™ˆ

๐Ÿฆ‹ CPS Transitive Requirements ๐Ÿฆ‹

A Common Package Specification package description consists of one or more components which may in turn depend on other components, either internal or external to the package. When external components are required, the providing package gets noted as a package-level requirement of the package. And typically, the set of required components is noted inside that external package requirement too. Chain of custody, but make it fancy โœจ

Where a CMake-script package description would use the find_dependency() <#command:find_dependency> command to handle transitive dependencies, CMake handles transitive dependencies for CPS itself using an internally nested find_package call. That call can resolve CPS package dependencies either via another CPS package, or via a CMake-script package. The manner in which the CPS component dependencies are handled is subject to some caveats โ€” buckle up ๐ŸŽข

When the candidate for resolving a transitive dependency is another CPS package, things are simple; COMPONENTS and CPS "components" are directly comparable (and are effectively synonymous with CMake "imported targets"). CMake-script packages, however, are encouraged to (and often do) check that required components were found, whether or not the package describes separate components. Plus, even the ones that do describe components typically don't have the same correlation to imported targets that's normal for CPS. As a result, passing the set of required components declared by a CPS package to COMPONENTS would result in spurious failures to resolve dependencies. Not the vibe. ๐Ÿ’€

To address that: if a candidate for resolving a CPS transitive dependency is a CMake-script package, CMake passes the required components as declared by the consuming CPS package as OPTIONAL_COMPONENTS, and performs a separate, internal check that the candidate package supplied the required imported targets. Those targets must be named <PackageName>::<ComponentName>, in conformance with CPS convention โ€” otherwise the check will consider the package not found. Name your targets right or it's a wrap, iykyk ๐ŸŽ€


๐ŸŒˆ find_path ๐ŸŒˆ

Okay so here's the short-hand signature, the little snack version:

find_path (<VAR> <name> [<path>...])

And here's the general signature, the full meal, the whole entire menu:

find_path (
          <VAR>
          {<name> | NAMES <name>...}
          [HINTS {<path> | ENV <var>}...]
          [PATHS {<path> | ENV <var>}...]
          [REGISTRY_VIEW {64|32|64_32|32_64|HOST|TARGET|BOTH}]
          [PATH_SUFFIXES <suffix>...]
          [VALIDATOR <function>]
          [DOC "cache documentation string"]
          [NO_CACHE]
          [REQUIRED|OPTIONAL]
          [NO_DEFAULT_PATH]
          [NO_PACKAGE_ROOT_PATH]
          [NO_CMAKE_PATH]
          [NO_CMAKE_ENVIRONMENT_PATH]
          [NO_SYSTEM_ENVIRONMENT_PATH]
          [NO_CMAKE_SYSTEM_PATH]
          [NO_CMAKE_INSTALL_PREFIX]
          [CMAKE_FIND_ROOT_PATH_BOTH |
           ONLY_CMAKE_FIND_ROOT_PATH |
           NO_CMAKE_FIND_ROOT_PATH]
         )

So like, this command's whole job is finding a directory that has the file you named. That's it. That's the mission. ๐ŸŽฏ

Before it even starts searching, find_path does a vibe check on whether the variable <VAR> is defined. Not defined? Search happens, periodt. Defined but its value is NOTFOUND, or ends in -NOTFOUND? Search still happens, we move. But if the variable holds any other value โ€” nope, search is not performed. It's giving "we already know her, we don't need to look again." ๐Ÿ’…

Note: <VAR> counts as defined if it's available in the current scope. Peep the cmake-language(7) variables documentation for the whole tea on scopes, plus how normal variables and cache entries interact with each other. ๐Ÿซถ

Where do the results go? Into a cache entry named <VAR>, obviously. Next time find_path gets called with that same <VAR>, it'll peek at this cache entry first. This is the little optimization that means successful searches don't get repeated โ€” unless, of course, the cache entry gets unset(). ๐Ÿงš

If the file gets found in some directory, the recorded value in cache entry <VAR> is the result of the search. If nothing turns up, the recorded value is <VAR>-NOTFOUND. Sad but honest. ๐Ÿ˜ญ

Options include:

  • NAMES โ€” specify one or more possible names for the file in a directory. โœจ When you're using this to list names with and without a version suffix, the recommendation is to put the unversioned name first, so locally-built packages get found before the ones your distro handed you. Priorities, bestie.
  • HINTS, PATHS โ€” specify directories to search in addition to the default locations. The ENV var sub-option reads paths straight out of a system environment variable. ๐ŸŒธ Changed in version 3.24: on Windows platforms you can include registry queries as part of the directories, using a dedicated syntax. Those specs get straight-up ignored on every other platform. ๐Ÿ™ˆ
  • REGISTRY_VIEW โ€” Added in version 3.24. Specify which registry views must be queried. This option is only meaningful on Windows platforms and gets ignored everywhere else. When you don't specify it, the TARGET view is used when the CMP0134 policy is NEW. Go read CMP0134 for what the default view is when the policy is OLD. ๐Ÿ‘‘
    • 64 โ€” query the 64-bit registry. On 32-bit Windows, it always returns the string /REGISTRY-NOTFOUND. ๐Ÿ’€
    • 32 โ€” query the 32-bit registry.
    • 64_32 โ€” query both views (64 and 32) and generate a path for each.
    • 32_64 โ€” query both views (32 and 64) and generate a path for each.
    • HOST โ€” query the registry matching the architecture of the host: 64 on 64-bit Windows and 32 on 32-bit Windows.
    • TARGET โ€” query the registry matching the architecture specified by the CMAKE_SIZEOF_VOID_P variable. If that's not defined, fall back to the HOST view.
    • BOTH โ€” query both views (32 and 64). The order depends on these rules: if CMAKE_SIZEOF_VOID_P is defined, use the following view depending on the content of that variable:
      1. 8: 64_32
      2. 4: 32_64

If CMAKE_SIZEOF_VOID_P is not defined, rely on the architecture of the host:

  1. 64-bit: 64_32
  2. 32-bit: 32
  • PATH_SUFFIXES โ€” specify additional subdirectories to check below each directory location that would otherwise be considered. Little bonus layers. ๐ŸŽ€
  • VALIDATOR โ€” Added in version 3.25. Specify a function() to be called for each candidate item found (you cannot hand it a macro() โ€” that results in an error, no cap). Two arguments get passed to the validator function: the name of a result variable, and the absolute path to the candidate item. The item is accepted and the search ends unless the function sets the value in the result variable to false in the calling scope. The result variable holds a true value when the validator function is entered. ๐Ÿฆ‹
function(my_check validator_result_var item)
  if(NOT item MATCHES ...)
    set(${validator_result_var} FALSE PARENT_SCOPE)
  endif()
endfunction()

find_path (result NAMES ... VALIDATOR my_check)

Keep in mind: if a cached result is used, the search is skipped and any VALIDATOR is ignored. The cached result is not required to pass the validation function. Iconic loophole, honestly. ๐Ÿ’…

  • DOC โ€” specify the documentation string for the <VAR> cache entry. ๐Ÿ“
  • NO_CACHE โ€” Added in version 3.21. The result of the search gets stored in a normal variable rather than a cache entry.
Note: find_path will still check for <VAR> as usual โ€” checking first for a variable, then a cache entry. If either indicates a previous successful search, the search will not be performed. Warning: use this option with caution, because it can greatly increase the cost of repeated configure steps. Like, greatly. โš ๏ธ
  • REQUIRED โ€” Added in version 3.18. Stop processing with an error message if nothing is found; otherwise the search gets attempted again the next time find_path is invoked with the same variable. Added in version 4.1: every find_path command is treated as REQUIRED when the CMAKE_FIND_REQUIRED variable is enabled. ๐Ÿ”ฅ
  • OPTIONAL โ€” Added in version 4.1. Ignore the value of CMAKE_FIND_REQUIRED and continue without an error message if nothing is found. Incompatible with REQUIRED. Pick a lane, babe. โญ

If NO_DEFAULT_PATH is specified, then no additional paths get added to the search. Full stop. If NO_DEFAULT_PATH is not specified, the search process goes like this:

  1. If it's called from within a find module or any other script loaded by a call to find_package(<PackageName>), search the prefixes unique to the current package being found. See policy CMP0074. Added in version 3.12. Specifically, search paths specified by the following variables, in order:
    1. <PackageName>_ROOT CMake variable, where <PackageName> is the case-preserved package name.
    2. <PACKAGENAME>_ROOT CMake variable, where <PACKAGENAME> is the upper-cased package name. See policy CMP0144. Added in version 3.27.
    3. <PackageName>_ROOT environment variable, where <PackageName> is the case-preserved package name.
    4. <PACKAGENAME>_ROOT environment variable, where <PACKAGENAME> is the upper-cased package name. See policy CMP0144. Added in version 3.27.

The package root variables are maintained as a stack, so if you're called from nested find modules or config packages, root paths from the parent's find module or config package get searched after the paths from the current module or package. In other words, the search order would be <CurrentPackage>_ROOT, ENV{<CurrentPackage>_ROOT}, <ParentPackage>_ROOT, ENV{<ParentPackage>_ROOT}, etc. You can skip all this if NO_PACKAGE_ROOT_PATH is passed, or by setting CMAKE_FIND_USE_PACKAGE_ROOT_PATH to FALSE. ๐Ÿซถ

  • <prefix>/include/<arch> if CMAKE_LIBRARY_ARCHITECTURE is set, and <prefix>/include for each <prefix> in the <PackageName>_ROOT CMake variable and the <PackageName>_ROOT environment variable if called from within a find module loaded by find_package(<PackageName>)
  1. Search paths specified in cmake-specific cache variables. These are intended to be used on the command line with a -DVAR=value. The values are interpreted as semicolon-separated lists. You can skip this if NO_CMAKE_PATH is passed, or by setting CMAKE_FIND_USE_CMAKE_PATH to FALSE. ๐ŸŒˆ
    • <prefix>/include/<arch> if CMAKE_LIBRARY_ARCHITECTURE is set, and <prefix>/include for each <prefix> in CMAKE_PREFIX_PATH
    • CMAKE_INCLUDE_PATH
    • CMAKE_FRAMEWORK_PATH
  2. Search paths specified in cmake-specific environment variables. These are meant to be set in the user's shell configuration, and therefore use the host's native path separator (; on Windows and : on UNIX). You can skip this if NO_CMAKE_ENVIRONMENT_PATH is passed, or by setting CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH to FALSE. ๐ŸŒธ
    • <prefix>/include/<arch> if CMAKE_LIBRARY_ARCHITECTURE is set, and <prefix>/include for each <prefix> in CMAKE_PREFIX_PATH
    • CMAKE_INCLUDE_PATH
    • CMAKE_FRAMEWORK_PATH
  3. Search the paths specified by the HINTS option. These should be paths computed by system introspection โ€” like a hint provided by the location of another item you already found. Hard-coded guesses belong in the PATHS option, bestie. ๐Ÿ’œ
  4. Search the standard system environment variables. You can skip this if NO_SYSTEM_ENVIRONMENT_PATH is passed, or by setting CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH to FALSE. ๐ŸŽ€
    • The directories in INCLUDE and PATH.

On Windows hosts, CMake 3.3 through 3.27 searched additional paths: <prefix>/include/<arch> if CMAKE_LIBRARY_ARCHITECTURE is set, and <prefix>/include for each <prefix>/[s]bin in PATH, and <entry>/include for other entries in PATH. This behavior was removed by CMake 3.28. Rip to a real one. ๐Ÿ’€

  1. Now it goes digging in the cmake variables defined in the Platform files for your current system. โœจ The search of CMAKE_INSTALL_PREFIX and CMAKE_STAGING_PREFIX can be skipped if you pass NO_CMAKE_INSTALL_PREFIX, or by setting the CMAKE_FIND_USE_INSTALL_PREFIX variable to FALSE. And like, all of these locations can be skipped if you pass NO_CMAKE_SYSTEM_PATH, or by setting CMAKE_FIND_USE_CMAKE_SYSTEM_PATH to FALSE. ๐Ÿ’…
    • <prefix>/include/<arch> if CMAKE_LIBRARY_ARCHITECTURE is set, and <prefix>/include for each <prefix> in CMAKE_SYSTEM_PREFIX_PATH
    • CMAKE_SYSTEM_INCLUDE_PATH
    • CMAKE_SYSTEM_FRAMEWORK_PATH

The platform paths that these variables contain are locations that typically include installed software. An example being /usr/local for UNIX based platforms. ๐Ÿ‘‘

  1. Search the paths specified by the PATHS option or in the short-hand version of the command. These are typically hard-coded guesses. ๐Ÿ™ˆ

The CMAKE_IGNORE_PATH, CMAKE_IGNORE_PREFIX_PATH, CMAKE_SYSTEM_IGNORE_PATH and CMAKE_SYSTEM_IGNORE_PREFIX_PATH variables can also cause some of the above locations to be ignored. (Yes bestie, you can yeet a whole search location. blocking, muted, unfollowed. ๐Ÿ’€)

Added in version 3.16: Added CMAKE_FIND_USE_<CATEGORY>_PATH variables to globally disable various search locations. ๐ŸŽ€

On macOS the CMAKE_FIND_FRAMEWORK and CMAKE_FIND_APPBUNDLE variables determine the order of preference between Apple-style and unix-style package components. ๐Ÿ“

The CMake variable CMAKE_FIND_ROOT_PATH specifies one or more directories to be prepended to all other search directories. This effectively "re-roots" the entire search under given locations. Paths which are descendants of the CMAKE_STAGING_PREFIX are excluded from this re-rooting, because that variable is always a path on the host system. By default the CMAKE_FIND_ROOT_PATH is empty. ๐Ÿฆ‹

The CMAKE_SYSROOT variable can also be used to specify exactly one directory to use as a prefix. Setting CMAKE_SYSROOT also has other effects. See the documentation for that variable for more. ๐Ÿ‘€

These variables are especially useful when cross-compiling to point to the root directory of the target environment and CMake will search there too. By default at first the directories listed in CMAKE_FIND_ROOT_PATH are searched, then the CMAKE_SYSROOT directory is searched, and then the non-rooted directories will be searched. The default behavior can be adjusted by setting CMAKE_FIND_ROOT_PATH_MODE_INCLUDE. This behavior can be manually overridden on a per-call basis using options:

  • CMAKE_FIND_ROOT_PATH_BOTH โ€” Search in the order described above.
  • NO_CMAKE_FIND_ROOT_PATH โ€” Do not use the CMAKE_FIND_ROOT_PATH variable.
  • ONLY_CMAKE_FIND_ROOT_PATH โ€” Search only the re-rooted directories and directories below CMAKE_STAGING_PREFIX.

The default search order is designed to be most-specific to least-specific for common use cases. Projects may override the order by simply calling the command multiple times and using the NO_* options:

find_path (<VAR> NAMES name PATHS <paths>... NO_DEFAULT_PATH)
find_path (<VAR> NAMES name)

Once one of the calls succeeds the result variable will be set and stored in the cache so that no call will search again. ๐Ÿ’– (one and done, she's not doing overtime)

When searching for frameworks, if the file is specified as A/b.h, then the framework search will look for A.framework/Headers/b.h. If that is found the path will be set to the path to the framework. CMake will convert this to the correct -F option to include the file. ๐Ÿงš


find_program ๐Ÿ”

A short-hand signature is:

find_program (<VAR> <name> [<path>...])

The general signature is:

find_program (
          <VAR>
          {<name> | NAMES <name>... [NAMES_PER_DIR]}
          [HINTS {<path> | ENV <var>}...]
          [PATHS {<path> | ENV <var>}...]
          [REGISTRY_VIEW {64|32|64_32|32_64|HOST|TARGET|BOTH}]
          [PATH_SUFFIXES <suffix>...]
          [VALIDATOR <function>]
          [DOC "cache documentation string"]
          [NO_CACHE]
          [REQUIRED|OPTIONAL]
          [NO_DEFAULT_PATH]
          [NO_PACKAGE_ROOT_PATH]
          [NO_CMAKE_PATH]
          [NO_CMAKE_ENVIRONMENT_PATH]
          [NO_SYSTEM_ENVIRONMENT_PATH]
          [NO_CMAKE_SYSTEM_PATH]
          [NO_CMAKE_INSTALL_PREFIX]
          [CMAKE_FIND_ROOT_PATH_BOTH |
           ONLY_CMAKE_FIND_ROOT_PATH |
           NO_CMAKE_FIND_ROOT_PATH]
         )

This command is used to find a program. ๐Ÿซถ Like, literally its whole job.

Prior to searching, find_program checks if variable <VAR> is defined. If the variable is not defined, the search will be performed. If the variable is defined and its value is NOTFOUND, or ends in -NOTFOUND, the search will be performed. If the variable contains any other value the search is not performed. โœจ

Note: <VAR> is considered defined if it is available in the current scope. See the cmake-language(7) variables documentation for details on scopes, and the interaction of normal variables and cache entries. ๐Ÿ“š

The results of the search will be stored in a cache entry named <VAR>. Future calls to find_program will inspect this cache entry when specifying the same <VAR>. This optimization ensures successful searches will not be repeated unless the cache entry is unset(). (slay, no redundant work ๐Ÿ’…)

If the program is found the recorded value in cache entry <VAR> will be the result of the search. If nothing is found, the recorded value will be <VAR>-NOTFOUND. ๐Ÿ˜ญ

Options include:

  • NAMES โ€” Specify one or more possible names for the program.

When using this to specify names with and without a version suffix, we recommend specifying the unversioned name first so that locally-built packages can be found before those provided by distributions. ๐Ÿ‘‘

  • HINTS, PATHS โ€” Specify directories to search in addition to the default locations. The ENV var sub-option reads paths from a system environment variable. ๐ŸŒˆ
Changed in version 3.24: On Windows platform, it is possible to include registry queries as part of the directories, using a dedicated syntax. Such specifications will be ignored on all other platforms. ๐Ÿ’€
  • REGISTRY_VIEW โ€” Added in version 3.24. Specify which registry views must be queried. This option is only meaningful on Windows platforms and will be ignored on other ones. When not specified, the BOTH view is used when the CMP0134 policy is NEW. Refer to CMP0134 for the default view when the policy is OLD. ๐ŸชŸ
    • 64 โ€” Query the 64-bit registry. On 32-bit Windows, it always returns the string /REGISTRY-NOTFOUND.
    • 32 โ€” Query the 32-bit registry.
    • 64_32 โ€” Query both views (64 and 32) and generate a path for each.
    • 32_64 โ€” Query both views (32 and 64) and generate a path for each.
    • HOST โ€” Query the registry matching the architecture of the host: 64 on 64-bit Windows and 32 on 32-bit Windows.
    • TARGET โ€” Query the registry matching the architecture specified by the CMAKE_SIZEOF_VOID_P variable. If not defined, fall back to HOST view.
    • BOTH โ€” Query both views (32 and 64). The order depends on the following rules: If the CMAKE_SIZEOF_VOID_P variable is defined, use the following view depending on the content of this variable:
      • 8: 64_32
      • 4: 32_64

If the CMAKE_SIZEOF_VOID_P variable is not defined, rely on the architecture of the host:

  • 64-bit: 64_32
  • 32-bit: 32
  • PATH_SUFFIXES โ€” Specify additional subdirectories to check below each directory location otherwise considered. ๐ŸŽ€
  • VALIDATOR โ€” Added in version 3.25. Specify a function() to be called for each candidate item found (a macro() cannot be provided, that will result in an error). Two arguments will be passed to the validator function: the name of a result variable, and the absolute path to the candidate item. The item will be accepted and the search will end unless the function sets the value in the result variable to false in the calling scope. The result variable will hold a true value when the validator function is entered. ๐Ÿ•ต๏ธโ€โ™€๏ธ
function(my_check validator_result_var item)
  if(NOT item MATCHES ...)
    set(${validator_result_var} FALSE PARENT_SCOPE)
  endif()
endfunction()

find_program (result NAMES ... VALIDATOR my_check)

Note that if a cached result is used, the search is skipped and any VALIDATOR is ignored. The cached result is not required to pass the validation function. ๐Ÿ’€

  • DOC โ€” Specify the documentation string for the <VAR> cache entry. ๐Ÿ“
  • NO_CACHE โ€” Added in version 3.21. The result of the search will be stored in a normal variable rather than a cache entry.
Note: find_program will still check for <VAR> as usual, checking first for a variable, and then a cache entry. If either indicate a previous successful search, the search will not be performed. ๐Ÿ‘€
Warning: This option should be used with caution because it can greatly increase the cost of repeated configure steps. ๐Ÿ˜ญ (she's expensive, bestie)
  • REQUIRED โ€” Added in version 3.18. Stop processing with an error message if nothing is found, otherwise the search will be attempted again the next time find_program is invoked with the same variable. ๐Ÿ”ฅ
  • Added in version 4.1: every find_program call lowkey gets treated as REQUIRED when the CMAKE_FIND_REQUIRED variable is enabled. โœจ
  • OPTIONAL โ€” added in version 4.1. This one's the chill one: it ignores whatever CMAKE_FIND_REQUIRED is set to and just keeps going with no error message if nothing turns up. ๐Ÿซถ Heads up tho, it's incompatible with REQUIRED โ€” they cannot both be in the group chat.

If NO_DEFAULT_PATH is specified, then no extra paths get added to the search. Periodt. If NO_DEFAULT_PATH is not specified, the search goes like this, in order:

  1. If it's called from inside a find module or any other script loaded by a call to find_package(<PackageName>), it searches the prefixes that are unique to the current package being found. See policy CMP0074. ๐Ÿงš

Added in version 3.12.

Specifically, it searches paths specified by these variables, in order:

  1. The <PackageName>_ROOT CMake variable, where <PackageName> is the case-preserved package name.
  2. The <PACKAGENAME>_ROOT CMake variable, where <PACKAGENAME> is the upper-cased package name. See policy CMP0144.

Added in version 3.27.

  1. The <PackageName>_ROOT environment variable, where <PackageName> is the case-preserved package name.
  2. The <PACKAGENAME>_ROOT environment variable, where <PACKAGENAME> is the upper-cased package name. See policy CMP0144.

Added in version 3.27.

The package root variables are maintained as a stack, so if you're called from nested find modules or config packages, root paths from the parent's find module or config package get searched after the paths from the current module or package. In other words, the search order is <CurrentPackage>_ROOT, ENV{<CurrentPackage>_ROOT}, <ParentPackage>_ROOT, ENV{<ParentPackage>_ROOT}, etc. You can skip this whole vibe by passing NO_PACKAGE_ROOT_PATH or by setting CMAKE_FIND_USE_PACKAGE_ROOT_PATH to FALSE.

  • <prefix>/[s]bin for each <prefix> in the <PackageName>_ROOT CMake variable and the <PackageName>_ROOT environment variable, if it's called from within a find module loaded by find_package(<PackageName>) ๐Ÿฆ‹
  1. Search the paths specified in CMake-specific cache variables. These are meant to be used on the command line with -DVAR=value. The values are interpreted as semicolon-separated lists. You can skip this if NO_CMAKE_PATH is passed or by setting CMAKE_FIND_USE_CMAKE_PATH to FALSE.
    • <prefix>/[s]bin for each <prefix> in CMAKE_PREFIX_PATH
    • CMAKE_PROGRAM_PATH
    • CMAKE_APPBUNDLE_PATH
  2. Search the paths specified in CMake-specific environment variables. These are meant to be set in the user's shell configuration, and therefore they use the host's native path separator (; on Windows and : on UNIX). You can skip this if NO_CMAKE_ENVIRONMENT_PATH is passed or by setting CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH to FALSE.
    • <prefix>/[s]bin for each <prefix> in CMAKE_PREFIX_PATH
    • CMAKE_PROGRAM_PATH
    • CMAKE_APPBUNDLE_PATH
  3. Search the paths specified by the HINTS option. These should be paths computed by system introspection โ€” like, a hint provided by the location of another item you already found. Hard-coded guesses belong in the PATHS option, bestie. ๐Ÿ’…
  4. Search the standard system environment variables. You can skip this if NO_SYSTEM_ENVIRONMENT_PATH is passed or by setting CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH to FALSE.
    • The directories in PATH itself.
  5. Search CMake variables defined in the Platform files for the current system. The search of CMAKE_INSTALL_PREFIX and CMAKE_STAGING_PREFIX can be skipped if NO_CMAKE_INSTALL_PREFIX is passed or by setting CMAKE_FIND_USE_INSTALL_PREFIX to FALSE. And, like, all of these locations can be skipped if NO_CMAKE_SYSTEM_PATH is passed or by setting CMAKE_FIND_USE_CMAKE_SYSTEM_PATH to FALSE. ๐ŸŒˆ
    • <prefix>/[s]bin for each <prefix> in CMAKE_SYSTEM_PREFIX_PATH
    • CMAKE_SYSTEM_PROGRAM_PATH
    • CMAKE_SYSTEM_APPBUNDLE_PATH

The platform paths these variables contain are locations that typically include installed software. An example being /usr/local for UNIX based platforms. ๐Ÿ‘‘

  1. Search the paths specified by the PATHS option or in the short-hand version of the command. These are typically hard-coded guesses.

The CMAKE_IGNORE_PATH, CMAKE_IGNORE_PREFIX_PATH, CMAKE_SYSTEM_IGNORE_PATH and CMAKE_SYSTEM_IGNORE_PREFIX_PATH variables can also cause some of the above locations to be ignored. ๐Ÿ™ˆ

Added in version 3.16: the CMAKE_FIND_USE_<CATEGORY>_PATH variables got added so you can globally disable various search locations.

On macOS, the CMAKE_FIND_FRAMEWORK and CMAKE_FIND_APPBUNDLE variables decide the order of preference between Apple-style and unix-style package components. ๐Ÿ“

The CMake variable CMAKE_FIND_ROOT_PATH specifies one or more directories to be prepended to all other search directories. This effectively "re-roots" the entire search under the given locations. Paths which are descendants of CMAKE_STAGING_PREFIX are excluded from this re-rooting, because that variable is always a path on the host system. By default CMAKE_FIND_ROOT_PATH is empty.

The CMAKE_SYSROOT variable can also be used to specify exactly one directory to use as a prefix. Setting CMAKE_SYSROOT also has other effects โ€” see the documentation for that variable for more. ๐Ÿงš

These variables are super useful when cross-compiling, to point at the root directory of the target environment, and CMake will search there too. By default, at first the directories listed in CMAKE_FIND_ROOT_PATH are searched, then the CMAKE_SYSROOT directory is searched, and then the non-rooted directories get searched. The default behavior can be adjusted by setting CMAKE_FIND_ROOT_PATH_MODE_PROGRAM. And you can manually override this behavior per-call using these options:

  • CMAKE_FIND_ROOT_PATH_BOTH โ€” search in the order described above.
  • NO_CMAKE_FIND_ROOT_PATH โ€” do not use the CMAKE_FIND_ROOT_PATH variable.
  • ONLY_CMAKE_FIND_ROOT_PATH โ€” search only the re-rooted directories and directories below CMAKE_STAGING_PREFIX.

The default search order is designed to be most-specific to least-specific for common use cases. Projects can override the order by simply calling the command multiple times and using the NO_* options:

find_program (<VAR> NAMES name PATHS <paths>... NO_DEFAULT_PATH)
find_program (<VAR> NAMES name)

Once one of the calls succeeds, the result variable gets set and stored in the cache so that no call will search again. One and done, slay. ๐Ÿ’…

When more than one value is given to the NAMES option, this command by default considers one name at a time and searches every directory for it. The NAMES_PER_DIR option tells this command to flip it: consider one directory at a time and search for all the names in it. ๐Ÿ”ฅ

The set of files considered to be programs is platform-specific:

  • On Windows, filename suffixes are considered in order .com, .exe, and no suffix.
  • On non-Windows systems, no filename suffix is considered, but files must have execute permission (see policy CMP0109). โญ

To search for scripts, specify an extension explicitly:

if(WIN32)
  set(_script_suffix .bat)
else()
  set(_script_suffix .sh)
endif()

find_program(MY_SCRIPT NAMES my_script${_script_suffix})

๐ŸŒ€ foreach ๐ŸŒ€

Evaluate a group of commands for each value in a list. Basically a glow-up for "do the thing again but with the next item." โœจ

foreach(<loop_var> <items>)
  <commands>
endforeach()

where <items> is a list of items separated by semicolon or whitespace. All commands between foreach and the matching endforeach are recorded without being invoked. Once the endforeach is evaluated, that recorded list of commands is invoked once for each item in <items>. At the beginning of each iteration, the variable <loop_var> gets set to the value of the current item. ๐ŸŽ€

The scope of <loop_var> is restricted to the loop scope. See policy CMP0124 for the details.

The commands break() and continue() are your escape hatches from the normal control flow. ๐Ÿฆ‹

Per legacy, the endforeach() command admits an optional <loop_var> argument. If you use it, it must be a verbatim repeat of the argument of the opening foreach command. No creative liberty. ๐Ÿ˜ญ

foreach(<loop_var> RANGE <stop>)

In this variant, foreach iterates over the numbers 0, 1, ... up to (and including) the nonnegative integer <stop>.

foreach(<loop_var> RANGE <start> <stop> [<step>])

In this variant, foreach iterates over the numbers from <start> up to at most <stop> in steps of <step>. If <step> is not specified, then the step size is 1. The three arguments <start> <stop> <step> must all be nonnegative integers, and <stop> must not be smaller than <start>; otherwise you enter the danger zone ๐Ÿ’€ of undocumented behavior that may change in future releases.

foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS [<items>]])

okay so in this variant, <lists> is a whitespace- or semicolon-separated list of list-valued variables, and foreach just iterates over every single item in every single one of those lists, one by one ๐Ÿ’… The <items> that come after the ITEMS keyword get processed exactly like in the first variant of foreach. And fr fr, LISTS A and ITEMS ${A} are literally the same thing. But here's the tea: if you don't give any <lists> or <items>, the body of the loop never runs โ€” it's processed as empty, bestie ๐Ÿซถ

Here's how the LISTS option gets processed, watch the magic:

set(A 0;1)
set(B 2 3)
set(C "4 5")
set(D 6;7 8)
set(E "")
foreach(X IN LISTS A B C D E)
    message(STATUS "X=${X}")
endforeach()

and it yields:

-- X=0
-- X=1
-- X=2
-- X=3
-- X=4 5
-- X=6
-- X=7
-- X=8
not the "4 5" staying glued together as one item ๐Ÿ˜ญ that's the whole point, she's quoting it
foreach(<loop_var>... IN ZIP_LISTS <lists>)

โœจ Added in version 3.17.

In this variant, <lists> is again a whitespace- or semicolon-separated list of list-valued variables. The foreach command iterates over each list simultaneously, setting the iteration variables like this:

  • if you pass a single loop_var, it sets a whole series of loop_var_N variables to the current item from the corresponding list ๐Ÿ‘‘
  • if you pass multiple variable names, it sets each variable to the current item from the corresponding list. The number of iteration variables has to match the number of list variables โ€” no cap, that's the rule ๐Ÿ’€

If no <lists> are given, the body of the loop will never be executed (i.e., it's processed as empty). Same energy as before.

Here's ZIP_LISTS in action:

list(APPEND English one two three four)
list(APPEND Bahasa satu dua tiga)

foreach(num IN ZIP_LISTS English Bahasa)
    message(STATUS "num_0=${num_0}, num_1=${num_1}")
endforeach()

foreach(en ba IN ZIP_LISTS English Bahasa)
    message(STATUS "en=${en}, ba=${ba}")
endforeach()

yields:

-- num_0=one, num_1=satu
-- num_0=two, num_1=dua
-- num_0=three, num_1=tiga
-- num_0=four, num_1=
-- en=one, ba=satu
-- en=two, ba=dua
-- en=three, ba=tiga
-- en=four, ba=
see how English has four things and Bahasa only has three? The fourth round just leaves the Bahasa side empty. She zips as far as she can, periodt ๐ŸŒธ

See Also

  • break() <#command:break>
  • continue() <#command:continue>
  • endforeach() <#command:endforeach>
  • while() <#command:while>

function

Start recording a function for later invocation as a command. It's giving "write it down now, run it later" ๐Ÿ“

function(<name> [<arg1> ...])
  <commands>
endfunction()

Defines a function named <name> that takes arguments named <arg1>, ... The <commands> in the function definition are recorded โ€” nobody runs them until the function actually gets invoked. Iconic lazy-loading behaviour honestly โœจ

Per legacy, the endfunction() <#command:endfunction> command admits an optional <name> argument. If you use it, it has to be a verbatim repeat of the argument of the opening function command. No paraphrasing, bestie ๐Ÿ™ˆ

A function opens a new scope: see set(var PARENT_SCOPE) <#command:set> for the deets.

Pop over to the cmake_policy() <#command:cmake_policy> command docs for how policies behave inside functions.

And check the macro() <#command:macro> command docs for the differences between CMake functions and macros โ€” they are not the same vibe, iykyk ๐Ÿ’…

Invocation

The function invocation is case-insensitive. A function defined as

function(foo)
  <commands>
endfunction()

can be invoked through any of

foo()
Foo()
FOO()
cmake_language(CALL foo)

and so on. But like, it is strongly recommended to stay with the case chosen in the function definition. Typically functions use all-lowercase names. Consistency ate, fr fr ๐ŸŽ€

Added in version 3.18: The cmake_language(CALL ...) <#command:cmake_language> command can also be used to invoke the function.

Arguments

When the function is invoked, the recorded <commands> are first modified by replacing formal parameters (${arg1}, ...) with the arguments passed, and then invoked as normal commands ๐Ÿ”ฅ

On top of referencing the formal parameters, you can reference the ARGC variable, which gets set to the number of arguments passed into the function, plus ARGV0, ARGV1, ARGV2, ... which hold the actual values of the arguments passed in. This is literally how you make functions with optional arguments. Chef's kiss ๐Ÿ‘‘

Furthermore, ARGV holds the list of all arguments given to the function, and ARGN holds the list of arguments past the last expected argument. Big main character energy for varargs girlies ๐Ÿฆ‹

โš ๏ธ Referencing ARGV# arguments beyond ARGC has undefined behavior. Deadass. Checking that ARGC is greater than # is the only way to ensure that ARGV# was passed to the function as an extra argument. Don't skip the check ๐Ÿ’€

See Also

  • cmake_parse_arguments() <#command:cmake_parse_arguments>
  • endfunction() <#command:endfunction>
  • return() <#command:return>

get_cmake_property

Get a global property of the CMake instance. Global. Everywhere. All at once ๐ŸŒˆ

get_cmake_property(<variable> <property>)

Gets a global property from the CMake instance. The value of the <property> gets stored in the specified <variable>. If the property isn't found, <variable> will be set to NOTFOUND โ€” sad but honest ๐Ÿ˜ญ

See the cmake-properties(7) <#manual:cmake-properties(7)> manual for the available properties.

On top of global properties, this command (for historical reasons, i.e. she's a legacy icon) also supports the VARIABLES <#prop_dir:VARIABLES> and MACROS <#prop_dir:MACROS> directory properties. She also supports a special COMPONENTS global property that lists the components given to the install() <#command:install> command. Versatile queen ๐Ÿ’…

See Also

  • the get_property() <#command:get_property> command's GLOBAL option

get_directory_property

Get a property of DIRECTORY scope. Very "this folder specifically" of her ๐ŸŽ€

get_directory_property(<variable> [DIRECTORY <dir>] <prop-name>)

Stores a property of directory scope in the named <variable>.

The DIRECTORY argument specifies another directory to pull the property value from instead of the current directory. Relative paths are treated as relative to the current source directory. CMake must already know about that directory โ€” either because it was added through a call to add_subdirectory() <#command:add_subdirectory>, or because it's the top level directory. No sneaking in strangers ๐Ÿšซ

Added in version 3.19: <dir> may reference a binary directory.

If the property isn't defined for the nominated directory scope, you get an empty string back. And for INHERITED properties, if the property isn't found for the nominated directory scope, the search chains up to a parent scope, exactly as described for the define_property() <#command:define_property> command. She goes looking upstairs ๐Ÿงš

get_directory_property(<variable> [DIRECTORY <dir>]
                       DEFINITION <var-name>)

Get a variable definition from a directory. This form hits different when you need a variable definition from another directory ๐Ÿฆ‹

See Also

  • define_property() <#command:define_property>
  • the more general get_property() <#command:get_property> command

get_filename_component

Get a specific component of a full filename. Path surgery, but make it cute โœ‚๏ธ

Changed in version 3.20: This command has been superseded by the cmake_path() <#command:cmake_path> command โ€” except for REALPATH, which is now offered by file(REAL_PATH) <#real-path>, and PROGRAM, now available in separate_arguments(PROGRAM) <#command:separate_arguments>. She's not cancelled, she's just... resting ๐Ÿ’…

Changed in version 3.24: The undocumented feature offering the capability to query the Windows registry is superseded by the cmake_host_system_information(QUERY WINDOWS_REGISTRY) <#query-windows-registry> command.

get_filename_component(<var> <FileName> <mode> [CACHE])

Sets <var> to a component of <FileName>, where <mode> is one of:

  • DIRECTORY โ€” directory without file name.
  • NAME โ€” file name without directory.
  • EXT โ€” file name longest extension (.b.c from d/a.b.c).
  • NAME_WE โ€” file name with neither the directory nor the longest extension.
  • LAST_EXT โ€” file name last extension (.c from d/a.b.c).
  • NAME_WLE โ€” file name with neither the directory nor the last extension.
  • PATH โ€” legacy alias for DIRECTORY (use for CMake <= 2.8.11).

Added in version 3.14: Added the LAST_EXT and NAME_WLE modes. New modes just dropped โœจ

Paths are returned with forward slashes and have no trailing slashes. If the optional CACHE argument is specified, the result variable gets added to the cache. Organized ๐Ÿ’–

get_filename_component(<var> <FileName> <mode> [BASE_DIR <dir>] [CACHE])

Added in version 3.4.

Sets <var> to the absolute path of <FileName>, where <mode> is one of:

  • ABSOLUTE โ€” full path to file.
  • REALPATH โ€” full path to existing file with symlinks resolved.

If the <FileName> you hand her is a relative path, it gets evaluated relative to the given base directory <dir>. If no base directory is provided, the default base directory is CMAKE_CURRENT_SOURCE_DIR <#variable:CMAKE_CURRENT_SOURCE_DIR>. Default behaviour, but make it documented ๐Ÿซถ

Paths are returned with forward slashes and have no trailing slashes. If the optional CACHE argument is specified, the result variable gets added to the cache.

get_filename_component(<var> <FileName> PROGRAM [PROGRAM_ARGS <arg_var>] [CACHE])

The program in <FileName> will be found in the system search path, or left as a full path. If PROGRAM_ARGS is present alongside PROGRAM, then any command-line arguments present in the <FileName> string get split away from the program name and stored in <arg_var>. This is how you separate a program name from its arguments inside a command line string. She does the splitting so you don't have to ๐Ÿ“

See Also

  • cmake_path() <#command:cmake_path>

get_property

Get a property. That's it. That's the whole description, and honestly? Slay for the brevity ๐Ÿ‘‘

get_property(<variable>
             <GLOBAL                                                  |
              DIRECTORY [<dir>]                                       |
              TARGET    <target>                                      |
              FILE_SET  <file_set> TARGET <target>                    |
              SOURCE    <source>
                        [DIRECTORY <dir> | TARGET_DIRECTORY <target>] |
              INSTALL   <file>                                        |
              TEST      <test>
                        [DIRECTORY <dir>]                             |
              CACHE     <entry>                                       |
              VARIABLE>
             PROPERTY <name>
             [SET | DEFINED | BRIEF_DOCS | FULL_DOCS])

Gets one property from one object in a scope. One. Singular. Don't be greedy ๐ŸŽ€

The first argument specifies the variable where the result gets stored. The second argument determines the scope you're pulling the property from. It has to be one of the following:

GLOBAL : Scope is unique and does not accept a name. No name needed, she's everything already ๐Ÿ’…

DIRECTORY : Scope defaults to the current directory, but another directory (one already processed by CMake) may be named by the full or relative path <dir>. Relative paths are treated as relative to the current source directory. See also the get_directory_property() <#command:get_directory_property> command.

  • Added in version 3.19: <dir> can reference a binary directory too ๐Ÿซถ
  • TARGET โ€” scope has to name one target that actually exists. Also peep the get_target_property() command ๐Ÿ‘€
  • FILE_SET โ€” Added in version 4.3. Scope must name one existing file set.

This next option is required, no skipping bestie:

  • TARGET <target> โ€” the target that the file set is attached to.
  • SOURCE โ€” scope must name one source file. Default behavior: the source file's property gets read from the current source directory's scope.

Added in version 3.18: the directory scope can be overridden with one of these sub-options ๐Ÿ’…

  • DIRECTORY <dir> โ€” the source file property gets read from the <dir> directory's scope. CMake has to already know about that directory, either because it was added through a call to add_subdirectory() or because <dir> is the top level directory. Relative paths are treated as relative to the current source directory.

Added in version 3.19: <dir> may reference a binary directory.

  • TARGET_DIRECTORY <target> โ€” the source file property gets read from the directory scope in which <target> was created (so <target> must already exist, periodt).

Also check out the get_source_file_property() command.

  • INSTALL โ€” Added in version 3.1. Scope must name one installed file path.
  • TEST โ€” scope must name one existing test. Also see the get_test_property() command.

Added in version 3.28: the directory scope can be overridden with this sub-option:

  • DIRECTORY <dir> โ€” the test property gets read from the <dir> directory's scope. CMake must already know about the directory, either by having added it through a call to add_subdirectory() or because <dir> is the top level directory. Relative paths are treated as relative to the current source directory. <dir> may reference a binary directory.
  • CACHE โ€” scope must name one cache entry.
  • VARIABLE โ€” this scope is unique and does not accept a name.

The required PROPERTY option is immediately followed by the name of the property you're getting. If the property isn't set, the named <variable> will be unset in the calling scope when it returns โ€” although some properties support inheriting from a parent scope if they're defined to behave that way (see define_property()) ๐Ÿซถ

If the SET option is given, the variable gets set to a boolean value indicating whether the property has been set. If the DEFINED option is given, the variable gets set to a boolean value indicating whether the property has been defined, like with the define_property() command.

If BRIEF_DOCS or FULL_DOCS is given, the variable gets set to a string containing documentation for the requested property. If you ask for documentation on a property that hasn't been defined, NOTFOUND comes back ๐Ÿซ 

Note: the GENERATED source file property may be globally visible. See its documentation for the deets.

๐Ÿ’– See Also ๐Ÿ’–

  • define_property()
  • set_property()

๐Ÿ”ฅ if ๐Ÿ”ฅ

Conditionally execute a group of commands. It's giving "choose your own adventure" but make it build system ๐ŸŒˆ

๐ŸŽ€ Synopsis ๐ŸŽ€

if(<condition>)
  <commands>
elseif(<condition>) # optional block, can be repeated
  <commands>
else()              # optional block
  <commands>
endif()

It evaluates the condition argument of the if clause according to the Condition syntax described below. If the result is true, then the commands in the if block get executed. Otherwise the optional elseif blocks get processed the same way. And finally, if no condition is true, the commands in the optional else block get executed โœจ

Per legacy, the else() and endif() commands admit an optional <condition> argument. If you use it, it must be a verbatim repeat of the argument of the opening if command. No paraphrasing bestie ๐Ÿ˜ญ

๐ŸŒธ Condition Syntax ๐ŸŒธ

The following syntax applies to the condition argument of the if, elseif and while() clauses.

Compound conditions are evaluated in this order of precedence:

  1. Parentheses.
  2. Unary tests such as:
    • The Existence Checks COMMAND, DEFINED, DIAGNOSTIC, EXISTS, POLICY, TARGET, and TEST.
    • The File Operations IS_READABLE, IS_WRITABLE, IS_EXECUTABLE, IS_DIRECTORY, IS_SYMLINK, and IS_ABSOLUTE.
  3. Binary tests such as those described in Comparisons, Version Comparisons, and Path Comparisons, as well as IN_LIST and IS_NEWER_THAN.
  4. Unary logical operator NOT.
  5. Binary logical operators AND and OR, from left to right, without any short-circuit.

๐Ÿฆ‹ Basic Expressions ๐Ÿฆ‹

  • if(<constant>) โ€” true if the constant is 1, ON, YES, TRUE, Y, or a non-zero number (floating point numbers included). False if the constant is 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, the empty string, or ends in the suffix -NOTFOUND. Named boolean constants are case-insensitive. If the argument is not one of these specific constants, it's treated as a variable or string (see Variable Expansion further below), and one of the following two forms applies.
  • if(<variable>) โ€” true if you give it a variable that's defined to a value that is not a false constant. False otherwise, undefined variable included. Note that macro arguments are not variables. Environment Variables also cannot be tested this way โ€” like if(ENV{some_var}) will always evaluate to false ๐Ÿ˜ญ
  • if(<string>) โ€” a quoted string always evaluates to false unless:
    • the string's value is one of the true constants, or
    • in CMake versions prior to 4.0, policy CMP0054 is not set to NEW and the string's value happens to be a variable name that is affected by CMP0054's behavior.
  • if() โ€” false if no argument is provided.

๐Ÿ‘‘ Logic Operators ๐Ÿ‘‘

  • if(NOT <condition>) โ€” true if the condition is not true.
  • if(<cond1> AND <cond2>) โ€” true if both conditions would be considered true individually.
  • if(<cond1> OR <cond2>) โ€” true if either condition would be considered true individually.
  • if((condition) AND (condition OR (condition))) โ€” the conditions inside the parentheses get evaluated first, then the remaining condition gets evaluated as in the other examples. Where there are nested parentheses, the innermost ones get evaluated as part of evaluating the condition that contains them ๐Ÿงš

๐Ÿ’œ Existence Checks ๐Ÿ’œ

  • if(COMMAND <command-name>) โ€” true if the given name is a command, macro or function that can be invoked.
  • if(DIAGNOSTIC <category>) โ€” Added in version 4.4. True if the given name is an existing diagnostic category.
  • if(POLICY <policy-id>) โ€” true if the given name is an existing policy (of the form CMP<NNNN>).
  • if(TARGET <target-name>) โ€” true if the given name is an existing logical target name created by a call to the add_executable(), add_library(), or add_custom_target() command that has already been invoked (in any directory).
  • if(TEST <test-name>) โ€” Added in version 3.3. True if the given name is an existing test name created by the add_test() command.
  • if(DEFINED <name>|CACHE{<name>}|ENV{<name>}) โ€” true if a variable, cache variable or environment variable with the given <name> is defined. The value of the variable does not matter. Note these caveats:
    • Macro arguments are not variables.
    • It is not possible to test directly whether a <name> is a non-cache variable. The expression if(DEFINED someName) will evaluate to true if either a cache or non-cache variable someName exists. In comparison, the expression if(DEFINED CACHE{someName}) will only evaluate to true if a cache variable someName exists. Both expressions need to be tested if you need to know whether a non-cache variable exists: if(DEFINED someName AND NOT DEFINED CACHE{someName}).

Added in version 3.14: added support for CACHE{<name>} variables.

  • if(<variable|string> IN_LIST <variable>) โ€” Added in version 3.3. True if the given element is contained in the named list variable.

๐Ÿ“ File Operations ๐Ÿ“

  • if(EXISTS <path-to-file-or-directory>) โ€” true if the named file or directory exists and is readable. Behavior is well-defined only for explicit full paths (a leading ~/ is not expanded as a home directory and is considered a relative path). Resolves symbolic links, i.e. if the named file or directory is a symbolic link, it returns true if the target of the symbolic link exists.

False if the given path is an empty string.

Note: prefer if(IS_READABLE) to check file readability. if(EXISTS) may be changed in the future to only check file existence ๐Ÿ‘€
  • if(IS_READABLE <path-to-file-or-directory>) โ€” Added in version 3.29. True if the named file or directory is readable. Behavior is well-defined only for explicit full paths (a leading ~/ is not expanded as a home directory and is considered a relative path). Resolves symbolic links, i.e. if the named file or directory is a symbolic link, it returns true if the target of the symbolic link is readable.

False if the given path is an empty string.

  • if(IS_WRITABLE <path-to-file-or-directory>) โ€” Added in version 3.29. True if the named file or directory is writable. Behavior is well-defined only for explicit full paths (a leading ~/ is not expanded as a home directory and is considered a relative path). Resolves symbolic links, i.e. if the named file or directory is a symbolic link, it returns true if the target of the symbolic link is writable.

False if the given path is an empty string.

  • if(IS_EXECUTABLE <path-to-file-or-directory>) โ€” Added in version 3.29. True if the file or directory you named is executable. Lowkey important caveat: the behavior is only well-defined for explicit full paths (so a leading ~/ is not expanded as a home directory โ€” it counts as a relative path, sorry bestie ๐Ÿ˜ญ). It resolves symbolic links, meaning if the thing you named is a symlink, you get true when the target of that symlink is executable. False if the given path is an empty string.
  • if(<file1> IS_NEWER_THAN <file2>) โ€” True if file1 is newer than file2, or if one of the two files doesn't even exist. Behavior is well-defined only for full paths. And here's the chaotic bit: if the file time stamps are exactly the same, IS_NEWER_THAN returns true, so that any dependent build operations will occur in the event of a tie ๐ŸŽ€. That includes the case of passing the same file name for both file1 and file2.
  • if(IS_DIRECTORY <path>) โ€” True if path is a directory. Behavior is well-defined only for full paths. False if the given path is an empty string.
  • if(IS_SYMLINK <path>) โ€” True if the given path is a symbolic link ๐Ÿ”—. Behavior is well-defined only for full paths.
  • if(IS_ABSOLUTE <path>) โ€” True if the given path is an absolute path. But bestie, note these special cases, they're giving "edge case icon":
    • An empty path evaluates to false.
    • On Windows hosts, any path that begins with a drive letter and colon (e.g. C:), a forward slash or a backslash will evaluate to true. Which means a path like C:no\base\dir will evaluate to true, even though the non-drive part of the path is relative ๐Ÿ˜ญ.
    • On non-Windows hosts, any path that begins with a tilde (~) evaluates to true.

๐Ÿ’… Comparisons

  • if(<variable|string> MATCHES <regex>) โ€” True if the given string or the variable's value matches the given regular expression. Head to the Regex Specification for the regex format. The () groups get captured into CMAKE_MATCH_<n> variables, no cap โœจ.
  • if(<variable|string> LESS <variable|string>) โ€” True if the given string or variable's value parses as a real number (like a C double) and is less than the one on the right.
  • if(<variable|string> GREATER <variable|string>) โ€” True if the given string or variable's value parses as a real number (like a C double) and is greater than the one on the right.
  • if(<variable|string> EQUAL <variable|string>) โ€” True if the given string or variable's value parses as a real number (like a C double) and is equal to the one on the right.
  • if(<variable|string> LESS_EQUAL <variable|string>) โ€” Added in version 3.7. True if the given string or variable's value parses as a real number (like a C double) and is less than or equal to the one on the right.
  • if(<variable|string> GREATER_EQUAL <variable|string>) โ€” Added in version 3.7. True if the given string or variable's value parses as a real number (like a C double) and is greater than or equal to the one on the right.
  • if(<variable|string> STRLESS <variable|string>) โ€” True if the given string or variable's value is lexicographically less than the string or variable on the right.
  • if(<variable|string> STRGREATER <variable|string>) โ€” True if the given string or variable's value is lexicographically greater than the string or variable on the right.
  • if(<variable|string> STREQUAL <variable|string>) โ€” True if the given string or variable's value is lexicographically equal to the string or variable on the right.
  • if(<variable|string> STRLESS_EQUAL <variable|string>) โ€” Added in version 3.7. True if the given string or variable's value is lexicographically less than or equal to the string or variable on the right.
  • if(<variable|string> STRGREATER_EQUAL <variable|string>) โ€” Added in version 3.7. True if the given string or variable's value is lexicographically greater than or equal to the string or variable on the right.

๐ŸŒˆ Version Comparisons

Ok so all five of these work the same way, and the way is: component-wise integer version number comparison โ€” the version format is major[.minor[.patch[.tweak]]], and omitted components are treated as zero. Any non-integer version component, or a non-integer trailing part of a version component, effectively truncates the string at that point. Slay ๐Ÿซถ

  • if(<variable|string> VERSION_LESS <variable|string>) โ€” the component-wise integer version comparison described above, checking "less than".
  • if(<variable|string> VERSION_GREATER <variable|string>) โ€” component-wise integer version number comparison (version format is major[.minor[.patch[.tweak]]], omitted components treated as zero, non-integer bits truncate the string right there), checking "greater than".
  • if(<variable|string> VERSION_EQUAL <variable|string>) โ€” same component-wise integer version number comparison rules, checking "equal to".
  • if(<variable|string> VERSION_LESS_EQUAL <variable|string>) โ€” Added in version 3.7. Same component-wise integer version number comparison (format major[.minor[.patch[.tweak]]], omitted components are zero, any non-integer version component or non-integer trailing part of one truncates the string at that point), checking "less than or equal to".
  • if(<variable|string> VERSION_GREATER_EQUAL <variable|string>) โ€” Added in version 3.7. Same component-wise integer version number comparison (format major[.minor[.patch[.tweak]]], omitted components are zero, non-integer version components or non-integer trailing parts truncate the string there), checking "greater than or equal to".

๐Ÿฆ‹ Path Comparisons

  • if(<variable|string> PATH_EQUAL <variable|string>) โ€” Added in version 3.24. Lexicographically compares two CMake paths component-by-component, without accessing the filesystem (no peeking, we respect privacy ๐Ÿ’…). Only if every component of both paths match will the two paths compare equal. Multiple path separators are effectively collapsed into a single separator, but heads up: backslashes are not converted to forward slashes. No other path normalization is performed. Trailing slashes are preserved, so /a/b and /a/b/ are not equal.

Component-wise comparison is superior to string-based comparison because of how it handles multiple path separators. In the example below, the expression evaluates to true using PATH_EQUAL, but false with STREQUAL:

# comparison is TRUE
if ("/a//b/c" PATH_EQUAL "/a/b/c")
   ...
endif()

# comparison is FALSE
if ("/a//b/c" STREQUAL "/a/b/c")
   ...
endif()

See cmake_path(COMPARE) for more details ๐Ÿ‘‘


โœจ Variable Expansion

Ok this is the lore bestie. The if command was written very early in CMake's history โ€” literally predating the ${} variable evaluation syntax โ€” and for convenience it evaluates variables named by its arguments as shown in the signatures above. Note that normal variable evaluation with ${} applies before the if command even receives the arguments. So code like

set(var1 OFF)
set(var2 "var1")
if(${var2})

appears to the if command as

if(var1)

and is evaluated according to the if(<variable>) case documented above. The result is OFF, which is false. But if we remove the ${} from the example, then the command sees

if(var2)

which is true, because var2 is defined to var1, which is not a false constant. Iconic behavior honestly ๐Ÿ˜ญ

Automatic evaluation applies in the other cases whenever the above-documented condition syntax accepts <variable|string>:

  • The left hand argument to MATCHES is first checked to see if it is a defined variable. If so, the variable's value is used, otherwise the original value is used.
  • If the left hand argument to MATCHES is missing it returns false without error ๐Ÿ™ˆ
  • Both left and right hand arguments to LESS, GREATER, EQUAL, LESS_EQUAL, and GREATER_EQUAL are independently tested to see if they are defined variables. If so, their defined values are used; otherwise the original value is used.
  • Both left and right hand arguments to STRLESS, STRGREATER, STREQUAL, STRLESS_EQUAL, and STRGREATER_EQUAL are independently tested to see if they are defined variables. If so, their defined values are used; otherwise the original value is used.
  • Both left and right hand arguments to VERSION_LESS, VERSION_GREATER, VERSION_EQUAL, VERSION_LESS_EQUAL, and VERSION_GREATER_EQUAL are independently tested to see if they are defined variables. If so, their defined values are used; otherwise the original value is used.
  • The left hand argument to IN_LIST is tested to see if it is a defined variable. If so, the variable's value is used; otherwise the original value is used.
  • The right hand argument to NOT is tested to see if it is a boolean constant. If so, the value is used; otherwise it is assumed to be a variable and it is dereferenced.
  • The left and right hand arguments to AND and OR are independently tested to see if they are boolean constants. If so, they are used as such, otherwise they are assumed to be variables and are dereferenced.
Changed in version 3.1: To prevent ambiguity, potential variable or keyword names can be specified in a Quoted Argument or a Bracket Argument. A quoted or bracketed variable or keyword will be interpreted as a string and not dereferenced or interpreted. See policy CMP0054.

There is no automatic evaluation for environment or cache Variable References. Their values must be referenced as $ENV{<name>} or $CACHE{<name>} wherever the above-documented condition syntax accepts <variable|string>.


๐Ÿ‘€ See also

  • else()
  • elseif()
  • endif()

๐ŸŽ€ include

Load and run CMake code from a file or module. It's giving "bring your own script" energy โœจ

include(<file|module> [OPTIONAL] [RESULT_VARIABLE <var>]
                      [NO_POLICY_SCOPE] [NO_DIAGNOSTIC_SCOPE])

So like, include() pulls in a file and runs whatever CMake code is in it, and the variable reads/writes inside totally access the scope of whoever called it โ€” that's dynamic scoping bestie, it's giving "what's mine is yours" ๐Ÿ’–. If OPTIONAL is chilling there, then no error gets raised when the file doesn't exist (iconic, no drama). And if you hand it RESULT_VARIABLE, the variable <var> gets set to the full filename that got included, or to NOTFOUND if it flopped ๐Ÿ˜ญ.

Now โœจ the module tea โœจ โ€” if you specify a module instead of a file, then the file named <modulename>.cmake gets searched first in CMAKE_MODULE_PATH, and then in the CMake module directory. But there's literally one exception to this whole situation: if the file calling include() is itself sitting in the CMake builtin module directory, then the CMake builtin module directory gets searched first and CMAKE_MODULE_PATH comes afterwards. Sneaky! Also go peek at policy CMP0017 for the deets.

For the whole discussion of the NO_POLICY_SCOPE option, check the cmake_policy() command docs. And for the NO_DIAGNOSTIC_SCOPE option, check the cmake_diagnostic() command docs. Both of those, bestie, not me making it up ๐Ÿซถ.


๐Ÿ›ก๏ธ include_guard

Added in version 3.10. โœจ

It literally provides an include guard for the file that CMake is currently processing. Main character energy for your headers.

include_guard([DIRECTORY|GLOBAL])

So it sets up an include guard for the current CMake file (peep the CMAKE_CURRENT_LIST_FILE variable docs for the context, iykyk).

Here's the vibe: CMake will end its processing of the current file right at the location of the include_guard command if that file has already been processed for the applicable scope (scopes explained below, keep reading ๐Ÿ’…). Which is basically the same energy as those include guards people put in source headers, or the #pragma once directive. And if the current file has been processed before for the applicable scope, the effect is exactly as though return() had been called. Chef's kiss.

๐Ÿšจ Bestie do not call this command from inside a function that's being defined within the current file. Just don't. It's not the move.

You can optionally pass an argument saying what the scope of the guard is. The possible values are:

  • DIRECTORY โ€” the include guard applies within the current directory and below ๐ŸŒธ. The file only gets included once within this directory scope, but it may get included again by other files outside of this directory โ€” like a parent directory, or another directory that wasn't pulled in by add_subdirectory() or include() from the current file or its children. Boundaries, we love them.
  • GLOBAL โ€” the include guard applies globally to the whole build ๐Ÿ‘‘. The current file only gets included once, regardless of the scope. Periodt.

If you give it no arguments at all, include_guard has the same scope as a variable โ€” meaning the include guard effect gets isolated by the most recent function scope, or by the current directory if there are no inner function scopes existing. In that case the command behavior is the same as:

if(__CURRENT_FILE_VAR__)
  return()
endif()
set(__CURRENT_FILE_VAR__ TRUE)

No cap, that's it. Simple. Slay.


๐Ÿ“‹ list

Operations on semicolon-separated lists ๐Ÿ“. Yes, semicolons. We don't make the rules.

Synopsis

Reading
  list(LENGTH <list> <out-var>)
  list(GET <list> <element index> [<index> ...] <out-var>)
  list(JOIN <list> <glue> <out-var>)
  list(SUBLIST <list> <begin> <length> <out-var>)

Search
  list(FIND <list> <value> <out-var>)

Modification
  list(APPEND <list> [<element>...])
  list(FILTER <list> <INCLUDE|EXCLUDE> <MODE>)
  list(INSERT <list> <index> [<element>...])
  list(POP_BACK <list> [<out-var>...])
  list(POP_FRONT <list> [<out-var>...])
  list(PREPEND <list> [<element>...])
  list(REMOVE_ITEM <list> <value>...)
  list(REMOVE_AT <list> <index>...)
  list(REMOVE_DUPLICATES <list>)
  list(TRANSFORM <list> <ACTION> [...])

Ordering
  list(REVERSE <list>)
  list(SORT <list> [...])

Introduction

Okay so the list subcommands APPEND, INSERT, FILTER, PREPEND, POP_BACK, POP_FRONT, REMOVE_AT, REMOVE_ITEM, REMOVE_DUPLICATES, REVERSE and SORT may create new values for the list within the current CMake variable scope. Just like the set() command, the list command creates new variable values in the current scope โ€” even if the list itself is actually defined in a parent scope. Rude? Maybe. But that's the deal ๐Ÿ’€.

So if you wanna propagate the results of these operations upwards, use set() with PARENT_SCOPE, or set() with CACHE INTERNAL, or literally some other means of value propagation. Your call, bestie.

๐Ÿ“Œ Note: A list in cmake is a ; separated group of strings. To create a list, the set() command can be used. For example, set(var a b c d e) creates a list with a;b;c;d;e, and set(var "a b c d e") creates a string or a list with one item in it. (Also note that macro arguments are not variables, and therefore cannot be used in LIST commands โ€” tragic, I know ๐Ÿ˜ญ.) Individual elements may not contain an unequal number of [ and ] characters, and may not end in a backslash (\). See semicolon-separated lists for the full details.
๐Ÿ“Œ Note: When specifying index values, if <element index> is 0 or greater, it's indexed from the beginning of the list, with 0 representing the first list element. If <element index> is -1 or lesser, it's indexed from the end of the list, with -1 representing the last list element. Be careful when counting with negative indices, bestie โ€” they do not start from 0. And -0 is equivalent to 0, the first list element. Math is math.

Reading

  • list(LENGTH <list> <output variable>) โ€” returns the list's length. That's it. Iconic.
  • list(GET <list> <element index> [<element index> ...] <output variable>) โ€” returns the list of elements specified by the indices from the list.
  • list(JOIN <list> <glue> <output variable>) โ€” added in version 3.12. โœจ Returns a string joining all of the list's elements using the glue string. And if you need to join multiple strings that aren't part of a list, use string(JOIN) instead, bestie.
  • list(SUBLIST <list> <begin> <length> <output variable>) โ€” added in version 3.12. ๐ŸŒธ Returns a sublist of the given list. If <length> is 0, an empty list will be returned. If <length> is -1, or the list is smaller than <begin>+<length>, then the remaining elements of the list starting at <begin> will be returned. Giving "the rest of it, whatever's left" energy.
  • list(FIND <list> <value> <output variable>) โ€” returns the index of the element specified in the list, or -1 if it wasn't found ๐Ÿ™ˆ. She looked everywhere. Not there.

Modification

  • list(APPEND <list> [<element> ...]) โ€” appends elements to the list. If no variable named <list> exists in the current scope, its value is treated as empty and the elements get appended to that empty list. Manifesting from nothing โœจ.
  • list(FILTER <list> <INCLUDE|EXCLUDE> <MODE>) โ€” added in version 3.6. Includes or removes items from the list that match the mode's pattern ๐ŸŽ€.

<MODE> must be one of the following:

  • REGEX โ€” items will be matched against the given regular expression.
list(FILTER <list> <INCLUDE|EXCLUDE> REGEX <regular_expression>)

For more info on regular expressions, look under string(REGEX). Regex girlies rise ๐Ÿฆ‹.

  • PREDICATE โ€” specify a user-defined function() as a predicate.
list(FILTER <list> <INCLUDE|EXCLUDE> PREDICATE <function>)

Added in version 4.4. ๐Ÿ’œ

<function> is a user-defined function() that acts as a unary predicate. The function must accept exactly two parameters: the input value and the name of an output variable. The function must set the output variable to a boolean value in the calling scope. That output variable gets interpreted using standard CMake boolean evaluation. And if the function does not set the output variable โ€” that's an error, bestie. No silent fails allowed ๐Ÿ’€.

Example, so you can see the vision:

function(file_exists path result)
  if(EXISTS "${path}")
    set(${result} TRUE PARENT_SCOPE)
  else()
    set(${result} FALSE PARENT_SCOPE)
  endif()
endfunction()

set(candidate_files main.c missing.c utils.c)
list(FILTER candidate_files INCLUDE PREDICATE file_exists)
  • list(INSERT <list> <element_index> <element> [<element> ...]) โ€” inserts elements into the list at the specified index. It is an error to specify an out-of-range index ๐Ÿšจ. Valid indexes are 0 to N, where N is the length of the list, inclusive. An empty list has length 0. If no variable named <list> exists in the current scope, its value is treated as empty and the elements are inserted into that empty list.
  • list(POP_BACK <list> [<out-var>...]) โ€” added in version 3.15. If no variable name is given, it removes exactly one element. Otherwise, with N variable names provided, it assigns the last N elements' values to the given variables and then removes the last N values from <list>. Bye from the back ๐Ÿ‘‹.
  • list(POP_FRONT <list> [<out-var>...]) โ€” added in version 3.15. If no variable name is given, it removes exactly one element. Otherwise, with N variable names provided, it assigns the first N elements' values to the given variables and then removes the first N values from <list>. Front row, evicted ๐Ÿ’….
  • list(PREPEND <list> [<element> ...]) โ€” added in version 3.15. Inserts elements into the 0th position in the list. If no variable named <list> exists in the current scope, its value is treated as empty and the elements are prepended to that empty list. Cutting the line, respectfully ๐Ÿ’–.
  • list(REMOVE_ITEM <list> <value> [<value> ...]) โ€” removes all instances of the given items from the list. All of them. Every single one. Gone ๐Ÿซถ.
  • list(REMOVE_AT <list> <index> [<index> ...]) โ€” removes items at the given indices from the list.
  • list(REMOVE_DUPLICATES <list>) โ€” removes duplicated items in the list. The relative order of items is preserved, but if duplicates are encountered, only the first instance is preserved. Original only, no remakes โœจ.
  • list(TRANSFORM <list> <ACTION> [<SELECTOR>] [OUTPUT_VARIABLE <output variable>]) โ€” added in version 3.12. Transforms the list by applying an <ACTION> to all of the elements โ€” or, by specifying a <SELECTOR>, to the selected elements of the list โ€” storing the result in-place or in the specified output variable. Glow-up service, basically ๐ŸŒˆ.
Note: the TRANSFORM sub-command does not change how many elements are in your list. Bestie, the count stays exactly the same. If you hand it a <SELECTOR>, only some elements get changed and the rest just chill there looking exactly like they did before the transformation. No glow-ups for them. ๐Ÿ’…

<ACTION> is the actual ~thing~ you're doing to the elements, and it has to be one of these:

  • APPEND, PREPEND โ€” stick the value you give onto each element's end (APPEND) or beginning (PREPEND).
list(TRANSFORM <list> (APPEND|PREPEND) <value> ...)
  • TOLOWER, TOUPPER โ€” convert each element to lower / upper characters. Case glow-up โœจ
list(TRANSFORM <list> (TOLOWER|TOUPPER) ...)
  • STRIP โ€” remove leading and trailing spaces from each element. Say bye to the extra whitespace ๐Ÿ‘‹
list(TRANSFORM <list> STRIP ...)
list(TRANSFORM <list> GENEX_STRIP ...)
  • REPLACE โ€” matches the regular expression as many times as possible and substitutes the replacement expression for the match, for each element of the list (same semantic as string(REGEX REPLACE)).
list(TRANSFORM <list> REPLACE <regular_expression> <replace_expression> ...)

Changed in version 4.1: the ^ anchor now matches only at the beginning of the input element instead of the beginning of each repeated search. See policy CMP0186 for the whole story ๐Ÿซถ

  • APPLY โ€” invokes a user-defined function() once for each element of the list. The function has to accept exactly two parameters: the input value, and the name of an output variable. And it must set that output variable in the calling scope. no cap.
list(TRANSFORM <list> APPLY <function> ...)

Added in version 4.4.

<function> is a function() with exactly two formal parameters. You set the output variable via set(<variable> <value> PARENT_SCOPE):

function(<function> <input> <output>)
  # Transform <input>, store result in ${<output>}
  set(${<output>} "<result>" PARENT_SCOPE)
endfunction()

Before each invocation, the output variable is unset in the calling scope, so you never get stuck with a stale value from the last call. icon behaviour honestly ๐ŸŽ€

Example:

function(make_absolute in out)
  cmake_path(ABSOLUTE_PATH in BASE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
  set(${out} "${in}" PARENT_SCOPE)
endfunction()

set(mylist main.c utils.c io.c)
list(TRANSFORM mylist APPLY make_absolute)
# mylist is now absolute paths relative to CMAKE_CURRENT_SOURCE_DIR

<SELECTOR> is the โœจvibe checkโœจ that decides which elements get transformed. Only one type of selector at a time, pls โ€” don't be greedy. When you give one, it must be one of these:

  • AT โ€” specify a list of indexes.
list(TRANSFORM <list> <ACTION> AT <index> [<index> ...] ...)
  • FOR โ€” specify a range, optionally with an increment used to iterate over that range.
list(TRANSFORM <list> <ACTION> FOR <start> <stop> [<step>] ...)
  • REGEX โ€” specify a regular expression. Only the elements that match it get transformed. everybody else stays unbothered ๐Ÿ’…
list(TRANSFORM <list> <ACTION> REGEX <regular_expression> ...)
  • PREDICATE โ€” specify a user-defined function() to act as a predicate. Only elements for which the function returns a true value get transformed.
list(TRANSFORM <list> <ACTION> PREDICATE <function> ...)

Added in version 4.4.

<function> is a user-defined function() with exactly two formal parameters: the input value, and the name of an output variable. The function must set that output variable to a boolean value, and standard CMake boolean evaluation is used. If the function doesn't set the output variable โ€” that's an error, fr fr. The function is evaluated for each element in turn, immediately before that element would be transformed.

Example:

function(is_relative path result)
  if(NOT IS_ABSOLUTE "${path}")
    set(${result} TRUE PARENT_SCOPE)
  else()
    set(${result} FALSE PARENT_SCOPE)
  endif()
endfunction()

set(search_paths /usr/include src lib /opt/lib)
list(TRANSFORM search_paths PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/"
     PREDICATE is_relative)

๐Ÿ’… Ordering

  • list(REVERSE <list>) โ€” reverses the contents of the list in-place. Like flipping your camera roll ๐ŸŒ€
  • list(SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
  • list(SORT <list> COMPARATOR <function> [CASE <case>] [ORDER <order>])

Sorts the list in-place alphabetically.

Added in version 3.13: added the COMPARE, CASE, and ORDER options.

Added in version 3.18: added the COMPARE NATURAL option.

Use the COMPARE keyword to pick your sorting method, bestie. The <compare> option should be one of:

  • STRING โ€” sorts a list of strings alphabetically. This is the default behaviour if you don't give the COMPARE option at all.
  • FILE_BASENAME โ€” sorts a list of pathnames of files by their basenames.
  • NATURAL โ€” sorts a list of strings using natural order (see the strverscmp(3) manual), i.e. such that contiguous digits get compared as whole numbers. For example: the following list 10.0 1.1 2.1 8.0 2.0 3.1 will be sorted as 1.1 2.0 2.1 3.1 8.0 10.0 if the NATURAL comparison is selected, whereas it will be sorted as 1.1 10.0 2.0 2.1 3.1 8.0 with the STRING comparison. numbers behaving like actual numbers, we love to see it ๐Ÿ”ฅ

Use the CASE keyword to select a case sensitive or case insensitive sort mode. The <case> option should be one of:

  • SENSITIVE โ€” list items are sorted in a case-sensitive manner. This is the default behaviour if the CASE option is not given.
  • INSENSITIVE โ€” list items are sorted case insensitively. The order of items which differ only by upper/lowercase is not specified, so don't come for it ๐Ÿ˜Œ

To control the sort order, the ORDER keyword can be given. The <order> option should be one of:

  • ASCENDING โ€” sorts the list in ascending order. This is the default behaviour when the ORDER option is not given.
  • DESCENDING โ€” sorts the list in descending order.

Instead of the built-in COMPARE methods, a user-defined comparison function() may be used with the COMPARATOR keyword.

Added in version 4.4.

COMPARATOR is mutually exclusive with COMPARE. The CASE and ORDER options may still be used alongside COMPARATOR: the CASE filter is applied to both values before they're passed to the function, and ORDER DESCENDING reverses the comparison by swapping the two arguments. sneaky but correct ๐Ÿงš

The function must accept exactly three parameters: two input values and the name of an output variable. It must set the output variable to a boolean value (TRUE if the first value should come before the second, FALSE otherwise) in the calling scope. If the function does not set the output variable, it's an error.

The comparator must define a strict weak ordering (https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings). In particular:

  • It must return FALSE when comparing an element to itself (irreflexivity).
  • If it returns TRUE for (a, b), it must return FALSE for (b, a) (asymmetry).

An error is raised if the comparator violates these requirements. it WILL catch you ๐Ÿ’€

Example:

function(version_less a b result)
  if("${a}" VERSION_LESS "${b}")
    set(${result} TRUE PARENT_SCOPE)
  else()
    set(${result} FALSE PARENT_SCOPE)
  endif()
endfunction()

set(versions 3.1 1.2 2.0 1.10)
list(SORT versions COMPARATOR version_less)
# versions is now: 1.2;1.10;2.0;3.1

๐ŸŽ€ load_cache

Loads in the values from another project's CMakeCache.txt cache file. Super useful for projects that depend on another project that was built in a separate directory tree. cross-project bestie behaviour ๐Ÿซถ

This command has two signatures. The recommended one is:

load_cache(<build-dir> READ_WITH_PREFIX <prefix> <entry>...)

Loads the cache file from the specified <build-dir> build directory and retrieves the listed cache entries. The retrieved values are stored in local variables, with their names prefixed by the <prefix> you provide. This only reads the cache values โ€” it does not create or modify entries in the local project's cache. look, don't touch ๐ŸŒธ

  • READ_WITH_PREFIX <prefix> โ€” for each cache <entry>, a local variable is created using the specified <prefix> followed by the entry name.

This signature can also be used in cmake -P script mode. cute and versatile โญ

The following signature of this command is strongly discouraged, but it's provided for backward compatibility:

  • EXCLUDE <entry>... โ€” bestie this one lets you skip a whole list of non-internal cache entries when the values get imported. like a guest list but for variables ๐Ÿšซ
  • INCLUDE_INTERNALS <entry>... โ€” and this one hands you a way to name internal cache entries you want pulled in on top of the non-internal ones ๐Ÿ’…

This signature can be used only in CMake projects. Script mode is not supported, periodt. ๐Ÿ™…โ€โ™€๏ธ

Note: okay so instead of loading the outside project's cache file and then manually poking at variables yourself, the way more robust and convenient glow-up is to use the export() command in the outside project, when it's available. it lets that project hand over its targets, configuration, or features in a structured and maintainable way, so integration is simpler and less error-prone. iykyk ๐Ÿซถ

Examples

Reading specific cache variables from another project and storing them as local variables:

load_cache(
  path/to/other-project/build-dir
  READ_WITH_PREFIX prefix_
  OTHER_PROJECT_CACHE_VAR_1
  OTHER_PROJECT_CACHE_VAR_2
)

message(STATUS "${prefix_OTHER_PROJECT_CACHE_VAR_1")
message(STATUS "${prefix_OTHER_PROJECT_CACHE_VAR_2")
# Outputs:
# -- some-value...
# -- another-value...

Reading all non-internal cache entries from another project and storing them as internal cache variables using the obsolete signature:

load_cache(path/to/other-project/build-dir)

message(STATUS "${OTHER_PROJECT_CACHE_VAR_1")
message(STATUS "${OTHER_PROJECT_CACHE_VAR_2")
# Outputs:
# -- some-value...
# -- another-value...

Excluding specific non-internal cache entries and including internal ones using the obsolete signature:

load_cache(
  path/to/other-project/build-dir
  EXCLUDE OTHER_PROJECT_CACHE_VAR_2
  INCLUDE_INTERNALS OTHER_PROJECT_INTERNAL_CACHE_VAR
)

message(STATUS "${OTHER_PROJECT_CACHE_VAR_1")
message(STATUS "${OTHER_PROJECT_CACHE_VAR_2")
message(STATUS "${OTHER_PROJECT_INTERNAL_CACHE_VAR}")
# Outputs:
# -- some-value...
# --
# -- some-internal-value...

๐ŸŽ€ macro

Start recording a macro for later invocation as a command ๐ŸŽฌ

macro(<name> [<arg1> ...])
  <commands>
endmacro()

Defines a macro named <name> that takes arguments named <arg1>, ... The commands listed after macro, but before the matching endmacro(), are not executed until the macro actually gets invoked. think of it as a saved draft you haven't hit send on yet โœจ

Per legacy, the endmacro() command admits an optional <name> argument. If you use it, it has to be a verbatim repeat of the argument of the opening macro command. no paraphrasing, bestie ๐Ÿ’€

See the cmake_policy() command documentation for the behavior of policies inside macros.

See the Macro vs Function section below for the differences between CMake macros and functions. ๐Ÿ‘‘

Invocation

The macro invocation is case-insensitive. A macro defined as

macro(foo)
  <commands>
endmacro()

can be invoked through any of

foo()
Foo()
FOO()
cmake_language(CALL foo)

and so on. However, it is strongly recommended to stay with the case chosen in the macro definition. Typically macros use all-lowercase names. consistency is the vibe check here ๐ŸŒธ

Added in version 3.18: The cmake_language(CALL ...) command can also be used to invoke the macro. slay ๐Ÿ’…

Arguments

When a macro is invoked, first all the commands recorded in the macro are modified by replacing formal parameters (${arg1}, ...) with the arguments passed. Then all the modified commands are invoked as normal commands. it's literally find-and-replace then go ๐Ÿƒโ€โ™€๏ธ

In addition to referencing the formal parameters you can reference the values ${ARGC}, which will be set to the number of arguments passed into the macro, as well as ${ARGV0}, ${ARGV1}, ${ARGV2}, ... which will have the actual values of the arguments passed in. This facilitates creating macros with optional arguments. so handy ๐Ÿ“

Furthermore, ${ARGV} holds the list of all arguments given to the macro, and ${ARGN} holds the list of arguments past the last expected argument. Referencing ${ARGV#} arguments beyond ${ARGC} has undefined behavior ๐Ÿ˜ญโš ๏ธ Checking that ${ARGC} is greater than # is the only way to ensure that ${ARGV#} was passed to the function as an extra argument. no cap.

Macro vs Function

The macro command is very similar to the function() command. Nonetheless, there are a few important differences. ๐Ÿ‘€

In a function, ARGN, ARGC, ARGV, and ARGV0, ARGV1, ... are true variables in the usual CMake sense. In a macro, they are not โ€” they're string replacements, much like the C preprocessor would do with a macro. This has a number of consequences, as explained in the Argument Caveats section below. ๐Ÿ’€

Another difference between macros and functions is the control flow. A function is executed by transferring control from the calling statement to the function body. A macro is executed as if the macro body were pasted in place of the calling statement. This has the consequence that a return() in a macro body does not just terminate execution of the macro; rather, control is returned from the scope of the macro call. To avoid confusion, it is recommended to avoid return() in macros altogether. ๐Ÿ™ˆ

Unlike a function, the CMAKE_CURRENT_FUNCTION, CMAKE_CURRENT_FUNCTION_LIST_DIR, CMAKE_CURRENT_FUNCTION_LIST_FILE, CMAKE_CURRENT_FUNCTION_LIST_LINE variables are not set for a macro. ๐Ÿซฅ

Argument Caveats

Since ARGN, ARGC, ARGV, ARGV0 etc. are not variables, you will NOT be able to use commands like

if(ARGV1) # ARGV1 is not a variable
if(DEFINED ARGV2) # ARGV2 is not a variable
if(ARGC GREATER 2) # ARGC is not a variable
foreach(loop_var IN LISTS ARGN) # ARGN is not a variable

In the first case, you can use if(${ARGV1}). In the second and third case, the proper way to check if an optional variable was passed to the macro is to use if(${ARGC} GREATER 2). In the last case, you can use foreach(loop_var ${ARGN}), but this will skip empty arguments. If you need to include them, you can use

set(list_var "${ARGN}")
foreach(loop_var IN LISTS list_var)

Note that if you have a variable with the same name in the scope from which the macro is called, using unreferenced names will use the existing variable instead of the arguments. For example:

macro(bar)
  foreach(arg IN LISTS ARGN)
    <commands>
  endforeach()
endmacro()

function(foo)
  bar(x y z)
endfunction()

foo(a b c)

Will loop over a;b;c and not over x;y;z as one might have expected. ๐Ÿ˜ญ If you want true CMake variables and/or better CMake scope control you should look at the function command. fr fr, that one's the upgrade.

See Also

  • cmake_parse_arguments()
  • endmacro()

โญ mark_as_advanced

Mark cmake cached variables as advanced. ๐Ÿง 

mark_as_advanced([CLEAR|FORCE] <var1> ...)

Sets the advanced/non-advanced state of the named cached variables.

An advanced variable will not be displayed in any of the cmake GUIs unless the show advanced option is on. In script mode, the advanced/non-advanced state has no effect. it's giving "hidden level unlocked" ๐Ÿ”“

If the keyword CLEAR is given then advanced variables are changed back to unadvanced. If the keyword FORCE is given then the variables are made advanced. If neither FORCE nor CLEAR is specified, new values will be marked as advanced, but if a variable already has an advanced/non-advanced state, it will not be changed. classic "if it ain't decided, we decide; if it is, we leave it alone" ๐Ÿ’…

Changed in version 3.17: Variables passed to this command which are not already in the cache are ignored. See policy CMP0102. ๐Ÿ‘€


๐Ÿงฎ math

Evaluate a mathematical expression.

math(EXPR <variable> "<expression>" [OUTPUT_FORMAT <format>])

Evaluates a mathematical <expression> and sets <variable> to the resulting value. The result of the expression must be representable as a 64-bit signed integer. Floating point inputs are invalid, e.g. 1.1 * 10. Non-integer results, e.g. 3 / 2, are truncated. so no decimals bestie, we're doing integer math only ๐Ÿ˜ค

The mathematical expression must be given as a string (i.e. enclosed in double quotation marks). An example is "5 * (10 + 13)". Supported operators are +, -, *, /, %, |, &, ^, ~, <<, >>, and (...); they have the same meaning as in C code. ๐Ÿ”ฅ

Added in version 3.13: Hexadecimal numbers are recognized when prefixed with 0x, as in C code.

Added in version 3.13: The result is formatted according to the option OUTPUT_FORMAT, where <format> is one of

  • HEXADECIMAL โ€” Hexadecimal notation as in C code, i. e. starting with "0x". ๐Ÿฆ‹
  • DECIMAL โ€” Decimal notation. Which is also used if no OUTPUT_FORMAT option is specified. the default, the main character ๐ŸŒˆ

For example

math(EXPR value "100 * 0xA" OUTPUT_FORMAT DECIMAL)      # value is set to "1000"
math(EXPR value "100 * 0xA" OUTPUT_FORMAT HEXADECIMAL)  # value is set to "0x3e8"

๐Ÿ’ฌ message

Log a message.

Synopsis

General messages
  message([<mode>] "message text" ...)

Reporting checks
  message(<checkState> "message text" ...)

Configure Log
  message(CONFIGURE_LOG <text>...)

General messages

message([<mode>] "message text" ...)

Record the specified message text in the log. If more than one message string is given, they are concatenated into a single message with no separator between the strings. ๐Ÿซถ

The optional <mode> keyword determines the type of message, which influences the way the message is handled:

  • FATAL_ERROR โ€” CMake Error, stop processing and generation. The cmake(1) executable will return a non-zero exit code. ๐Ÿ›‘ like, everything stops.
  • SEND_ERROR โ€” CMake Error, continue processing, but skip generation. ๐Ÿ˜ฌ
  • WARNING โ€” CMake Warning, continue processing. โš ๏ธ
  • AUTHOR_WARNING โ€” CMake CMD_AUTHOR diagnostic.
  • DEPRECATION โ€” CMake CMD_DEPRECATED diagnostic.
  • (none) or NOTICE โ€” Important message printed to stderr to attract user's attention. ๐Ÿ‘€ like tapping you on the shoulder.
  • STATUS โ€” bestie these are the messages your project's users actually care about ๐Ÿ‘€ keep 'em short, ideally a single line, but still make them say something useful.
  • VERBOSE โ€” the detailed info drops for project users โœจ extra deets that most people won't need, but that the folks building the project will lowkey love when they want a deeper look at what's going on.
  • DEBUG โ€” this one's for the devs working on the project, not the users who just wanna build it ๐Ÿซถ usually not interesting to anyone else building it, and it's often super tied to internal implementation details.
  • TRACE โ€” fine-grained, very low-level implementation tea ๐Ÿ’€ these are normally temporary and are expected to be gone before the project ships, gets packaged up, etc.

Added in version 3.15: added the NOTICE, VERBOSE, DEBUG, and TRACE levels.

So here's the vibe check on where stuff shows up ๐Ÿ‘‘ the CMake command-line tool prints STATUS through TRACE messages on stdout, with each message prefixed by two hyphens and a space. Every other message type goes to stderr and gets no hyphens. The CMake GUI shows all messages in its log area. The curses interface (ccmake) shows STATUS through TRACE one at a time on a status line, and everything else in an interactive pop-up box. The --log-level command-line option on each of these tools controls which messages get shown ๐ŸŽ€

Added in version 3.17: want a log level that persists between CMake runs? Set the CMAKE_MESSAGE_LOG_LEVEL variable instead โœจ but note the command-line option takes precedence over the cache variable.

Added in version 3.16: messages at log level NOTICE and below get every line prefixed with the contents of the CMAKE_MESSAGE_INDENT variable (converted to a single string by concatenating its list items). For STATUS through TRACE messages, that indent content goes after the hyphens ๐Ÿ’…

Added in version 3.17: messages at NOTICE and below can also get each line prefixed with context that looks like [some.context.example]. The stuff inside the square brackets comes from converting the CMAKE_MESSAGE_CONTEXT list variable into a dot-separated string. Message context always shows up before any indenting content, but after any automatically added leading hyphens ๐ŸŒธ by default context is not shown โ€” you have to explicitly turn it on with the cmake --log-context command-line option, or by setting the CMAKE_MESSAGE_CONTEXT_SHOW variable to true. Peep the CMAKE_MESSAGE_CONTEXT docs for usage examples.

CMake Warning and Error message text renders with a simple markup language ๐Ÿงš non-indented text gets formatted into line-wrapped paragraphs delimited by newlines. Indented text is treated as pre-formatted.

๐Ÿฆ‹ Reporting checks

Added in version 3.17.

Okay so the classic pattern in CMake output is: a message saying a check is starting, then another message reporting the result. Like this:

message(STATUS "Looking for someheader.h")
#... do the checks, set checkSuccess with the result
if(checkSuccess)
  message(STATUS "Looking for someheader.h - found")
else()
  message(STATUS "Looking for someheader.h - not found")
endif()

The way that this is so much more robust and convenient is with the CHECK_... keyword form of the message() command ๐Ÿ’–

message(<checkState> "message" ...)

where <checkState> has to be one of these:

  • CHECK_START โ€” records a concise message about the check that's about to happen ๐ŸŒŸ
  • CHECK_PASS โ€” records a successful result for a check โœ…
  • CHECK_FAIL โ€” records an unsuccessful result for a check ๐Ÿ˜ญ

When it records a check result, the command repeats the message from the most recently started check that hasn't had a result reported yet, then some separator characters, then the message text you handed it after CHECK_PASS or CHECK_FAIL. Check messages are always reported at STATUS log level, no cap.

Checks can nest, and every CHECK_START should have exactly one matching CHECK_PASS or CHECK_FAIL ๐ŸŽฏ you can also use CMAKE_MESSAGE_INDENT to add indent to nested checks if that's your vibe. For example:

message(CHECK_START "Finding my things")
list(APPEND CMAKE_MESSAGE_INDENT "  ")
unset(missingComponents)

message(CHECK_START "Finding partA")
# ... do check, assume we find A
message(CHECK_PASS "found")

message(CHECK_START "Finding partB")
# ... do check, assume we don't find B
list(APPEND missingComponents B)
message(CHECK_FAIL "not found")

list(POP_BACK CMAKE_MESSAGE_INDENT)
if(missingComponents)
  message(CHECK_FAIL "missing components: ${missingComponents}")
else()
  message(CHECK_PASS "all components found")
endif()

The output from that would look something like this ๐ŸŽ€

-- Finding my things
--   Finding partA
--   Finding partA - found
--   Finding partB
--   Finding partB - not found
-- Finding my things - missing components: B

๐ŸŒˆ Configure Log

Added in version 3.26.

message(CONFIGURE_LOG <text>...)

Records a configure-log message event with the <text> you specify โœจ by convention, if the text has more than one line, the first line should be a summary of the event.

This mode is for recording the details of a system inspection check or some other one-time operation that's guarded by a cache entry โ€” but that isn't done with try_compile() or try_run(), because those automatically log their own details ๐Ÿ’… projects should avoid calling it every single time CMake runs. For example:

if (NOT DEFINED MY_CHECK_RESULT)
  # Print check summary in configure output.
  message(CHECK_START "My Check")

  # ... perform system inspection, e.g., with execute_process ...

  # Cache the result so we do not run the check again.
  set(MY_CHECK_RESULT "${MY_CHECK_RESULT}" CACHE INTERNAL "My Check")

  # Record the check details in the cmake-configure-log.
  message(CONFIGURE_LOG
    "My Check Result: ${MY_CHECK_RESULT}\n"
    "${details}"
  )

  # Print check result in configure output.
  if(MY_CHECK_RESULT)
    message(CHECK_PASS "passed")
  else()
    message(CHECK_FAIL "failed")
  endif()
endif()

If no project is currently being configured โ€” like in cmake -P script mode โ€” this command does absolutely nothing ๐Ÿ™ˆ

๐Ÿ‘‘ See Also

  • cmake_language(GET_MESSAGE_LOG_LEVEL)

๐Ÿ’– option

Gives the user a boolean option they can optionally select ๐ŸŽ€

option(<variable> "<help_text>" [value])

If no initial <value> is given, boolean OFF is the default. If <variable> is already set as a normal or cache variable, then the command does nothing (see policy CMP0077) ๐Ÿคทโ€โ™€๏ธ

In CMake project mode, a boolean cache variable gets created with the option value. In CMake script mode, a boolean variable gets set with the option value โญ

๐ŸŒธ See Also

  • The CMakeDependentOption module, for specifying boolean options that depend on the values of other options or on a set of conditions.

๐Ÿฆ‹ return

Bounce out of a file, directory, or function ๐Ÿซถ

return([PROPAGATE <var-name>...])

When this command shows up inside an included file (via include() or find_package()), it stops processing of the current file and hands control back to the including file โœจ if it shows up in a file that isn't included by another file โ€” like a CMakeLists.txt โ€” then deferred calls scheduled by cmake_language(DEFER) get invoked, and control returns to the parent directory if there is one.

If return() is called inside a function, control goes back to that function's caller ๐Ÿ’… heads up though: a macro(), unlike a function(), is expanded in place and therefore cannot handle return() ๐Ÿ˜ญ

Policy CMP0140 controls the behavior around the command's arguments. All arguments are ignored unless that policy is set to NEW.

๐Ÿ”ฅ PROPAGATE

Added in version 3.25.

This option sets or unsets the specified variables in the parent directory or the function caller's scope ๐ŸŽ€ it's equivalent to the set(PARENT_SCOPE) or unset(PARENT_SCOPE) commands โ€” except for how it interacts with the block() command, which we're about to get into.

PROPAGATE is so useful alongside block() ๐Ÿ‘‘ a return will propagate the specified variables through any enclosing block scopes created by block() commands. Inside a function, that means the variables make it out to the function's caller no matter how many blocks are in the function. If you're not inside a function, it means the variables propagate to the parent file or directory scope. For example:

CMakeLists.txt

cmake_minimum_required(VERSION 3.25)
project(example)

set(var1 "top-value")

block(SCOPE_FOR VARIABLES)
  add_subdirectory(subDir)
  # var1 has the value "block-nested"
endblock()

# var1 has the value "top-value"

subDir/CMakeLists.txt

function(multi_scopes result_var1 result_var2)
  block(SCOPE_FOR VARIABLES)
    # This would only propagate out of the immediate block, not to
    # the caller of the function.
    #set(${result_var1} "new-value" PARENT_SCOPE)
    #unset(${result_var2} PARENT_SCOPE)

    # This propagates the variables through the enclosing block and
    # out to the caller of the function.
    set(${result_var1} "new-value")
    unset(${result_var2})
    return(PROPAGATE ${result_var1} ${result_var2})
  endblock()
endfunction()
set(var1 "some-value")
set(var2 "another-value")

multi_scopes(var1 var2)
# Now var1 will hold "new-value" and var2 will be unset

block(SCOPE_FOR VARIABLES)
  # This return() will set var1 in the directory scope that included us
  # via add_subdirectory(). The surrounding block() here does not limit
  # propagation to the current file, but the block() in the parent
  # directory scope does prevent propagation going any further.
  set(var1 "block-nested")
  return(PROPAGATE var1)
endblock()

๐ŸŒธ See Also ๐ŸŒธ

  • block() โ€” the scope-making icon itself
  • function() โ€” because functions make scopes too, bestie

โœจ separate_arguments โœจ

Okay so this one takes a command line and splits it up into a semicolon-separated list, like the little organizational queen it is. ๐Ÿ’…

separate_arguments(<variable> <mode> [PROGRAM [SEPARATE_ARGS]] <args>)

So what it does: it parses a space-separated string <args> into a list of items, and then stores that list in <variable> in semicolon-separated standard form. ๐ŸŽ€

This function is for parsing command-line arguments โ€” that's literally its whole job. And the entire command line has to be passed as one single string in the <args> argument. One string. That's the vibe. ๐Ÿซถ

Now โ€” the exact parsing rules depend on the operating system. They're specified by the <mode> argument, and it has to be one of these keywords:

  • UNIX_COMMAND โ€” Arguments are separated by unquoted whitespace. Both single-quote and double-quote pairs are respected. A backslash escapes the next literal character (\" is "); and heads up, there are no special escapes โ€” \n is just n. Literally just the letter n. ๐Ÿ˜ญ
  • WINDOWS_COMMAND โ€” A Windows command-line is parsed using the same syntax the runtime library uses to construct argv at startup. It separates arguments by whitespace that is not double-quoted. Backslashes are literal unless they precede double-quotes. See the MSDN article Parsing C Command-Line Arguments (<https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments>) for details. iykyk ๐Ÿฆ‹
  • NATIVE_COMMAND โ€” Added in version 3.9. Proceeds as in WINDOWS_COMMAND mode if the host system is Windows. Otherwise proceeds as in UNIX_COMMAND mode. Basically it reads the room. ๐Ÿ‘‘
  • PROGRAM โ€” Added in version 3.19. Here's the slay: the first item in <args> is assumed to be an executable, and it will be searched in the system search path โ€” or left as a full path. If it's not found, <variable> will be empty. Otherwise, <variable> is a list of 2 elements:
    1. Absolute path of the program
    2. Any command-line arguments present in <args> as a string

For example:

separate_arguments (out UNIX_COMMAND PROGRAM "cc -c main.c")
  • First element of the list: /path/to/cc
  • Second element of the list: " -c main.c"
  • SEPARATE_ARGS โ€” When this sub-option of the PROGRAM option is specified, command-line arguments will be split as well and stored in <variable>. Extra splitting! We love options. ๐Ÿ’–

For example:

separate_arguments (out UNIX_COMMAND PROGRAM SEPARATE_ARGS "cc -c main.c")

The contents of out will be: /path/to/cc;-c;main.c

the two-argument form ๐ŸŽ€

separate_arguments(<var>)

Convert the value of <var> to a semi-colon separated list. All spaces are replaced with ;. This helps with generating command lines. Simple, clean, iconic. โœจ


๐Ÿ’œ set ๐Ÿ’œ

Set a normal, cache, or environment variable to a given value. Main character energy โ€” this command does it all. ๐Ÿ‘‘

See the cmake-language(7) variables documentation for the scopes and interaction of normal variables and cache entries.

Signatures of this command that specify a <value>... placeholder expect zero or more arguments. Multiple arguments will be joined as a semicolon-separated list to form the actual variable value to be set. So like, they all get smooshed together with semicolons. Fr fr. ๐Ÿ“


๐ŸŒˆ Set Normal Variable ๐ŸŒˆ

set(<variable> <value>... [PARENT_SCOPE])

Set or unset <variable> in the current function or directory scope:

  • If at least one <value>... is given, set the variable to that value.
  • If no value is given, unset the variable. This is equivalent to unset(<variable>).

If the PARENT_SCOPE option is given, the variable will be set in the scope above the current scope. Okay so every new directory or function() command creates a new scope. And a scope can also be created with the block() command. set(PARENT_SCOPE) will set the value of a variable into the parent directory, calling function, or encompassing scope โ€” whichever is applicable to the case at hand. ๐Ÿงš

Important detail bestie: the previous state of the variable's value stays the same in the current scope. Like, if it was undefined before, it's still undefined; if it had a value, it's still that value. No take-backs in the current scope. ๐Ÿ˜Œ

The block(PROPAGATE) and return(PROPAGATE) commands can be used as an alternate method to the set(PARENT_SCOPE) and unset(PARENT_SCOPE) commands to update the parent scope. Options, options, options. ๐Ÿ’…

Note: When evaluating variable references of the form ${VAR}, CMake first searches for a normal variable with that name. If no such normal variable exists, CMake will then search for a cache entry with that name. Because of this, unsetting a normal variable can expose a cache variable that was previously hidden. ๐Ÿ™ˆ To force a variable reference of the form ${VAR} to return an empty string, use set(<variable> ""), which clears the normal variable but leaves it defined.

๐Ÿฆ‹ Set Cache Entry ๐Ÿฆ‹

set(CACHE{<variable>} [TYPE <type>] [HELP <helpstring>...] [FORCE] VALUE [<value>...])

Added in version 4.2. Sets the given cache <variable> (cache entry). The options are:

  • TYPE <type> โ€” Specify the type of the cache entry. The <type> must be one of:
    • BOOL โ€” Boolean ON/OFF value. cmake-gui(1) offers a checkbox. โœ…
    • FILEPATH โ€” Path to a file on disk. cmake-gui(1) offers a file dialog.
    • PATH โ€” Path to a directory on disk. cmake-gui(1) offers a file dialog.
    • STRING โ€” A line of text. cmake-gui(1) offers a text field or a drop-down selection if the STRINGS cache entry property is set.
    • INTERNAL โ€” A line of text. cmake-gui(1) does not show internal entries. They may be used to store variables persistently across runs. Use of this type implies FORCE.

If TYPE is not specified: if the cache variable already exists and its type is not UNINITIALIZED, the type previously specified will be kept โ€” otherwise, STRING will be used. Default behavior, we love her. ๐Ÿ’–

  • HELP <helpstring>... โ€” The <helpstring> must be specified as a line of text providing a quick summary of the option for presentation to cmake-gui(1) users. If more than one string is given, they are concatenated into a single string with no separator between them. (No separator. Not even a space. Just glued together. ๐Ÿ˜ญ) If HELP is not specified, an empty string will be used.
  • FORCE โ€” Since cache entries are meant to provide user-settable values, this does not overwrite existing cache entries by default. Use the FORCE option to overwrite existing entries. Use it responsibly, bestie. ๐Ÿ’€
  • VALUE <value>... โ€” List of values to be set to the cache <variable>. This argument must always be the last one. Periodt. ๐Ÿ”ฅ

If the cache entry does not exist prior to the call, or the FORCE option is given, then the cache entry will be set to the given value.

Note: The content of the cache variable will not be directly accessible if a normal variable of the same name already exists (see rules of variable evaluation). If policy CMP0126 is set to OLD, any normal variable binding in the current scope will be removed.

It is possible for the cache entry to exist prior to the call but have no type set โ€” that happens if it was created on the cmake(1) command line by a user through the -D<var>=<value> option without specifying a type. In this case the set command will add the type. ๐ŸŽ€ Furthermore, if the <type> is PATH or FILEPATH and the <value> provided on the command line is a relative path, then the set command will treat the path as relative to the current working directory and convert it to an absolute path. Slay.

set(<variable> <value>... CACHE <type> <docstring> [FORCE])

This signature is supported for compatibility purpose. Use preferably the other one. ๐Ÿ‘‘ (that's the polite way of saying "the new one ate, use that")


๐Ÿ’… Set Environment Variable ๐Ÿ’…

set(ENV{<variable>} [<value>])

Sets an Environment Variable to the given value. Subsequent calls of $ENV{<variable>} will return this new value. ๐ŸŒˆ

Okay but here's the catch, and it's lowkey important: this command affects only the current CMake process. Not the process from which CMake was called, not the system environment at large, and not the environment of subsequent build or test processes. Just this one process. That's it. ๐Ÿซถ

If no argument is given after ENV{<variable>}, or if <value> is an empty string, then this command will clear any existing value of the environment variable.

Arguments after <value> are ignored. If extra arguments are found, then an author warning is issued. (Not me forgetting that and getting yelled at by CMake ๐Ÿ™ˆ)


๐ŸŒธ See Also ๐ŸŒธ

  • unset() โ€” the opposite bestie, go check her out

๐ŸŽ€ set_directory_properties ๐ŸŽ€

Set properties of the current directory and subdirectories. Passing those properties down like hand-me-downs, but make it CMake. ๐Ÿ’–

set_directory_properties(PROPERTIES <prop1> <value1> [<prop2> <value2>] ...)

Sets properties of the current directory and its subdirectories in key-value pairs. Very neat, very organized. โœจ

See also the set_property(DIRECTORY) command.

See Properties on Directories for the list of properties known to CMake and their individual documentation for the behavior of each property. (Go read those, they're important, no cap.)

๐ŸŒˆ See Also ๐ŸŒˆ

  • define_property()
  • get_directory_property()
  • the more general set_property() command โ€” she's the versatile one ๐Ÿ‘‘

โญ set_property โญ

Set a named property in a given scope.

...and here's the full shape of it, bestie:

set_property({GLOBAL                                    |
              DIRECTORY [<dir>]                         |
              TARGET    <target>...                     |
              FILE_SET  <file_set>... TARGET <target>   |
              SOURCE    <source>...
                        [DIRECTORY <dirs> ...]
                        [TARGET_DIRECTORY <targets>...] |
              INSTALL   <file>...                       |
              TEST      <test>...
                        [DIRECTORY <dir>]               |
              CACHE     <entry>...}
             [APPEND] [APPEND_STRING]
             PROPERTY <name> [<value>...])

Sets one property on zero or more objects of a scope. Zero is allowed, yes โ€” it's giving "no thoughts, head empty, still valid." ๐Ÿ’…

The first argument determines the scope in which the property is set. It must be one of the following, no substitutions:

  • GLOBAL โ€” Scope is unique and does not accept a name. One global, no last name needed. ๐Ÿ‘‘
  • DIRECTORY โ€” Scope defaults to the current directory, but other directories (already processed by CMake) may be named by full or relative path. Relative paths are treated as relative to the current source directory. See also the set_directory_properties() command.
    • Added in version 3.19: <dir> may reference a binary directory.
  • TARGET โ€” Scope may name zero or more existing targets. See also the set_target_properties() command.
    • Alias Targets do not support setting target properties. like, they said no. respect it. ๐Ÿ™ˆ
  • FILE_SET โ€” Added in version 4.3.
    • Scope may name zero or more existing file sets.
    • The following option is required:
      • TARGET <target> โ€” The target to which the file set is attached.
  • SOURCE โ€” Scope may name zero or more source files. By default, source file properties are only visible to targets added in the same directory (CMakeLists.txt).
    • Added in version 3.18: Visibility can be set in other directory scopes using one or both of the following sub-options:
      • DIRECTORY <dirs>... โ€” The source file property will be set in each of the <dirs> directories' scopes. CMake must already know about each of these directories, either by having added them through a call to add_subdirectory() or it being the top level source directory. Relative paths are treated as relative to the current source directory.
        • Added in version 3.19: <dirs> may reference a binary directory.
      • TARGET_DIRECTORY <targets>... โ€” The source file property will be set in each of the directory scopes where any of the specified <targets> were created (the <targets> must therefore already exist).
    • See also the set_source_files_properties() command.
  • INSTALL โ€” Added in version 3.1.
    • Scope may name zero or more installed file paths. These are made available to CPack to influence deployment.
    • Both the property key and value may use generator expressions. Specific properties may apply to installed files and/or directories.
    • Path components have to be separated by forward slashes, must be normalized and are case sensitive. so no \ shenanigans, we're not doing that today ๐Ÿ’€
    • To reference the installation prefix itself with a relative path use ..
    • Currently installed file properties are only defined for the WIX generator where the given paths are relative to the installation prefix.
  • TEST โ€” Scope is limited to the directory the command is called in. It may name zero or more existing tests. See also command set_tests_properties().
    • Test property values may be specified using generator expressions for tests created by the add_test(NAME) signature.
    • Added in version 3.28: Visibility can be set in other directory scopes using the following sub-option:
      • DIRECTORY <dir> โ€” The test property will be set in the <dir> directory's scope. CMake must already know about this directory, either by having added it through a call to add_subdirectory() or it being the top level source directory. Relative paths are treated as relative to the current source directory. <dir> may reference a binary directory.
  • CACHE โ€” Scope must name zero or more existing cache entries.

The required PROPERTY option is immediately followed by the name of the property to set. Remaining arguments are used to compose the property value in the form of a semicolon-separated list. โœจ

If the APPEND option is given the list is appended to any existing property value (except that empty values are ignored and not appended โ€” they get ghosted ๐Ÿ‘ป). If the APPEND_STRING option is given the string is appended to any existing property value as string, i.e. it results in a longer string and not a list of strings. When using APPEND or APPEND_STRING with a property defined to support INHERITED behavior (see define_property()), no inheriting occurs when finding the initial value to append to. If the property is not already directly set in the nominated scope, the command will behave as though APPEND or APPEND_STRING had not been given.

Note: The GENERATED source file property may be globally visible. See its documentation for details. iykyk ๐Ÿซถ

See Also

  • define_property()
  • get_property()
  • The cmake-properties(7) manual for a list of properties in each scope.

๐ŸŽ€ site_name

Set the given variable to the name of the computer. literally just "who am i" but as a variable ๐Ÿ–ฅ๏ธโœจ

site_name(variable)

On UNIX-like platforms, if the variable HOSTNAME is set, its value will be executed as a command expected to print out the host name, much like the hostname command-line tool. yes bestie it executes it, that's the assignment, don't @ me.


๐ŸŒˆ string

String operations. the whole buffet, one command. ๐Ÿ“

Synopsis

Search and Replace
  string(FIND <string> <substring> <out-var> [...])
  string(REPLACE <match-string> <replace-string> <out-var> <input>...)
  string(REGEX MATCH <match-regex> <out-var> <input>...)
  string(REGEX MATCHALL <match-regex> <out-var> <input>...)
  string(REGEX REPLACE <match-regex> <replace-expr> <out-var> <input>...)

Manipulation
  string(APPEND <string-var> [<input>...])
  string(PREPEND <string-var> [<input>...])
  string(CONCAT <out-var> [<input>...])
  string(JOIN <glue> <out-var> [<input>...])
  string(TOLOWER <string> <out-var>)
  string(TOUPPER <string> <out-var>)
  string(LENGTH <string> <out-var>)
  string(SUBSTRING <string> <begin> <length> <out-var>)
  string(STRIP <string> <out-var>)
  string(GENEX_STRIP <string> <out-var>)
  string(REPEAT <string> <count> <out-var>)
  string(REGEX QUOTE <out-var> <input>...)

Comparison
  string(COMPARE <op> <string1> <string2> <out-var>)

Hashing
  string(<HASH> <out-var> <input>)

Generation
  string(ASCII <number>... <out-var>)
  string(HEX <string> <out-var>)
  string(CONFIGURE <string> <out-var> [...])
  string(MAKE_C_IDENTIFIER <string> <out-var>)
  string(RANDOM [<option>...] <out-var>)
  string(TIMESTAMP <out-var> [<format string>] [UTC])
  string(UUID <out-var> ...)

JSON
  string(JSON <out-var> [ERROR_VARIABLE <error-var>]
         {GET | GET_RAW | TYPE | LENGTH}
         <json-string> [<member|index> ...])
  string(JSON <out-var> [ERROR_VARIABLE <error-var>]
         REMOVE
         <json-string> <member|index> [<member|index> ...])
  string(JSON <out-var> [ERROR_VARIABLE <error-var>]
         MEMBER <json-string>
         [<member|index> ...] <index>)
  string(JSON <out-var> [ERROR_VARIABLE <error-var>]
         SET <json-string>
         <member|index> [<member|index> ...] <value>)
  string(JSON <out-var> [ERROR_VARIABLE <error-var>]
         EQUAL <json-string1> <json-string2>)
  string(JSON <out-var> [ERROR_VARIABLE <error-var>]
         PARTIAL_EQUAL <json-string1> <json-string2>)
  string(JSON <out-var> [ERROR_VARIABLE <error-var>]
         STRING_ENCODE <string>)

๐Ÿ” Search and Replace

Search and Replace With Plain Strings

string(FIND <string> <substring> <output_variable> [REVERSE])

Return the position where the given <substring> was found in the supplied <string>. If the REVERSE flag was used, the command will search for the position of the last occurrence of the specified <substring>. If the <substring> is not found, a position of -1 is returned.

The string(FIND) subcommand treats all strings as ASCII-only characters. The index stored in <output_variable> will also be counted in bytes, so strings containing multi-byte characters may lead to unexpected results. โš ๏ธ multi-byte besties, check yourselves before you wreck yourselves.

string(REPLACE <match_string> <replace_string> <output_variable> <input> [<input>...])

Replace all occurrences of <match_string> in the <input> with <replace_string> and store the result in the <output_variable>.

Search and Replace With Regular Expressions

string(REGEX MATCH <regular_expression> <output_variable> <input> [<input>...])

Match the <regular_expression> once and store the match in the <output_variable>. All <input> arguments are concatenated before matching. Regular expressions are specified in the subsection just below. one (1) match, that's the whole vibe.

string(REGEX MATCHALL <regular_expression> <output_variable> <input> [<input>...])

Match the <regular_expression> as many times as possible and store the matches in the <output_variable> as a list. All <input> arguments are concatenated before matching. collect them all like they're Pokรฉmon ๐Ÿ”ฅ

string(REGEX REPLACE <regular_expression> <replacement_expression> <output_variable> <input> [<input>...])

Match the <regular_expression> as many times as possible and substitute the <replacement_expression> for the match in the output. All <input> arguments are concatenated before matching.

The <replacement_expression> may refer to parenthesis-delimited subexpressions of the match using \1, \2, ..., \9. Note that two backslashes (\\1) are required in CMake code to get a backslash through argument parsing. fr fr, the escaping will humble you. ๐Ÿ˜ญ

okay so the ^ anchor literally got a glow-up in 4.1: it matches only at the beginning of the input string now, not at the start of every repeated search. The way that's a whole personality change ๐Ÿ˜ญ โ€” go peek at policy CMP0186 if you need the receipts.

Zero-length matches are allowed in MATCHALL and REPLACE now. Bestie, they used to throw an error for that. Growth. ๐ŸŒฑ

And your replacement expression can reference subexpressions that didn't match anything. Previously? error. Now? it just vibes. ๐Ÿ’…


๐Ÿฆ‹ Regex Specification ๐Ÿฆ‹

These characters are the main characters of regex โ€” they have special meaning:

  • ^ โ€” matches at beginning of input.
  • $ โ€” matches at end of input.
  • . โ€” matches any single character.
  • \<char> โ€” matches the single character specified by <char>. Use this to match special regex characters, e.g. \. for a literal ., or \\ for a literal backslash \. Escaping a non-special character is unnecessary but allowed, e.g. \a matches a.
  • [ ] โ€” matches any character(s) inside the brackets. To match a literal ], make it the first character, e.g., []ab].
  • [^ ] โ€” matches any character(s) not inside the brackets. To not match a literal ], make it the first character, e.g., [^]ab].
  • - โ€” inside brackets, specifies an inclusive range between the characters on either side, e.g., [a-f] is [abcdef]. To match a literal - using brackets, make it the first or the last character, e.g., [+*/-] matches basic mathematical operators.
  • * โ€” matches preceding pattern zero or more times.
  • + โ€” matches preceding pattern one or more times.
  • ? โ€” matches preceding pattern zero or once only.
  • | โ€” matches a pattern on either side of the |.
  • () โ€” saves a matched subexpression, which can be referenced in the REGEX REPLACE operation.
Added in version 3.9: All regular expression-related commands โ€” including, like, if(MATCHES) โ€” save subgroup matches in the variables CMAKE_MATCH_<n> for <n> 0..9. No cap, group chat memory unlocked. ๐Ÿซถ

Precedence tea โ˜•: *, + and ? outrank concatenation. | sits below concatenation. So the regex ^ab+d$ matches abbd but not ababd, and the regex ^(ab|cd)$ matches ab but not abd. Slay, that's just how it eats. ๐Ÿ”ฅ

CMake language Escape Sequences like \t, \r, \n, and \\ may be used to construct literal tabs, carriage returns, newlines, and backslashes (respectively) to pass in a regex. For example:

  • The quoted argument "[ \t\r\n]" specifies a regex that matches any single whitespace character.
  • The quoted argument "[/\\]" specifies a regex that matches a single forward slash / or backslash \.
  • The quoted argument "[A-Za-z0-9_]" specifies a regex that matches any single "word" character in the C locale.
  • The quoted argument "\\(\\a\\+b\\)" specifies a regex that matches the exact string (a+b). Each \\ is parsed in a quoted argument as just \, so the regex itself is actually \(\a\+\b\). This can alternatively be specified in a Bracket Argument without having to escape the backslashes, e.g. [[\(\a\+\b\)]].

๐Ÿ’– Manipulation ๐Ÿ’–

  • string(APPEND <string_variable> [<input>...]) โ€” Added in version 3.4. Append all the <input> arguments to the string. Pile it on, bestie ๐Ÿ’…
  • string(PREPEND <string_variable> [<input>...]) โ€” Added in version 3.10. Prepend all the <input> arguments to the string. Front of the line energy. ๐Ÿ‘‘
  • string(CONCAT <output_variable> [<input>...]) โ€” Concatenate all the <input> arguments together and store the result in the named <output_variable>.
  • string(JOIN <glue> <output_variable> [<input>...]) โ€” Added in version 3.12. Join all the <input> arguments together using the <glue> string and store the result in the named <output_variable>.
Bestie, to join a list's elements, prefer the JOIN operator from the list() command. That one lets the elements have special characters like ; in them. iykyk ๐Ÿงš
  • string(TOLOWER <string> <output_variable>) โ€” Convert <string> to lower characters. lowercase aesthetic fr fr.
  • string(TOUPPER <string> <output_variable>) โ€” Convert <string> to upper characters. LOUDER. ๐Ÿ“ฃ
  • string(LENGTH <string> <output_variable>) โ€” Store in an <output_variable> a given string's length in bytes. Note that this means if <string> contains multi-byte characters, the result stored in <output_variable> will not be the number of characters. Bytes โ‰  characters, don't let it gaslight you. ๐Ÿ’€
  • string(SUBSTRING <string> <begin> <length> <output_variable>) โ€” Store in an <output_variable> a substring of a given <string>. If <length> is -1 the remainder of the string starting at <begin> will be returned.
Changed in version 3.2: If <string> is shorter than <length> then the end of the string is used instead. Previous versions of CMake reported an error in this case.

Both <begin> and <length> are counted in bytes, so care must be exercised if <string> could contain multi-byte characters. ๐Ÿ›

  • string(STRIP <string> <output_variable>) โ€” Store in an <output_variable> a substring of a given <string> with leading and trailing spaces removed. Trim the toxic energy โœจ
  • string(GENEX_STRIP <string> <output_variable>) โ€” Added in version 3.1. Strip any generator expressions from the input <string> and store the result in the <output_variable>.
  • string(REPEAT <string> <count> <output_variable>) โ€” Added in version 3.15. Produce the output string as the input <string> repeated <count> times. Spam that group chat ๐ŸŽ€
  • string(REGEX QUOTE <out-var> <input>...) โ€” Added in version 4.2. Store in an <out-var> a regular expression matching the <input>. All characters that have special meaning in a regular expression are escaped, such that the output string can be used as part of a regular expression to match the input literally. Escape room but make it regex ๐Ÿ”

๐ŸŒˆ Comparison ๐ŸŒˆ

  • string(COMPARE LESS <string1> <string2> <output_variable>)
  • string(COMPARE GREATER <string1> <string2> <output_variable>)
  • string(COMPARE EQUAL <string1> <string2> <output_variable>)
  • string(COMPARE NOTEQUAL <string1> <string2> <output_variable>)
  • string(COMPARE LESS_EQUAL <string1> <string2> <output_variable>)
  • string(COMPARE GREATER_EQUAL <string1> <string2> <output_variable>)

Compare the strings and store true or false in the <output_variable>. Simple as that, bestie ๐Ÿ’–

Added in version 3.7: Added the LESS_EQUAL and GREATER_EQUAL options. The glow-up we deserved. ๐Ÿ‘‘

๐Ÿ”ฅ Hashing ๐Ÿ”ฅ

  • string(<HASH> <output_variable> <input>) โ€” Compute a cryptographic hash of the <input> string.

The supported <HASH> algorithm names are:

  • MD5 โ€” Message-Digest Algorithm 5, RFC 1321.
  • SHA1 โ€” US Secure Hash Algorithm 1, RFC 3174.
  • SHA224 โ€” US Secure Hash Algorithms, RFC 4634.
  • SHA256 โ€” US Secure Hash Algorithms, RFC 4634.
  • SHA384 โ€” US Secure Hash Algorithms, RFC 4634.
  • SHA512 โ€” US Secure Hash Algorithms, RFC 4634.
  • SHA3_224 โ€” Keccak SHA-3.
  • SHA3_256 โ€” Keccak SHA-3.
  • SHA3_384 โ€” Keccak SHA-3.
  • SHA3_512 โ€” Keccak SHA-3.
Added in version 3.8: Added the SHA3_* hash algorithms. They're giving new-gen cryptography. ๐ŸŒธ

๐ŸŽ€ Generation ๐ŸŽ€

  • string(ASCII <number> [<number> ...] <output_variable>) โ€” Convert all numbers into corresponding ASCII characters.
  • string(HEX <string> <output_variable>) โ€” Added in version 3.18. Convert each byte in the input <string> to its hexadecimal representation and store the concatenated hex digits in the <output_variable>. Letters in the output (a through f) are in lowercase. lowkey aesthetic. ๐Ÿ“
  • string(CONFIGURE <string> <output_variable> [@ONLY] [ESCAPE_QUOTES]) โ€” Transform a <string> like configure_file() transforms a file.
  • string(MAKE_C_IDENTIFIER <string> <output_variable>) โ€” Convert each non-alphanumeric character in the input <string> to an underscore and store the result in the <output_variable>. If the first character of the <string> is a digit, an underscore will also be prepended to the result. Cute, legal, C-ified ๐Ÿ’…
  • string(RANDOM [LENGTH <length>] [ALPHABET <alphabet>] [RANDOM_SEED <seed>] <output_variable>) โ€” Return a random string of given <length> consisting of characters from the given <alphabet>. Default length is 5 characters and default alphabet is all numbers and upper and lower case letters. If an integer RANDOM_SEED is given, its value will be used to seed the random number generator. Chaos, but make it reproducible ๐ŸŽฒ
  • string(TIMESTAMP <output_variable> [<format_string>] [UTC]) โ€” Write a string representation of the current date and/or time to the <output_variable>.

If the command is unable to obtain a timestamp, the <output_variable> will be set to the empty string "".

The optional UTC flag requests the current date/time representation to be in Coordinated Universal Time (UTC) rather than local time. ๐ŸŒ

The optional <format_string> may contain the following format specifiers:

  • %% โ€” Added in version 3.8. A literal percent sign (%).
  • %d โ€” The day of the current month (01-31).
  • %H โ€” The hour on a 24-hour clock (00-23).
  • %I โ€” The hour on a 12-hour clock (01-12).
  • %j โ€” The day of the current year (001-366).
  • %m โ€” The month of the current year (01-12).
  • %b โ€” Added in version 3.7. Abbreviated month name (e.g. Oct).
  • %B โ€” Added in version 3.10. Full month name (e.g. October).
  • %M โ€” The minute of the current hour (00-59).
  • %s โ€” Added in version 3.6. Seconds since midnight (UTC) 1-Jan-1970 (UNIX time).
  • %S โ€” The second of the current minute. 60 represents a leap second. (00-60)
  • %f โ€” Added in version 3.23. The microsecond of the current second (000000-999999).
  • %U โ€” The week number of the current year (00-53).
  • %V โ€” Added in version 3.22. The ISO 8601 week number of the current year (01-53).
  • %w โ€” The day of the current week. 0 is Sunday. (0-6)
  • %a โ€” Added in version 3.7. Abbreviated weekday name (e.g. Fri).
  • %A โ€” Added in version 3.10. Full weekday name (e.g. Friday).
  • %y โ€” The last two digits of the current year (00-99).
  • %Y โ€” The current year.
  • %z โ€” Added in version 3.26. The offset of the time zone from UTC, in hours and minutes, with format +hhmm or -hhmm.
  • %Z โ€” Added in version 3.26.
  • The time zone name.

Unknown format specifiers don't throw hands โ€” they just get ignored and copied to the output as-is. ๐Ÿ’…

If you don't pass an explicit <format_string>, it lowkey defaults to:

  • %Y-%m-%dT%H:%M:%S for local time.
  • %Y-%m-%dT%H:%M:%SZ for UTC.
Added in version 3.8: if the SOURCE_DATE_EPOCH environment variable is set, its value gets used instead of the current time. See <https://reproducible-builds.org/specs/source-date-epoch/> for the full tea. ๐Ÿซถ
string(UUID <output_variable> NAMESPACE <namespace> NAME <name> TYPE
       <MD5|SHA1> [UPPER])
Added in version 3.1.

Makes a universally unique identifier (aka a GUID, yes bestie, the same thing) as per RFC4122, built from the hash of <namespace> (which itself has to be a valid UUID, no faking it) combined with <name>. The hash algorithm is either MD5 (that's a Version 3 UUID) or SHA1 (Version 5 UUID, my beloved โœจ). A UUID looks like xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx where every x is a lower case hexadecimal character. If you need it shouty, the optional UPPER flag gives you the uppercase representation. ๐Ÿ‘‘


JSON ๐Ÿฆ‹

Added in version 3.19.

Functionality for querying a JSON string. Yes, CMake does JSON now, we're thriving.

Note: in each of the following JSON-related subcommands, if the optional ERROR_VARIABLE argument is given, errors get reported in <error-variable> and the <out-var> is set to <member|index>-[<member|index>...]-NOTFOUND with the path elements up to the point where the error happened, or just NOTFOUND if there's no relevant path. If an error occurs but the ERROR_VARIABLE option isn't there, you get a fatal error message โ€” like, the whole thing dies ๐Ÿ˜ญ. And if nothing goes wrong, <error-variable> gets set to NOTFOUND.

In the following subcommands, write the <json-string> argument as a Quoted Argument <#quoted-argument> so the entire JSON string gets passed as a single argument even if it's full of semicolons. Semicolons are menace behavior, protect yourself. ๐Ÿ’€

GET

string(JSON <out-var> [ERROR_VARIABLE <error-variable>] GET
       <json-string> [<member|index> ...])

Grabs an element out of <json-string> at the location given by the list of <member|index> arguments. Array and object elements come back as a JSON string. Boolean elements come back as ON or OFF. Null elements come back as an empty string. Number and string types come back as strings. It's giving type flattening, but make it fashion. ๐Ÿ’…

GET_RAW

string(JSON <out-var> [ERROR_VARIABLE <error-variable>] GET_RAW
       <json-string> [<member|index> ...])
Added in version 4.3.

Grabs an element out of <json-string> at the location given by the list of <member|index> arguments. Similar to GET, but it does not convert number, string, boolean, or null elements. Raw dogging the JSON, respectfully. ๐Ÿ™ˆ

TYPE

string(JSON <out-var> [ERROR_VARIABLE <error-variable>] TYPE
       <json-string> [<member|index> ...])

Gets the type of an element in <json-string> at the location given by the list of <member|index> arguments. <out-var> gets set to one of NULL, NUMBER, STRING, BOOLEAN, ARRAY, or OBJECT. Six options, all iconic. ๐ŸŒˆ

MEMBER

string(JSON <out-var> [ERROR_VARIABLE <error-var>] MEMBER <json-string>
       [<member|index> ...] <index>)

Gets the name of the <index>-th member in <json-string> at the location given by the list of <member|index> arguments. Requires an element of object type, so don't try this on an array bestie, it will not slay. ๐ŸŽ€

LENGTH

string(JSON <out-var> [ERROR_VARIABLE <error-variable>] LENGTH
       <json-string> [<member|index> ...])

Gets the length of an element in <json-string> at the location given by the list of <member|index> arguments. Requires an element of array or object type โ€” those are the only two things that even have a length, no cap.

REMOVE

string(JSON <out-var> [ERROR_VARIABLE <error-variable>] REMOVE
       <json-string> <member|index> [<member|index> ...])

Removes an element from <json-string> at the location given by the list of <member|index> arguments. The JSON string without the removed element gets stored in <out-var>. It's giving "you're not invited to the group chat anymore". ๐Ÿซถ

SET

string(JSON <out-var> [ERROR_VARIABLE <error-variable>] SET
       <json-string> <member|index> [<member|index> ...] <value>)

Sets an element in <json-string> at the location given by the list of <member|index> arguments to <value>. The contents of <value> should be valid JSON โ€” fr fr, don't be out here passing vibes. If <json-string> is an array, you can append <value> to the end of the array by using a number greater than or equal to the array length as the <member|index> argument. โœจ

EQUAL

string(JSON <out-var> [ERROR_VARIABLE <error-var>] EQUAL <json-string1>
       <json-string2>)

Compares the two JSON objects given by <json-string1> and <json-string2> for equality. The contents of <json-string1> and <json-string2> should be valid JSON. <out-var> gets set to a true value if the JSON objects are considered equal, or a false value otherwise. Simple, clean, no drama. ๐Ÿ’–

PARTIAL_EQUAL

string(JSON <out-var> [ERROR_VARIABLE <error-var>] PARTIAL_EQUAL
       <json-pattern> <json-actual>)
Added in version 4.4.

Compares two JSON values using partial equality. The first argument (<json-pattern>) is treated as a pattern to match against the second argument (<json-actual>), which is treated as the complete JSON value. The contents of <json-pattern> and <json-actual> must be valid JSON.

<out-var> gets set to a true value if <json-pattern> is contained within <json-actual> according to the rules below, or a false value otherwise.

Heads up โš ๏ธ: partial equality is not symmetric. In general, PARTIAL_EQUAL(A, B) and PARTIAL_EQUAL(B, A) may produce different results. Yes, order matters, it's that kind of function.

Matching rules:

  • Scalars (null, number, string, boolean) must match exactly. No rounding, no "close enough".
  • Objects match if every member in <json-pattern> exists in <json-actual> and the corresponding values also match recursively. Extra members in <json-actual> are ignored โ€” they're just bonus content, we don't gatekeep.
  • Arrays match if every element in <json-pattern> matches a distinct element in <json-actual> recursively. Once an element in <json-actual> successfully matches an element in <json-pattern>, it is consumed and cannot be reused to match any subsequent elements. Array element order is not significant. Extra elements in <json-actual> are ignored. So it's like, one dance partner each, periodt. ๐Ÿ’…
  • Type differences do not match (for example, an object does not match an array, and a string does not match a number). Obviously, babe.

STRING_ENCODE

string(JSON <out-var> [ERROR_VARIABLE <error-var>] STRING_ENCODE
       <string>)
Added in version 4.3.

Turns a raw string into a JSON string surrounded by quotes. Special characters get properly escaped inside the JSON string โ€” no more broken quotes ruining your whole build. ๐Ÿงš


unset

Unset a variable, cache variable, or environment variable. Three flavors, pick your fighter.

Unset Normal Variable

unset(<variable> [PARENT_SCOPE])

Removes a normal variable from the current scope, causing it to become undefined. Poof. Gone. Ghosted. ๐Ÿ‘ป

If PARENT_SCOPE is present then the variable gets removed from the scope above the current scope. See the same option in the set() <#command:set> command for further details.

Note: when evaluating Variable References <#variable-references> of the form ${VAR}, CMake first searches for a normal variable with that name. If no such normal variable exists, CMake then searches for a cache entry with that name. Because of this, unsetting a normal variable can expose a cache variable that was previously hidden โ€” like pulling back the curtain and finding a whole other variable living there ๐Ÿ˜ญ. To force a variable reference of the form ${VAR} to return an empty string, use set(<variable> ""), which clears the normal variable but leaves it defined. Different vibe entirely.

Unset Cache Entry

unset(CACHE{<variable>})
Added in version 4.2.

Removes <variable> from the cache, causing it to become undefined.

unset(<variable> CACHE)

This signature is supported for compatibility purpose. Use preferably the other one. Yes, the older one is still here, living its legacy era, but we're gently nudging you toward the newer form. ๐ŸŽ€

Unset Environment Variable

unset(ENV{<variable>})

Removes <variable> from the currently available Environment Variables <#manual:cmake-env-variables(7)>. Subsequent calls of $ENV{<variable>} will return the empty string.

This command affects only the current CMake process โ€” not the process from which CMake was called, nor the system environment at large, nor the environment of subsequent build or test processes. So like, it's a local vibe, not a global one. Keep that in mind before you blame CMake for your shell looking weird. ๐Ÿ™ˆ

See Also

  • set() <#command:set>

variable_watch

Watch the CMake variable for change. It's giving surveillance but make it helpful. ๐Ÿ‘€โœจ

variable_watch(<variable> [<command>])

If the specified <variable> changes and no <command> is given, a message gets printed to inform you about the change.

If <command> is given, that command gets executed instead. The command receives the following arguments: COMMAND(<variable> <access> <value> <current_list_file> <stack>)

<variable> : Name of the variable being accessed.

<access> : One of READ_ACCESS, UNKNOWN_READ_ACCESS, MODIFIED_ACCESS, UNKNOWN_MODIFIED_ACCESS, or REMOVED_ACCESS. The UNKNOWN_ values are only used when the variable has never been set. Once set, they are never used again during the same CMake run, even if the variable is later unset. One and done, no takebacks. ๐Ÿ’€

<value> : The value of the variable. On a modification, this is the new (modified) value of the variable. On removal, the value is empty. Sad little empty string.

<current_list_file> : Full path to the file doing the access.

<stack> : List of absolute paths of all files currently on the stack of file inclusion, with the bottom-most file first and the currently processed file (that is, current_list_file) last. Bottom to top, like a stack of pancakes. ๐Ÿฅž

Note that for some accesses such as list(APPEND) <#append>, the watcher runs twice โ€” first with a read access and then with a write one. Also note that an if(DEFINED) <#defined> query on the variable does not register as an access and the watcher is not executed. Sneaky little query, flying under the radar. ๐Ÿฆ‹

Only non-cache variables can be watched using this command. Access to cache variables is never watched. However, the existence of a cache variable var causes accesses to the non-cache variable var to not use the UNKNOWN_ prefix, even if a non-cache variable var has never existed. Yeah, it's messy, that's just how it is bestie. ๐Ÿ’…


while

Evaluate a group of commands while a condition is true. The original loop-de-loop. ๐Ÿ”

while(<condition>)
  <commands>
endwhile()

All commands between while and the matching endwhile() <#command:endwhile> are recorded without being invoked โ€” they're just sitting there, waiting, main character energy but no screentime yet. Once the endwhile() <#command:endwhile> is evaluated, the recorded list of commands gets invoked as long as the <condition> is true. โœจ

The <condition> has the same syntax and is evaluated using the same logic as described at length for the if() <#command:if> command. So go read that one, iykyk. ๐Ÿซถ

The commands break() and continue() are literally your escape hatches from the normal control flow โ€” think of them as the "bestie I gotta go" of CMake loops. ๐Ÿ’จ

Per legacy (yes, the old-school vibes), the endwhile() command admits an optional <condition> argument. If you use it, it must be a verbatim repeat of the argument of the opening while command. No paraphrasing, no remixing โ€” copy it exactly, periodt. ๐Ÿ“‹

๐Ÿ‘€ See Also

  • break()
  • continue()
  • foreach()
  • endwhile()

๐Ÿ› ๏ธ PROJECT COMMANDS ๐Ÿ› ๏ธ

Okay so these commands are available only in CMake projects. Not in script mode, bestie โ€” projects only. ๐Ÿ’…

๐Ÿท๏ธ add_compile_definitions

Added in version 3.12. โœจ

Add preprocessor definitions to the compilation of source files. It's giving "let me tell the compiler about my lore" energy.

add_compile_definitions(<definition> ...)

Adds preprocessor definitions to the compiler command line. Fr fr, that's the whole bit.

The preprocessor definitions get added to the COMPILE_DEFINITIONS directory property for the current CMakeLists file. They're also added to the COMPILE_DEFINITIONS target property for each target in the current CMakeLists file. Double the property, double the slay. ๐ŸŽ€

Definitions are specified using the syntax VAR or VAR=value. Function-style definitions are not supported, sorry bestie, that's just not on the menu. ๐Ÿ™ˆ CMake will automatically escape the value correctly for the native build system โ€” note that CMake language syntax may require escapes to specify some values, so like, watch out for that.

Added in version 3.26: any leading -D on an item will be removed. Iconic behaviour, honestly.

Arguments to add_compile_definitions may use generator expressions with the syntax $<...>. See the cmake-generator-expressions(7) manual for available expressions. See the cmake-buildsystem(7) manual for more on defining buildsystem properties. ๐Ÿ“š

๐Ÿ‘€ See Also

  • The command target_compile_definitions() adds target-specific definitions. ๐ŸŽฏ

โš™๏ธ add_compile_options

Add options to the compilation of source files. Lowkey the workhorse of the whole buildsystem. ๐Ÿ”ฅ

add_compile_options(<option> ...)

Adds options to the COMPILE_OPTIONS directory property. These options are used when compiling targets from the current directory and below. It trickles down, main character energy. ๐Ÿ‘‘

Note: these options are not used when linking. Like, at all. See the add_link_options() command for that one. ๐Ÿ”—

๐Ÿง  Arguments

Arguments to add_compile_options may use generator expressions with the syntax $<...>. See the cmake-generator-expressions(7) manual for available expressions. See the cmake-buildsystem(7) manual for more on defining buildsystem properties. Same deal as before, iykyk.

๐Ÿงน Option De-duplication

So here's the tea โ˜• โ€” the final set of options used for a target is constructed by accumulating options from the current target and the usage requirements of its dependencies. The set of options is de-duplicated to avoid repetition. Clean, tidy, no duplicates.

Added in version 3.12: while this is beneficial for individual options, the de-duplication step can break up option groups. ๐Ÿ˜ญ For example, -option A -option B becomes -option A B. Not the vibe.

One may specify a group of options using shell-like quoting along with a SHELL: prefix. The SHELL: prefix is dropped, and the rest of the option string is parsed using the separate_arguments() UNIX_COMMAND mode. For example, "SHELL:-option A" "SHELL:-option B" becomes -option A -option B. Slay, group preserved. ๐Ÿซถ

๐ŸŒธ Example

Since different compilers support different options, a typical use of this command is in a compiler-specific conditional clause:

if (MSVC)
    # warning level 4
    add_compile_options(/W4)
else()
    # additional warnings
    add_compile_options(-Wall -Wextra -Wpedantic)
endif()

To set per-language options, use the $<COMPILE_LANGUAGE> or $<COMPILE_LANGUAGE:languages> generator expressions. ๐Ÿฆ‹

๐Ÿ‘€ See Also

  • This command can be used to add any options. However, for adding preprocessor definitions and include directories it is recommended to use the more specific commands add_compile_definitions() and include_directories(). ๐Ÿ’–
  • The command target_compile_options() adds target-specific options. ๐ŸŽฏ
  • This command adds compile options for all languages. Use the COMPILE_LANGUAGE generator expression to specify per-language compile options. ๐ŸŒˆ
  • The source file property COMPILE_OPTIONS adds options to one source file. ๐Ÿ“„
  • add_link_options() adds options for linking. ๐Ÿ”—
  • CMAKE_<LANG>_FLAGS and CMAKE_<LANG>_FLAGS_<CONFIG> add language-wide flags passed to all invocations of the compiler. This includes invocations that drive compiling and those that drive linking. Literally all of them. ๐Ÿ’€

๐Ÿงฉ add_custom_command

Add a custom build rule to the generated build system. This one's a big deal, bestie โ€” buckle up. ๐ŸŽข

๐Ÿ“œ Synopsis

Generating Files:

add_custom_command(OUTPUT <output1> [<output2> ...]
                   COMMAND <command1> [ARGS] [<args1>...]
                   [...])

Build Events:

add_custom_command(TARGET <target>
                   PRE_BUILD | PRE_LINK | POST_BUILD
                   [...])

๐Ÿงฑ Generating Files

add_custom_command(OUTPUT <output1> [<output2> ...] COMMAND <command1>
[ARGS] [<args1>...] [...])

Add a custom command to produce an output:

add_custom_command(OUTPUT <output1> [<output2> ...]
                   COMMAND <command1> [ARGS] [<args1>...]
                   [COMMAND <command2> [ARGS] [<args2>...]] ...
                   [MAIN_DEPENDENCY depend]
                   [DEPENDS <depends>...]
                   [BYPRODUCTS <files>...]
                   [IMPLICIT_DEPENDS <lang1> <depend1>
                                    [<lang2> <depend2>] ...]
                   [WORKING_DIRECTORY <dir>]
                   [COMMENT <comment>]
                   [DEPFILE <depfile>]
                   [JOB_POOL <job_pool>]
                   [JOB_SERVER_AWARE <bool>]
                   [VERBATIM] [APPEND] [USES_TERMINAL]
                   [CODEGEN]
                   [COMMAND_EXPAND_LISTS]
                   [DEPENDS_EXPLICIT_ONLY])

This defines a command to generate specified OUTPUT file(s). A target created in the same directory (CMakeLists.txt file) that specifies any output of the custom command as a source file is given a rule to generate the file using the command at build time. ๐Ÿ—๏ธ

โš ๏ธ Do not list the output in more than one independent target that may build in parallel, or the instances of the rule may conflict. Instead, use the add_custom_target() command to drive the command and make the other targets depend on that one. See the Example: Generating Files for Multiple Targets below. ๐Ÿ™

The options are:

  • APPEND โ€” Append the COMMAND and DEPENDS option values to the custom command for the first output specified. There must have already been a previous call to this command with the same output.

If the previous call specified the output via a generator expression, the output specified by the current call must match in at least one configuration after evaluating generator expressions. In this case, the appended commands and dependencies apply to all configurations. ๐ŸŒ

The COMMENT, MAIN_DEPENDENCY, and WORKING_DIRECTORY options are currently ignored when APPEND is given, but may be used in the future. ๐Ÿ™ˆ (Read: don't count on them doing anything rn, but also don't be surprised later.)

  • BYPRODUCTS โ€” Added in version 3.2. โœจ

Specify the files the command is expected to produce but whose modification time may or may not be newer than the dependencies. If a byproduct name is a relative path it will be interpreted relative to the build tree directory corresponding to the current source directory. Each byproduct file will be marked with the GENERATED source file property automatically. ๐Ÿท๏ธ

See policy CMP0058 for the motivation behind this feature. ๐Ÿ“œ

Explicit specification of byproducts is supported by the Ninja generator to tell the ninja build tool how to regenerate byproducts when they are missing. It is also useful when other build rules (e.g. custom commands) depend on the byproducts. Ninja requires a build rule for any generated file on which another rule depends โ€” even if there are order-only dependencies to ensure the byproducts will be available before their dependents build. ๐Ÿฅท

The Makefile Generators will remove BYPRODUCTS and other GENERATED files during make clean. ๐Ÿงน

This keyword cannot be used with APPEND (see policy CMP0175). All byproducts must be set in the first call to add_custom_command(OUTPUT...) for the output files. No sneaking them in later, bestie. ๐Ÿšซ

Added in version 3.20: arguments to BYPRODUCTS may use a restricted set of generator expressions. Target-dependent expressions are not permitted. ๐Ÿ™…

Changed in version 3.28: in targets using File Sets, custom command byproducts are now considered private unless they are listed in a non-private file set. See policy CMP0154. ๐Ÿ”’

  • COMMAND โ€” Specify the command-line(s) to execute at build time. At least one COMMAND would normally be given, but certain patterns may omit it, such as adding commands in separate calls using APPEND. ๐Ÿงต

okay so if you pass more than one COMMAND, they all run in order โ€” but โœจheads upโœจ they are not necessarily glued together into one stateful shell or batch script. So don't expect variable carryover, bestie, it's not that kind of party. Wanna run a whole actual script? Use the configure_file() command or the file(GENERATE) command to create it, then hand COMMAND the job of launching it. Iconic workaround, honestly.

The optional ARGS argument is just there for backward compatibility and will be ignored. ๐Ÿ’€ yes literally ignored, like that one guy in the group chat.

If COMMAND names an executable target (one made by the add_executable() command), CMake will automatically swap in the location of the executable built at build time โ€” but only if either of these is true:

  • The target is not being cross-compiled (i.e. the CMAKE_CROSSCOMPILING variable is not set to true).
  • Added in version 3.6: The target is being cross-compiled and an emulator is provided (i.e. its CROSSCOMPILING_EMULATOR target property is set). In that case, the contents of CROSSCOMPILING_EMULATOR get prepended to the command, right before the location of the target executable. ๐Ÿงš

If neither of those conditions is met, CMake just assumes the command name is a program found on the PATH at build time. Simple as that, no cap.

Arguments to COMMAND may use generator expressions. Use the TARGET_FILE generator expression when you want to point at a target's location later in the command line โ€” like, as a command argument rather than as the thing being executed. Big difference, iykyk. ๐ŸŒˆ

And whenever one of these target-based generator expressions shows up as the command to execute or gets mentioned in a command argument, a target-level dependency gets added automatically, so the mentioned target builds before any target using this custom command (see policy CMP0112). The VIP list ๐Ÿ’…:

  • TARGET_FILE
  • TARGET_LINKER_FILE
  • TARGET_SONAME_FILE
  • TARGET_PDB_FILE
๐Ÿซถ Important lil caveat: this target-level dependency does NOT add a file-level dependency that would make the custom command re-run every time the executable gets recompiled. If you want that, list the target names with the DEPENDS option. Different tool, different job, bestie.

COMMENT ๐Ÿ’ฌ

Displays the given message before the commands get executed at build time. It'll be ignored if APPEND is given โ€” though a future version might actually use it, so, watch this space ๐Ÿ‘€.

Added in version 3.26: Arguments to COMMENT may use generator expressions. Slay.

DEPENDS ๐Ÿ”—

Specifies the files your command depends on. Each argument gets converted into a dependency like this:

  1. If the argument is the name of a target (created by the add_custom_target(), add_executable(), or add_library() command), a target-level dependency is created to make sure that target is built before any target using this custom command. Plus โ€” if the target is an executable or a library โ€” a file-level dependency is created so the custom command re-runs whenever the target recompiles. ๐Ÿ”ฅ

Changed in version 4.4: Previously an executable target could be specified using the name of the target with .exe appended. This is no longer allowed. See policy CMP0212.

  1. If the argument is an absolute path, a file-level dependency is created on that path.
  2. If the argument is the name of a source file that's been added to a target, or on which a source file property has been set, a file-level dependency is created on that source file.
  3. If the argument is a relative path and it exists in the current source directory, a file-level dependency is created on that file in the current source directory.
  4. Otherwise, a file-level dependency is created on that path relative to the current binary directory. ๐ŸŽ€

If any dependency is an OUTPUT of another custom command in the same directory (CMakeLists.txt file), CMake automatically brings that other custom command into the target in which this command is built. It's giving teamwork, honestly.

Added in version 3.16: A target-level dependency is added if any dependency is listed as BYPRODUCTS of a target, or of any of its build events, in the same directory โ€” just to make sure the byproducts will be available. ๐Ÿฆ‹

If DEPENDS is not specified, the command runs whenever the OUTPUT is missing. And if the command doesn't actually create the OUTPUTโ€ฆ ๐Ÿซ  the rule will always run. Forever. Every single time. Not a vibe, but that's the deal.

Added in version 3.1: Arguments to DEPENDS may use generator expressions.

COMMAND_EXPAND_LISTS ๐ŸŽ

Added in version 3.8.

Lists inside COMMAND arguments get expanded โ€” including the ones created with generator expressions โ€” which lets COMMAND arguments like the following actually expand properly:

${CC} "-I$<JOIN:$<TARGET_PROPERTY:foo,INCLUDE_DIRECTORIES>,;-I>" foo.cc
โš ๏ธ This keyword cannot be used with APPEND (see policy CMP0175). If the appended commands need this option set, it has to be set on the first call to add_custom_command(OUTPUT...) for the output files. No exceptions, bestie.

CODEGEN โšก

Added in version 3.31.

Adds the custom command to a global codegen target that you can use to execute the custom command while dodging the majority of the build graph. Speedrun strats, basically. ๐Ÿ”ฅ

This option is supported only by the Ninja Generators and the Makefile Generators, and is ignored by other generators. Also, it's allowed only if policy CMP0171 is set to NEW.

โš ๏ธ This keyword cannot be used with APPEND (see policy CMP0175). It can only be set on the first call to add_custom_command(OUTPUT...) for the output files.

IMPLICIT_DEPENDS ๐Ÿ”

Requests scanning of implicit dependencies of an input file. The language you give specifies the programming language whose corresponding dependency scanner should be used. Currently only the C and CXX language scanners are supported. And yes, the language has to be specified for every file in the IMPLICIT_DEPENDS list โ€” no skipping, bestie. Dependencies discovered from the scanning get added to those of the custom command at build time.

Note that the IMPLICIT_DEPENDS option is currently supported only for Makefile generators and will be ignored by other generators. ๐Ÿ˜ญ

Note: This option cannot be specified at the same time as the DEPFILE option. Pick a lane, queen.

JOB_POOL ๐ŸŠ

Added in version 3.15.

Specifies a pool for the Ninja generator. Incompatible with USES_TERMINAL, which implies the console pool โ€” so yeah, those two are not friends. ๐Ÿ’€ And using a pool that isn't defined by JOB_POOLS causes an error by ninja at build time. You've been warned.

โš ๏ธ This keyword cannot be used with APPEND (see policy CMP0175). Job pools can only be specified in the first call to add_custom_command(OUTPUT...) for the output files.

JOB_SERVER_AWARE ๐ŸŽ›๏ธ

Added in version 3.28.

Specifies that the command is GNU Make job server aware. Fancy.

For the Unix Makefiles, MSYS Makefiles, and MinGW Makefiles generators, this adds the + prefix to the recipe line. See the GNU Make Documentation (https://www.gnu.org/software/make/manual/html_node/MAKE-Variable.html) for more info, iykyk.

This option is silently ignored by other generators. ๐Ÿคซ

โš ๏ธ This keyword cannot be used with APPEND (see policy CMP0175). Job server awareness can only be specified in the first call to add_custom_command(OUTPUT...) for the output files.

MAIN_DEPENDENCY ๐Ÿ‘‘

Specifies the primary input source file to the command. It's treated just like any value given to the DEPENDS option, but it also suggests to the Visual Studio Generators where to hang the custom command. Helpful lil hint ๐ŸŽ€.

Each source file may have at most one command specifying it as its main dependency. A compile command (i.e. for a library or an executable) counts as an implicit main dependency, and that one gets silently overwritten by a custom command specification. Not me being territorial about my main dependency ๐Ÿ˜ญ.

This option is currently ignored if APPEND is given, but a future version may use it. So likeโ€ฆ stay tuned, bestie.

OUTPUT

Okay so this is where you tell CMake which files your command is expected to produce ๐Ÿ’…. Every output file automatically gets the GENERATED source file property slapped on it. And listen โ€” if your command doesn't actually write a real file to disk? Mark it with the SYMBOLIC source file property, bestie, or things get weird.

If an output file name is a relative path, its absolute path gets figured out by interpreting it relative to:

  1. the build directory corresponding to the current source directory (CMAKE_CURRENT_BINARY_DIR), or
  2. the current source directory (CMAKE_CURRENT_SOURCE_DIR).

The path in the build directory is preferred unless the path in the source tree is mentioned as an absolute source file path elsewhere in the current directory.

The output file path may not contain < or > characters. (Rude, but okay.)

Added in version 3.20: Arguments to OUTPUT may use a restricted set of generator expressions. Target-dependent expressions are not permitted. ๐Ÿšซ
Changed in version 3.28: In targets using File Sets, custom command outputs are now considered private unless they are listed in a non-private file set. See policy CMP0154.
Changed in version 3.30: The output file path may now use # characters, except when using the Borland Makefiles generator.

USES_TERMINAL

Added in version 3.2. โญ

The command gets direct access to the terminal if possible. With the Ninja generator, this drops the command into the console pool (JOB_POOLS). Very main character of it honestly.

This keyword cannot be used with APPEND (see policy CMP0175). If the appended commands need access to the terminal, it must be set on the first call to add_custom_command(OUTPUT...) for the output files. First-call-only, no takebacks ๐Ÿซถ.

VERBATIM

All arguments to the commands get escaped properly for the build tool so that the invoked command receives each argument unchanged. Note that one level of escapes is still used by the CMake language processor before add_custom_command even sees the arguments โ€” layers, babe, it's giving onions ๐Ÿง….

Use of VERBATIM is recommended as it enables correct behavior. When VERBATIM is not given, the behavior is platform specific because there is no protection of tool-specific special characters. Lowkey a vibes-based gamble without it ๐Ÿ˜ญ.

This keyword cannot be used with APPEND (see policy CMP0175). If the appended commands need to be treated as VERBATIM, it must be set on the first call to add_custom_command(OUTPUT...) for the output files.

WORKING_DIRECTORY

Execute the command with the given current working directory. If it's a relative path, it gets interpreted relative to the build tree directory corresponding to the current source directory. If not specified, it's set to CMAKE_CURRENT_BINARY_DIR.

This option is currently ignored if APPEND is given, but a future version may use it. So likeโ€ฆ don't rely on it being ignored forever, bestie ๐Ÿ™ˆ.

Added in version 3.13: Arguments to WORKING_DIRECTORY may use generator expressions.

DEPFILE

Added in version 3.7.

Specify a depfile which holds dependencies for the custom command. It's usually emitted by the custom command itself. This keyword may only be used if the generator supports it, as detailed below. ๐Ÿ‘‘

The expected format, compatible with what is generated by gcc with the option -M, is independent of the generator or platform.

The formal syntax, as specified using BNF notation with the regular extensions, is the following:

depfile       ::= rule*
rule          ::= targets (':' (separator dependencies?)?)? eol
targets       ::= target (separator target)* separator*
target        ::= pathname
dependencies  ::= dependency (separator dependency)* separator*
dependency    ::= pathname
separator     ::= (space | line_continue)+
line_continue ::= '\' eol
space         ::= ' ' | '\t'
pathname      ::= character+
character     ::= std_character | dollar | hash | whitespace
std_character ::= <any character except '$', '#' or ' '>
dollar        ::= '$$'
hash          ::= '\#'
whitespace    ::= '\ '
eol           ::= '\r'? '\n'
Note: As part of pathname, any slash and backslash is interpreted as a directory separator.
Added in version 3.7: The Ninja generator supports DEPFILE since the keyword was first added.
Added in version 3.17: Added the Ninja Multi-Config generator, which included support for the DEPFILE keyword.
Added in version 3.20: Added support for Makefile Generators.
Note: DEPFILE cannot be specified at the same time as the IMPLICIT_DEPENDS option for Makefile Generators. Pick a lane, diva ๐Ÿ’€.
Added in version 3.21: Added support for Visual Studio Generators with VS 2012 and above, and for the Xcode generator. Support for generator expressions was also added.
Added in version 3.29: The Ninja Generators will now incorporate the dependencies into its "deps log" database if the file is not listed in OUTPUTS or BYPRODUCTS.

Using DEPFILE with generators other than those listed above is an error. Not a vibe check failure โ€” an actual error ๐Ÿšจ.

If the DEPFILE argument is relative, it should be relative to CMAKE_CURRENT_BINARY_DIR, and any relative paths inside the DEPFILE should also be relative to CMAKE_CURRENT_BINARY_DIR. See policy CMP0116, which is always NEW for Makefile Generators, Visual Studio Generators, and the Xcode generator.

This keyword cannot be used with APPEND (see policy CMP0175). Depfiles can only be set on the first call to add_custom_command(OUTPUT...) for the output files.

DEPENDS_EXPLICIT_ONLY

Added in version 3.27.

Indicates that the command's DEPENDS argument represents all files required by the command and implicit dependencies are not required. Like, she's self-sufficient, she doesn't need your charity ๐Ÿ’….

Without this option, if any target uses the output of the custom command, CMake will consider that target's dependencies as implicit dependencies for the custom command in case this custom command requires files implicitly created by those targets.

This option can be enabled on all custom commands by setting CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY to ON.

This keyword cannot be used with APPEND (see policy CMP0175). It can only be set on the first call to add_custom_command(OUTPUT...) for the output files.

Only the Ninja Generators actually use this information to remove unnecessary implicit dependencies. Everyone else is just nodding along politely ๐ŸŽ€.

See also the OPTIMIZE_DEPENDENCIES target property, which may provide another way for reducing the impact of target dependencies in some scenarios.


๐ŸŽ€ Examples: Generating Files ๐ŸŽ€

Custom commands may be used to generate source files. For example, the code:

add_custom_command(
  OUTPUT out.c
  COMMAND someTool -i ${CMAKE_CURRENT_SOURCE_DIR}/in.txt
                   -o out.c
  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/in.txt
  VERBATIM)
add_library(myLib out.c)

adds a custom command to run someTool to generate out.c and then compile the generated source as part of a library. The generation rule will re-run whenever in.txt changes. Obsessed with a self-updating pipeline, honestly โœจ.

Added in version 3.20: One may use generator expressions to specify per-configuration outputs. For example, the code:
add_custom_command(
  OUTPUT "out-$<CONFIG>.c"
  COMMAND someTool -i ${CMAKE_CURRENT_SOURCE_DIR}/in.txt
                   -o "out-$<CONFIG>.c"
                   -c "$<CONFIG>"
  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/in.txt
  VERBATIM)
add_library(myLib "out-$<CONFIG>.c")

so this one ๐Ÿ’… adds a custom command that runs someTool to generate out-<config>.c โ€” where <config> is the build configuration โ€” and then compiles the generated source as part of a library. it's giving "generate per-config, compile per-config," we love a two-for-one.

Added in version 3.31: use the CODEGEN option to add a custom command's outputs to the builtin codegen target. this is lowkey iconic when you want generated code available for static analysis without building the entire project. like so:

add_executable(someTool someTool.c)

add_custom_command(
  OUTPUT out.c
  COMMAND someTool -o out.c
  CODEGEN)

add_library(myLib out.c)

a user may build the codegen target to generate out.c. someTool is built as a dependency, but myLib is not built at all. periodt. ๐Ÿ’…

๐ŸŒธ Example: Generating Files for Multiple Targets ๐ŸŒธ

okay so if multiple independent targets need the same custom command output, it must be attached to a single custom target that they all depend on. no cap. consider this:

add_custom_command(
  OUTPUT table.csv
  COMMAND makeTable -i ${CMAKE_CURRENT_SOURCE_DIR}/input.dat
                    -o table.csv
  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/input.dat
  VERBATIM)
add_custom_target(generate_table_csv DEPENDS table.csv)

add_custom_command(
  OUTPUT foo.cxx
  COMMAND genFromTable -i table.csv -case foo -o foo.cxx
  DEPENDS table.csv           # file-level dependency
          generate_table_csv  # target-level dependency
  VERBATIM)
add_library(foo foo.cxx)

add_custom_command(
  OUTPUT bar.cxx
  COMMAND genFromTable -i table.csv -case bar -o bar.cxx
  DEPENDS table.csv           # file-level dependency
          generate_table_csv  # target-level dependency
  VERBATIM)
add_library(bar bar.cxx)

so foo.cxx is needed only by target foo and bar.cxx is needed only by target bar, but both targets need table.csv, transitively. ๐Ÿซถ and since foo and bar are independent targets that may build concurrently, we prevent them from racing to generate table.csv by placing its custom command in a separate target, generate_table_csv. the custom commands generating foo.cxx and bar.cxx each specify a target-level dependency on generate_table_csv, so the targets using them โ€” foo and bar โ€” will not build until after target generate_table_csv is built. no race condition, no tears, we love to see it. โœจ


๐ŸŽ€ Build Events ๐ŸŽ€

add_custom_command(TARGET <target> PRE_BUILD | PRE_LINK | POST_BUILD
                   [...])

add a build event to a target such as a library or executable. this is useful for performing an operation before or after building the target. the command becomes part of the target and will only execute when the target itself is built. if the target is already built, the command will not execute. ๐Ÿ™ˆ

add_custom_command(TARGET <target>
                   PRE_BUILD | PRE_LINK | POST_BUILD
                   COMMAND <command1> [ARGS] [<args1>...]
                   [COMMAND <command2> [ARGS] [<args2>...]] ...
                   [BYPRODUCTS <files>...]
                   [WORKING_DIRECTORY <dir>]
                   [COMMENT <comment>]
                   [VERBATIM]
                   [COMMAND_EXPAND_LISTS]
                   [USES_TERMINAL])

this defines a new command that will be associated with building the specified <target>. and heads up bestie: the <target> must be defined in the current directory โ€” targets defined in other directories may not be specified. ๐Ÿ’€

when the command will happen is determined by which of the following is specified:

PRE_BUILD

this option has unique behavior for the Visual Studio Generators. when using one of the Visual Studio generators, the command will run before any other rules are executed within the target. with all other generators, this option behaves the same as PRE_LINK instead. because of this, it is recommended to avoid using PRE_BUILD except when it is known that a Visual Studio generator is being used. iykyk. ๐Ÿ™ˆ

run after sources have been compiled but before linking the binary or running the librarian or archiver tool of a static library. this is not defined for targets created by the add_custom_target() command.

POST_BUILD

run after all other rules within the target have been executed. the grand finale vibe. ๐Ÿ‘‘

projects should always specify one of the above three keywords when using the TARGET form. see policy CMP0175.

all other keywords shown in the signature above have the same meaning as they do for the add_custom_command(OUTPUT) form of the command. at least one COMMAND must be given, see policy CMP0175. โœ…

note: because generator expressions can be used in custom commands, it is possible to define COMMAND lines or whole custom commands which evaluate to empty strings for certain configurations. for Visual Studio Generators these command lines or custom commands will be omitted for the specific configuration and no "empty-string-command" will be added. ๐Ÿ’… this allows adding individual build events for every configuration. slay.

Added in version 3.21: support for target-dependent generator expressions.

Added in version 3.29: the <target> may be an ALIAS target.

๐Ÿฆ‹ Examples: Build Events ๐Ÿฆ‹

a POST_BUILD event may be used to post-process a binary after linking. for example, this code:

add_executable(myExe myExe.c)
add_custom_command(
  TARGET myExe POST_BUILD
  COMMAND someHasher -i "$<TARGET_FILE:myExe>"
                     -o "$<TARGET_FILE:myExe>.hash"
  VERBATIM)

will run someHasher to produce a .hash file next to the executable after linking. cute, useful, iconic. โœจ

Added in version 3.20: one may use generator expressions to specify per-configuration byproducts. for example, this code:

add_library(myPlugin MODULE myPlugin.c)
add_custom_command(
  TARGET myPlugin POST_BUILD
  COMMAND someHasher -i "$<TARGET_FILE:myPlugin>"
                     --as-code "myPlugin-hash-$<CONFIG>.c"
  BYPRODUCTS "myPlugin-hash-$<CONFIG>.c"
  VERBATIM)
add_executable(myExe myExe.c "myPlugin-hash-$<CONFIG>.c")

will run someHasher after linking myPlugin โ€” e.g. to produce a .c file containing code to check the hash of myPlugin that the myExe executable can use to verify it before loading. honestly? that's main character behavior. ๐ŸŽ€

๐Ÿ’œ Ninja Multi-Config ๐Ÿ’œ

Added in version 3.20: add_custom_command supports the Ninja Multi-Config generator's cross-config capabilities. see the generator documentation for more information. go read it bestie, it's worth it. ๐Ÿซถ

๐ŸŒˆ See Also ๐ŸŒˆ

  • add_custom_target()

๐Ÿ‘‘ add_custom_target ๐Ÿ‘‘

add a target with no output so it will always be built. yes, always. it never gets to rest. ๐Ÿ˜ญ

add_custom_target(Name [ALL] [command1 [<args1>...]]
                  [COMMAND command2 [<args2>...]] ...
                  [DEPENDS <depend>...]
                  [BYPRODUCTS <file>...]
                  [WORKING_DIRECTORY <dir>]
                  [COMMENT <comment>]
                  [JOB_POOL <job_pool>]
                  [JOB_SERVER_AWARE <bool>]
                  [VERBATIM] [USES_TERMINAL]
                  [COMMAND_EXPAND_LISTS]
                  [SOURCES <source>...])

adds a target with the given name that executes the given commands. the target has no output file and is always considered out of date even if the commands try to create a file with the name of the target. ๐Ÿ’€ use the add_custom_command() command to generate a file with dependencies. by default nothing depends on the custom target โ€” it's giving independent queen energy โ€” so use the add_dependencies() command to add dependencies to or from other targets.

the options are:

  • ALL โ€” indicate that this target should be added to the default build target so that it will be run every time (the command cannot be called ALL). ๐Ÿ”ฅ
  • BYPRODUCTS โ€” Added in version 3.2. specify the files the command is expected to produce but whose modification time may or may not be updated on subsequent builds. if a byproduct name is a relative path it will be interpreted relative to the build tree directory corresponding to the current source directory. each byproduct file will be marked with the GENERATED source file property automatically. โœจ

see policy CMP0058 for the motivation behind this feature. fr fr, go read the policy.

explicit specification of byproducts is supported by the Ninja generator to tell the ninja build tool how to regenerate byproducts when they are missing. it's also useful when other build rules (e.g. custom commands) depend on the byproducts. ninja requires a build rule for any generated file on which another rule depends โ€” even if there are order-only dependencies โ€” to ensure the byproducts will be available before their dependents build. no shortcuts, no vibes-based builds. ๐Ÿ’…

the Makefile Generators will remove BYPRODUCTS and other GENERATED files during make clean. bye bestie ๐Ÿซถ

Added in version 3.20: arguments to BYPRODUCTS may use a restricted set of generator expressions. target-dependent expressions are not permitted. ๐Ÿ™…

Changed in version 3.28: in custom targets using File Sets, byproducts are now considered private unless they are listed in a non-private file set. see policy CMP0154.

  • COMMAND โ€” tells CMake the command-line(s) to run at build time ๐Ÿ’…. If you give more than one COMMAND, they run in order, but they are not necessarily stitched together into one stateful shell or batch script (so like, no, variable you set in one doesn't carry over to the next, bestie). If you want a full script, use the configure_file() command or the file(GENERATE) command to create one, then specify a COMMAND to launch it ๐Ÿซถ.

If COMMAND names an executable target (i.e. one created by the add_executable() command), CMake automatically swaps in the location of the executable built at build time if either of these is true:

  • The target is not being cross-compiled (i.e. the CMAKE_CROSSCOMPILING variable is not set to true).
  • Added in version 3.6: The target is being cross-compiled and an emulator is provided (i.e. its CROSSCOMPILING_EMULATOR target property is set). In this case, the contents of CROSSCOMPILING_EMULATOR get prepended to the command, before the location of the target executable.

If neither of those is the case, CMake assumes the command name is a program to be found on the PATH at build time ๐Ÿ”.

Arguments to COMMAND can use generator expressions. Use the TARGET_FILE generator expression when you want to refer to a target's location later in the command line (i.e. as a command argument rather than as the command to execute) ๐Ÿ‘‘.

Whenever one of these target-based generator expressions is used as a command to execute, or shows up in a command argument, a target-level dependency gets added automatically so the mentioned target builds before this custom target (see policy CMP0112):

  • TARGET_FILE
  • TARGET_LINKER_FILE
  • TARGET_SONAME_FILE
  • TARGET_PDB_FILE

The command and arguments are optional โ€” if you don't specify them, an empty target gets created ๐ŸŽ€.

  • COMMENT โ€” shows the given message before the commands run at build time ๐Ÿ’ฌ.

Added in version 3.26: Arguments to COMMENT can use generator expressions.

  • DEPENDS โ€” references files and outputs of custom commands created with add_custom_command() command calls in the same directory (the CMakeLists.txt file). They get brought up to date when the target is built โœจ.

Changed in version 3.16: A target-level dependency gets added if any dependency is a byproduct of a target, or of any of its build events, in the same directory โ€” this makes sure the byproducts are available before this target builds.

Use the add_dependencies() command to add dependencies on other targets ๐Ÿซก.

  • COMMAND_EXPAND_LISTS โ€” Added in version 3.8. Lists in COMMAND arguments get expanded, including the ones created with generator expressions, which lets COMMAND arguments like
${CC} "-I$<JOIN:$<TARGET_PROPERTY:foo,INCLUDE_DIRECTORIES>,;-I>" foo.cc

expand properly ๐Ÿฆ‹.

  • JOB_POOL โ€” Added in version 3.15. Specifies a pool for the Ninja generator. Incompatible with USES_TERMINAL, which implies the console pool ๐Ÿšซ. Using a pool that isn't defined by JOB_POOLS causes an error by ninja at build time.
  • JOB_SERVER_AWARE โ€” Added in version 3.28. Says the command is GNU Make job server aware ๐Ÿ‘ท.

For the Unix Makefiles, MSYS Makefiles, and MinGW Makefiles generators this adds the + prefix to the recipe line. See the GNU Make Documentation for more info.

This option is silently ignored by other generators ๐Ÿคซ.

  • SOURCES โ€” specifies additional source files to include in the custom target. They get added to IDE project files so editing is comfy, even if they have no build rules ๐Ÿ“.
  • VERBATIM โ€” all arguments to the commands get escaped properly for the build tool, so the invoked command receives each argument unchanged ๐Ÿ’ฏ. Note that one level of escapes is still used by the CMake language processor before add_custom_target even sees the arguments. Using VERBATIM is recommended, because it enables correct behavior. When VERBATIM isn't given, the behavior is platform specific, since there's no protection of tool-specific special characters ๐Ÿ˜ฌ.
  • USES_TERMINAL โ€” Added in version 3.2. The command gets direct access to the terminal if possible. With the Ninja generator, this places the command in the console pool ๐Ÿ–ฅ๏ธ.
  • WORKING_DIRECTORY โ€” executes the command with the given current working directory. If it's a relative path, it's interpreted relative to the build tree directory corresponding to the current source directory. If it's not specified, it's set to CMAKE_CURRENT_BINARY_DIR ๐Ÿ“‚.

Added in version 3.13: Arguments to WORKING_DIRECTORY can use generator expressions.

๐ŸŒˆ Ninja Multi-Config ๐ŸŒˆ

Added in version 3.20: add_custom_target supports the Ninja Multi-Config generator's cross-config capabilities. Peep the generator documentation for the deets ๐Ÿ‘€.

๐Ÿ’– See Also ๐Ÿ’–

  • add_custom_command()

๐ŸŽฏ add_definitions ๐ŸŽฏ

Adds -D define flags to the compilation of source files, slay ๐Ÿ’….

add_definitions(-DFOO -DBAR ...)

This adds definitions to the compiler command line for targets in the current directory โ€” whether they were added before or after this command is invoked โ€” and for the ones in sub-directories added after. You can use this command to add any flags, but its whole personality is meant to be preprocessor definitions โœจ.

Note: this command has been superseded by alternatives, bestie: - Use add_compile_definitions() to add preprocessor definitions. - Use include_directories() to add include directories. - Use add_compile_options() to add other options.

Flags beginning in -D or /D that look like preprocessor definitions get automatically added to the COMPILE_DEFINITIONS directory property for the current directory ๐ŸŽ€. Definitions with non-trivial values may be left in the set of flags instead of being converted, for reasons of backwards compatibility. See the documentation of the directory, target, and source file COMPILE_DEFINITIONS properties for details on adding preprocessor definitions to specific scopes and configurations ๐Ÿซถ.

๐Ÿ‘€ See Also ๐Ÿ‘€


๐Ÿงš add_dependencies ๐Ÿงš

Adds a dependency between top-level targets. It's giving glue but make it build-graph ๐Ÿ”—.

add_dependencies(<target> <target-dependency>...)

Makes a top-level <target> depend on other top-level targets, so they build before <target> does โณ. A top-level target is one created by one of the add_executable(), add_library(), or add_custom_target() commands โ€” but not targets generated by CMake itself, like install ๐Ÿ™….

Dependencies added to an imported target or an interface library are followed transitively in its place, since the target itself doesn't build ๐Ÿซก.

Added in version 3.3: Allows adding dependencies to interface libraries.

Added in version 3.8: Dependencies populate the MANUALLY_ADDED_DEPENDENCIES property of <target>.

Changed in version 3.9: The Ninja Generators use weaker ordering than other generators in order to improve available concurrency ๐Ÿ”ฅ. They only guarantee that the dependencies' custom commands are finished before sources in <target> start compiling โ€” this makes sure generated sources are available.

Changed in version 4.1: The command may be called with no dependencies. Previously, at least one dependency was required ๐Ÿ˜Œ.

๐Ÿ’– See Also ๐Ÿ’–

  • The DEPENDS option of the add_custom_target() and add_custom_command() commands, for adding file-level dependencies in custom rules.
  • The OBJECT_DEPENDS source file property, for adding file-level dependencies to object files.

๐Ÿ‘‘ add_executable ๐Ÿ‘‘

Adds an executable to the project using the specified source files. Main character energy fr fr ๐Ÿ’….

๐ŸŽ€ Normal Executables ๐ŸŽ€

add_executable(<name> <options>... <sources>...)

Adds an executable target called <name> to be built from the source files listed in the command invocation ๐ŸŒŸ.

The options are:

  • WIN32 โ€” sets the WIN32_EXECUTABLE target property automatically. See that target property's documentation for the deets.
  • MACOSX_BUNDLE โ€” sets the MACOSX_BUNDLE target property automatically. See that target property's documentation for the deets.
  • EXCLUDE_FROM_ALL โ€” sets the EXCLUDE_FROM_ALL target property automatically. See that target property's documentation for the deets.

The <name> corresponds to the logical target name and must be globally unique within a project โš ๏ธ. The actual file name of the executable that gets built is constructed based on the conventions of the native platform (like <name>.exe, or just <name>) ๐Ÿฆ‹.

okay so back to add_executable for a sec because there's more tea โ˜•

Added in version 3.1: the source args you pass to add_executable can absolutely use "generator expressions" with the syntax $<...>. go check the cmake-generator-expressions(7) manual for the full menu of expressions you can use ๐Ÿฆ‹

Added in version 3.11: you can literally skip the source files entirely if you're gonna add them later with target_sources(). no cap, it's allowed โœจ

So by default the executable file gets created in the build tree directory that corresponds to the source tree directory where you invoked the command. if you want to move it somewhere else, peep the docs for the RUNTIME_OUTPUT_DIRECTORY target property. and if you wanna change the <name> part of the final file name, look at the OUTPUT_NAME target property ๐ŸŽ€

See the cmake-buildsystem(7) manual for more on defining buildsystem properties โ€” it's giving essential reading ngl.

Also see HEADER_FILE_ONLY for what to do if some of your sources are pre-processed and you still want the original sources reachable from inside your IDE ๐Ÿซถ

Imported Executables

add_executable(<name> IMPORTED [GLOBAL])

Adds an IMPORTED executable target so you can reference an executable file that lives outside the project. the target name can be referenced just like any target built inside the project โ€” except by default it's only visible in the directory where it was created, and below. scoped vibes ๐Ÿ’…

The options are:

  • GLOBAL โ€” makes the target name globally visible. that's it, that's the whole option ๐Ÿ‘‘

No rules get generated to build imported targets, and the IMPORTED target property is True. imported executables are super handy for conveniently referencing stuff from commands like add_custom_command() ๐Ÿ”ฅ

You spell out the details of the imported executable by setting properties whose names begin in IMPORTED_. The most important one is IMPORTED_LOCATION (and its per-configuration version IMPORTED_LOCATION_<CONFIG>) which specifies the location of the main executable file on disk. see the docs for the IMPORTED_* properties for more, bestie ๐Ÿ“š

Alias Executables

add_executable(<name> ALIAS <target>)

Creates an Alias Target, so that <name> can be used to refer to <target> in commands that come after. iconic behaviour honestly. the <name> does not show up in the generated buildsystem as a make target. and heads up โ€” the <target> may not itself be an ALIAS ๐Ÿ™ˆ

Added in version 3.15... wait no, Added in version 3.11: an ALIAS can target a GLOBAL Imported Target โœจ

Added in version 3.18: an ALIAS can target a non-GLOBAL Imported Target. that kind of alias is scoped to the directory where it's created plus subdirectories. you can check whether an alias is global or not with the ALIAS_GLOBAL target property ๐Ÿ‘‘

ALIAS targets can be used as targets to read properties from, and as executables for custom commands and custom targets. they can also be tested for existence with the regular if(TARGET) subcommand ๐ŸŽฏ

but bestie, listen โ€” the <name> may not be used to modify properties of <target>. like, it may not be used as the operand of set_property(), set_target_properties(), target_link_libraries(), etc. and an ALIAS target may not be installed or exported. it's a read-only situationship, periodt ๐Ÿ’€

See Also

  • add_library() ๐Ÿซถ

๐ŸŒธ add_library ๐ŸŒธ

Adds a library to the project using the source files you specify. let's go ๐Ÿ’–

Normal Libraries

add_library(<name> [<type>] [EXCLUDE_FROM_ALL] <sources>...)

Adds a library target called <name> to be built from the source files you list in the command invocation. easy โœจ

The optional <type> tells CMake what kind of library to create:

  • STATIC โ€” a Static Library: an archive of object files for use when linking other targets.
  • SHARED โ€” a Shared Library: a dynamic library that may be linked by other targets and loaded at runtime.
  • MODULE โ€” a Module Library: a plugin that may not be linked by other targets, but may be dynamically loaded at runtime using dlopen-like functionality.

If you don't give a <type>, the default is STATIC or SHARED based on the value of the BUILD_SHARED_LIBS variable ๐ŸŒˆ

The options are:

  • EXCLUDE_FROM_ALL โ€” sets the EXCLUDE_FROM_ALL target property automatically. see the docs for that target property for the details ๐Ÿ‘€

The <name> corresponds to the logical target name and must be globally unique within a project. the actual file name of the library that gets built is constructed based on conventions of the native platform (think lib<name>.a or <name>.lib) ๐ŸŽ€

Added in version 3.1: source args to add_library may use "generator expressions" with the syntax $<...>. see the cmake-generator-expressions(7) manual for the available expressions ๐Ÿฆ‹

Added in version 3.11: the source files can be omitted if you're adding them later with target_sources() ๐Ÿ’…

For SHARED and MODULE libraries, the POSITION_INDEPENDENT_CODE target property is set to ON automatically. and a SHARED library may be marked with the FRAMEWORK target property to create an macOS Framework ๐Ÿ‘‘

Added in version 3.8: a STATIC library may be marked with the FRAMEWORK target property to create a static Framework ๐ŸŒธ

Important warning: if a library does not export any symbols, it must not be declared as a SHARED library. like, for example, a Windows resource DLL or a managed C++/CLI DLL that exports no unmanaged symbols would need to be a MODULE library instead. why? because CMake expects a SHARED library to always have an associated import library on Windows. it's a whole thing ๐Ÿ˜ญ

By default the library file gets created in the build tree directory corresponding to the source tree directory where the command was invoked. see the docs for the ARCHIVE_OUTPUT_DIRECTORY, LIBRARY_OUTPUT_DIRECTORY, and RUNTIME_OUTPUT_DIRECTORY target properties to change that location. and see OUTPUT_NAME to change the <name> part of the final file name ๐Ÿ“

See the cmake-buildsystem(7) manual for more on defining buildsystem properties.

Also see HEADER_FILE_ONLY on what to do if some sources are pre-processed and you want the original sources reachable from within your IDE ๐Ÿซถ

Changed in version 3.30: on platforms that do not support shared libraries, add_library now fails on calls creating SHARED libraries instead of automatically converting them to STATIC libraries like it used to. so if that's you, mind the vibe check. see policy CMP0164 ๐Ÿ’€

Object Libraries

add_library(<name> OBJECT <sources>...)

Adds an Object Library to compile source files without archiving or linking their object files into a library. it's giving "just compile, no commitment" โœจ

Other targets created by add_library or add_executable() may reference those objects using an expression of the form $<TARGET_OBJECTS:objlib> as a source, where objlib is the object library name. for example:

add_library(... $<TARGET_OBJECTS:objlib> ...)
add_executable(... $<TARGET_OBJECTS:objlib> ...)

will include objlib's object files in a library and an executable, along with the ones compiled from their own sources. obsessed ๐Ÿซถ

Object libraries may contain only sources that compile, header files, and other files that wouldn't affect linking of a normal library (like .txt). they may contain custom commands generating such sources, but not PRE_BUILD, PRE_LINK, or POST_BUILD commands ๐Ÿšซ

Also, some native build systems (such as Xcode) may not like targets that have only object files, so consider adding at least one real source file to any target that references $<TARGET_OBJECTS:objlib>. little pro move for you ๐Ÿ’…

Added in version 3.12: object libraries can be linked to with target_link_libraries() ๐Ÿ”ฅ

Interface Libraries

add_library(<name> INTERFACE)

Adds an Interface Library target that may specify usage requirements for dependents but does not compile sources and does not produce a library artifact on disk. it's the main character of vibes-only targets ๐Ÿ‘‘

An interface library with no source files isn't included as a target in the generated buildsystem. however, it may have properties set on it, and it may be installed and exported. typically you populate INTERFACE_* properties on an interface target using these commands:

  • set_property(),
  • target_link_libraries(INTERFACE),
  • target_link_options(INTERFACE),
  • target_include_directories(INTERFACE),
  • target_compile_options(INTERFACE),
  • target_compile_definitions(INTERFACE), and
  • target_sources(INTERFACE),

and then you use it as an argument to target_link_libraries() just like any other target ๐ŸŒˆ

Added in version 3.15: an interface library can have PUBLIC_HEADER and PRIVATE_HEADER properties. the headers specified by those properties can be installed using the install(TARGETS) command ๐ŸŽ€

add_library(<name> INTERFACE [EXCLUDE_FROM_ALL] <sources>...)

Added in version 3.19.

Adds an Interface Library target with source files (in addition to usage requirements and properties as documented by the signature above). source files may be listed directly in the add_library call, or added later by calls to target_sources() with the PRIVATE or PUBLIC keywords ๐Ÿ’–

Okay so if an interface library has actual source files (like when the SOURCES target property is set) or header sets (when HEADER_SETS is set), it's gonna show up in the generated buildsystem as a real build target โ€” basically the same energy as a target defined by add_custom_target(). It doesn't compile any sources, but it does carry build rules for custom commands made by add_custom_command(). ๐Ÿ’…

The options are:

  • EXCLUDE_FROM_ALL โ€” auto-sets the EXCLUDE_FROM_ALL target property for you. Go read that target property's docs for the full tea. ๐Ÿซ–
Note: In most command signatures where the INTERFACE keyword shows up, the items listed after it only become part of that target's usage requirements โ€” they're not part of the target's own settings. But in this signature of add_library, INTERFACE refers to the library type only, periodt. Sources listed after it in the add_library call are PRIVATE to the interface library and do not appear in its INTERFACE_SOURCES target property. ๐Ÿ™ˆ

โœจ add_library(<name> INTERFACE SYMBOLIC) โœจ

Added in version 4.2.

Adds a symbolic Interface Library target. Symbolic interface libraries are lowkey iconic for representing optional components or features in a package. They have no usage requirements, do not compile sources, and do not produce a library artifact on disk โ€” but they may be exported and installed. They can also be tested for existence with the regular if(TARGET) subcommand. ๐Ÿ‘‘

A symbolic interface library may be used as a linkable target to enforce the presence of optional components in a dependency. For example, if a library libgui may or may not provide a feature widget, a consumer package can link against widget to express that it requires this component to be available. This lets find_package() calls that declare required components be validated by linking against the corresponding symbolic targets. Fr fr, it's giving dependency validation. ๐ŸŽ€

A symbolic interface library has the SYMBOLIC target property set to true.

๐ŸŒธ Imported Libraries ๐ŸŒธ

add_library(<name> <type> IMPORTED [GLOBAL])

Adds an IMPORTED library target called <name>. The target name may be referenced like any target built within the project, except that by default it is visible only in the directory in which it is created, and below. (Scoped visibility, we love to see it. โœจ)

The <type> must be one of:

  • STATIC, SHARED, MODULE, UNKNOWN โ€” references a library file located outside the project. The IMPORTED_LOCATION target property (or its per-configuration variant IMPORTED_LOCATION_<CONFIG>) specifies the location of the main library file on disk:
    • For a SHARED library on most non-Windows platforms, the main library file is the .so or .dylib file used by both linkers and dynamic loaders. If the referenced library file has a SONAME (or on macOS, has a LC_ID_DYLIB starting in @rpath/), the value of that field should be set in the IMPORTED_SONAME target property. If the referenced library file does not have a SONAME, but the platform supports it, then the IMPORTED_NO_SONAME target property should be set. ๐Ÿงš
    • For a SHARED library on Windows, the IMPORTED_IMPLIB target property (or its per-configuration variant IMPORTED_IMPLIB_<CONFIG>) specifies the location of the DLL import library file (.lib or .dll.a) on disk, and the IMPORTED_LOCATION is the location of the .dll runtime library (and is optional, but needed by the TARGET_RUNTIME_DLLS generator expression). ๐ŸชŸ

Additional usage requirements may be specified in INTERFACE_* properties.

An UNKNOWN library type is typically only used in the implementation of Find Modules. It lets the path to an imported library (often found using the find_library() command) be used without having to know what type of library it is. This is especially useful on Windows where a static library and a DLL's import library both have the same file extension. ๐Ÿ˜ญ

  • OBJECT โ€” references a set of object files located outside the project. The IMPORTED_OBJECTS target property (or its per-configuration variant IMPORTED_OBJECTS_<CONFIG>) specifies the locations of object files on disk. Additional usage requirements may be specified in INTERFACE_* properties.
  • INTERFACE โ€” does not reference any library or object files on disk, but may specify usage requirements in INTERFACE_* properties.

The options are:

  • GLOBAL โ€” makes the target name globally visible. ๐ŸŒˆ

No rules are generated to build imported targets, and the IMPORTED target property is True. Imported libraries are useful for convenient reference from commands like target_link_libraries(). ๐Ÿ’–

Details about the imported library are specified by setting properties whose names begin in IMPORTED_ and INTERFACE_. See the documentation of such properties for more info.

๐Ÿฆ‹ Alias Libraries ๐Ÿฆ‹

add_library(<name> ALIAS <target>)

Creates an Alias Target, such that <name> can be used to refer to <target> in subsequent commands. The <name> does not appear in the generated buildsystem as a make target. The <target> may not be an ALIAS. No cap.

Added in version 3.11: An ALIAS can target a GLOBAL Imported Target.
Added in version 3.18: An ALIAS can target a non-GLOBAL Imported Target. Such an alias is scoped to the directory in which it is created and below. The ALIAS_GLOBAL target property can be used to check if the alias is global or not.

ALIAS targets can be used as linkable targets and as targets to read properties from. They can also be tested for existence with the regular if(TARGET) subcommand. The <name> may not be used to modify properties of <target> โ€” that is, it may not be used as the operand of set_property(), set_target_properties(), target_link_libraries() etc. An ALIAS target may not be installed or exported. ๐Ÿšซ

๐Ÿ‘€ See Also ๐Ÿ‘€

  • add_executable()

Added in version 3.13.

Adds options to the link step for executable, shared library or module library targets in the current directory and below that are added after this command is invoked. Iconic behaviour, honestly.

add_link_options(<option> ...)

This command can be used to add any link options, but alternative commands exist to add libraries (target_link_libraries() or link_libraries()). See the documentation of the directory and target LINK_OPTIONS properties. ๐Ÿ“š

Note: This command cannot be used to add options for static library targets, since they do not use a linker. To add archiver or MSVC librarian flags, see the STATIC_LIBRARY_OPTIONS target property. ๐Ÿ’€

Arguments to add_link_options may use generator expressions with the syntax $<...>. See the cmake-generator-expressions(7) manual for available expressions. See the cmake-buildsystem(7) manual for more on defining buildsystem properties. โœจ

Added in version 3.18: When a device link step is involved โ€” which is controlled by CUDA_SEPARABLE_COMPILATION and CUDA_RESOLVE_DEVICE_SYMBOLS properties and policy CMP0105 โ€” the raw options will be delivered to the host and device link steps (wrapped in -Xcompiler or equivalent for device link). Options wrapped with the $<DEVICE_LINK:...> generator expression will be used only for the device link step. Options wrapped with the $<HOST_LINK:...> generator expression will be used only for the host link step. Two vibes, two lanes. ๐Ÿ›ฃ๏ธ

๐ŸŽ€ Option De-duplication ๐ŸŽ€

The final set of options used for a target is constructed by accumulating options from the current target and the usage requirements of its dependencies. The set of options is de-duplicated to avoid repetition. (No repeats, we're not doing that. ๐Ÿ’…)

Added in version 3.12: While beneficial for individual options, the de-duplication step can break up option groups. For example, -option A -option B becomes -option A B. One may specify a group of options using shell-like quoting along with a SHELL: prefix. The SHELL: prefix is dropped, and the rest of the option string is parsed using the separate_arguments() UNIX_COMMAND mode. For example, "SHELL:-option A" "SHELL:-option B" becomes -option A -option B. Slay, grouping preserved. ๐Ÿซถ

โญ Handling Compiler Driver Differences โญ

To pass options to the linker tool, each compiler driver has its own syntax. The LINKER: prefix and , separator can be used to specify, in a portable way, options to pass to the linker tool. LINKER: is replaced by the appropriate driver option and , by the appropriate driver separator. The driver prefix and driver separator are given by the values of the CMAKE_<LANG>_LINKER_WRAPPER_FLAG and CMAKE_<LANG>_LINKER_WRAPPER_FLAG_SEP variables. Portability ate and left no crumbs. ๐Ÿ“

For example, "LINKER:-z,defs" becomes -Xlinker -z -Xlinker defs for Clang and -Wl,-z,defs for GNU GCC. โœจ

The LINKER: prefix can be specified as part of a SHELL: prefix expression.

The LINKER: prefix supports, as an alternative syntax, specification of arguments using the SHELL: prefix and space as separator. The previous example then becomes "LINKER:SHELL:-z defs". ๐Ÿซถ

Note: specifying the SHELL: prefix anywhere other than at the beginning of the LINKER: prefix is not supported. like, don't do it bestie, it's not gonna work ๐Ÿ’€

๐Ÿ’– See Also

  • link_libraries()
  • target_link_libraries()
  • target_link_options()
  • CMAKE_<LANG>_FLAGS and CMAKE_<LANG>_FLAGS_<CONFIG> add language-wide flags passed to all invocations of the compiler. This includes invocations that drive compiling and those that drive linking.
  • Added in version 4.3: CMAKE_<LANG>_LINK_FLAGS and CMAKE_<LANG>_LINK_FLAGS_<CONFIG> add language-wide flags passed to all invocations of the compiler which drive linking.

๐Ÿฆ‹ add_subdirectory

Add a subdirectory to the build. yes literally that's the whole vibe ๐ŸŽ€

add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL] [SYSTEM])

Adds a subdirectory to the build. The source_dir specifies the directory in which the source CMakeLists.txt and code files are located. If it's a relative path, it will be evaluated with respect to the current directory (the typical usage), but it may also be an absolute path. The binary_dir specifies the directory in which to place the output files. If it's a relative path, it will be evaluated with respect to the current output directory, but it may also be an absolute path. If binary_dir is not specified, the value of source_dir, before expanding any relative path, will be used (the typical usage). The CMakeLists.txt file in the specified source directory will be processed immediately by CMake before processing in the current input file continues beyond this command. iconic behavior ngl ๐Ÿ‘‘

If the EXCLUDE_FROM_ALL argument is provided then the EXCLUDE_FROM_ALL property will be set on the added directory. This will exclude the directory from a default build. See the directory property EXCLUDE_FROM_ALL for full details.

Added in version 3.25: if the SYSTEM argument is provided, the SYSTEM directory property of the subdirectory will be set to true. This property is used to initialize the SYSTEM property of each non-imported target created in that subdirectory. slay ๐Ÿ’…

๐ŸŒธ add_test

Add a test to the project to be run by ctest(1). we love a test suite that runs itself ๐Ÿงš

add_test(NAME <name> COMMAND <command> [<arg>...]
         [CONFIGURATIONS <config>...]
         [WORKING_DIRECTORY <dir>]
         [COMMAND_EXPAND_LISTS]
         [BUILD_DEPENDS <dependencies>...])

Adds a test called <name>. The test name may contain arbitrary characters, expressed as a Quoted Argument or Bracket Argument if necessary. See policy CMP0110. (yes, arbitrary characters, go off, name your test whatever unhinged thing you want โœจ)

CMake only generates tests if the enable_testing() command has been invoked. The CTest module invokes enable_testing automatically unless BUILD_TESTING is set to OFF. so like, the module's got you covered unless you specifically said no ๐Ÿซถ

Tests added with the add_test(NAME) signature support using generator expressions in test properties set by set_property(TEST) or set_tests_properties(). Test properties may only be set in the directory the test is created in. periodt, no setting them from across the repo ๐Ÿ’€

add_test options are:

  • COMMAND โ€” specify the test command-line.

If <command> specifies an executable target created by add_executable():

  • It will automatically be replaced by the location of the executable created at build time.
  • Added in version 3.3: the target's CROSSCOMPILING_EMULATOR, if set, will be used to run the command on the host:
<emulator> <command>
  • Changed in version 3.29: the emulator is used only when cross-compiling. See policy CMP0158.
  • Added in version 3.29: the target's TEST_LAUNCHER, if set, will be used to launch the command:
<launcher> <command>

If the CROSSCOMPILING_EMULATOR is also set, both are used:

<launcher> <emulator> <command>
  • Added in version 4.4: when the CMAKE_TEST_BUILD_DEPENDS variable is enabled, the Ninja Generators generate a convenience build target named test_prep/<name> that depends on the test executable target. Building this target ensures the executable is up-to-date before the test runs. ๐Ÿƒโ€โ™€๏ธ

Additionally, targets referenced by the test command via generator expressions are added as dependencies of the test_prep/<name> target.

If multiple tests in different directories share the same name, their dependencies are merged into a single test_prep/<name> target.

Tests with names that are not valid target names are excluded from this behavior.

The BUILD_DEPENDS keyword can be used to add explicit build dependencies.

The command may be specified using generator expressions.

  • CONFIGURATIONS โ€” restrict execution of the test only to the named configurations. like, this test is for these configs only, don't @ me ๐Ÿ™ˆ
  • WORKING_DIRECTORY โ€” set the test property WORKING_DIRECTORY in which to execute the test. If not specified, the test will be run in CMAKE_CURRENT_BINARY_DIR. The working directory may be specified using generator expressions. ๐Ÿ“
  • BUILD_DEPENDS โ€” Added in version 4.4.

Specify a list of targets or files that must be built before the test can run. Each dependency is added to the test_prep/<name> build target described above when CMAKE_TEST_BUILD_DEPENDS is enabled with the Ninja Generators. The test name must be a valid target name in order to list build dependencies with this keyword. no valid target name, no deps, that's the rule ๐Ÿ’…

  • COMMAND_EXPAND_LISTS โ€” Added in version 3.16.

Lists in COMMAND arguments will be expanded, including those created with generator expressions. ๐ŸŽ€

If the test command exits with code 0 the test passes. Non-zero exit code is a "failed" test. The test property WILL_FAIL inverts this logic. Note that system-level test failures such as segmentation faults or heap errors will still fail the test even if WILL_FAIL is true. (yep, a segfault is a segfault, you can't gaslight ctest into thinking it's fine ๐Ÿ’€) Output written to stdout or stderr is captured by ctest(1) and only affects the pass/fail status via the PASS_REGULAR_EXPRESSION, FAIL_REGULAR_EXPRESSION, or SKIP_REGULAR_EXPRESSION test properties.

Added in version 3.16: added SKIP_REGULAR_EXPRESSION property.

Example usage:

add_test(NAME mytest
         COMMAND testDriver --config $<CONFIG>
                            --exe $<TARGET_FILE:myexe>)

This creates a test mytest whose command runs a testDriver tool passing the configuration name and the full path to the executable file produced by target myexe. cute right? look at those generator expressions go ๐Ÿ˜ญ๐Ÿ’–


The command syntax above is recommended over the older, less flexible form:

add_test(<name> <command> [<arg>...])

Add a test called <name> with the given command-line.

Unlike the above NAME signature, target names are not supported in the command-line. Furthermore, tests added with this signature do not support generator expressions in the command-line or test properties, and the TEST_LAUNCHER and CROSSCOMPILING_EMULATOR target properties are not supported. it's giving legacy, it's giving old money, but like... lowkey don't use it ๐ŸŒˆ

โญ aux_source_directory

Find all source files in a directory. ๐Ÿ•ต๏ธโ€โ™€๏ธ

aux_source_directory(<dir> <variable>)

Collects the names of all the source files in the specified directory and stores the list in the <variable> provided. This command is intended to be used by projects that use explicit template instantiation. Template instantiation files can be stored in a Templates subdirectory and collected automatically using this command to avoid manually listing all instantiations. bless, no giant hand-typed lists ๐Ÿซถ

It is tempting to use this command to avoid writing the list of source files for a library or executable target. While this seems to work, there is no way for CMake to generate a build system that knows when a new source file has been added. Normally the generated build system knows when it needs to rerun CMake because the CMakeLists.txt file is modified to add a new source. When the source is just added to the directory without modifying this file, one would have to manually rerun CMake to generate a build system incorporating the new file.

so like... it works until the day it doesn't bestie ๐Ÿ˜ญ you'll add a file, nothing happens, and you'll be sitting there like "hello???" โ€” that's the trap. ngl just list your sources ๐Ÿ’€

๐Ÿ‘‘ build_command

Get a command line to build the current project. This is mainly intended for internal use by the CTest module. it's an inside-job kind of command โœจ

build_command(<variable>
              [CONFIGURATION <config>]
              [PARALLEL_LEVEL <parallel>]
              [TARGET <target>]
              [PROJECT_NAME <projname>] # legacy, causes warning
             )

Sets the given <variable> to a command-line string of the form:

<cmake> --build . [--config <config>] [--parallel <parallel>] [--target <target>...]

where <cmake> is the location of the cmake(1) command-line tool, and <config>, <parallel> and <target> are the values provided to the CONFIGURATION, PARALLEL_LEVEL and TARGET options, if any. In CMake versions prior to 4.0, a trailing -- -i option was added for Makefile Generators if policy CMP0061 was not set to NEW.

psst โ€” that PROJECT_NAME option is legacy and causes a warning ๐Ÿ™ˆ you've been warned, bestie ๐ŸŽ€

When you invoke it, that cmake --build command line is literally gonna launch the underlying build system tool for you. ๐Ÿ› ๏ธ

Added in version 3.21: the PARALLEL_LEVEL argument can be used to set the --parallel flag. Go off, parallelism. ๐Ÿ”ฅ

build_command(<cachevariable> <makecommand>)

Okay so this second signature is deprecated ๐Ÿ™ˆ but still available for backwards compatibility โ€” use the first signature instead, bestie, fr fr.

It sets the given <cachevariable> to a command-line string just like the above, but without the --target option. The <makecommand> is ignored, but it should be the full path to devenv, nmake, make or one of the end user build tools for legacy invocations.

Note: in CMake versions prior to 3.0, this command returned a command line that directly invokes the native build tool for the current generator. Their implementation of the PROJECT_NAME option had no useful effects, so CMake now warns on use of the option. ๐Ÿ’€

๐Ÿฆ‹ cmake_file_api

Added in version 3.27.

This one lets you interact with the CMake file API โ€” cmake-file-api(7), iykyk. โœจ

cmake_file_api(QUERY ...)

The QUERY subcommand adds a file API query for the current CMake invocation. It's giving very "let me just ask nicely" energy:

cmake_file_api(
  QUERY
  API_VERSION <version>
  [CODEMODEL <versions>...]
  [CACHE <versions>...]
  [CMAKEFILES <versions>...]
  [TOOLCHAINS <versions>...]
)

API_VERSION must always be given โ€” no skipping it, queen. ๐Ÿ‘‘ Currently, the only supported value for <version> is 1. See API v1 for the deets on reply content and location.

Each of the optional keywords CODEMODEL, CACHE, CMAKEFILES and TOOLCHAINS correspond to one of the object kinds that can be requested by the project. The configureLog object kind cannot be set with this command, since it must be set before CMake starts reading the top level CMakeLists.txt file. Respect the ordering, bestie. ๐ŸŒธ

For each of the optional keywords, the <versions> list must contain one or more version values of the form major or major.minor, where major and minor are integers. Projects should list the versions they accept in their preferred order, because only the first supported value from the list will be selected. The command will ignore versions with a major version higher than any major version it supports for that object kind. It will raise an error if it encounters an invalid version number, or if none of the requested versions is supported โ€” so like, list a real one, okay? ๐Ÿ˜ญ

For each type of object kind requested, a query equivalent to a shared, stateless query will be added internally. No query file will be created in the file system. The reply will be written to the file system at generation time. ๐Ÿ’…

It is not an error to add a query for the same thing more than once, whether from query files or from multiple calls to cmake_file_api(QUERY). The final set of queries will be a merged combination of all queries specified on disk and queries submitted by the project. Slay, merging. ๐Ÿงš

Example

A project may want to use replies from the file API at build time to implement some form of verification task. Instead of relying on something outside of CMake to create a query file, the project can use cmake_file_api(QUERY) to request the required information for the current run. It can then create a custom command to run at build time, knowing that the requested information should always be available. Obsessed with the foresight honestly. ๐ŸŽ€

cmake_file_api(
  QUERY
  API_VERSION 1
  CODEMODEL 2.3
  TOOLCHAINS 1
)

add_custom_target(verify_project
  COMMAND ${CMAKE_COMMAND}
    -D BUILD_DIR=${CMAKE_BINARY_DIR}
    -D CONFIG=$<CONFIG>
    -P ${CMAKE_CURRENT_SOURCE_DIR}/verify_project.cmake
)

๐Ÿงš cmake_instrumentation

Added in version 4.3.

This enables interacting with the CMake Instrumentation API โ€” cmake-instrumentation(7). It basically allows for configuring instrumentation at the project-level. Main character energy for your build. ๐ŸŒˆ

cmake_instrumentation(
  API_VERSION <version>
  DATA_VERSION <version>
  [HOOKS <hooks>...]
  [OPTIONS <options>...]
  [CALLBACK <callback>]
  [CUSTOM_CONTENT <name> <type> <content>]
)

API_VERSION and DATA_VERSION must always be given. No exceptions, periodt.

API_VERSION is an integer. Currently, the only supported value is 1. See API v1 for details.

DATA_VERSION is a version value of the form major or major.minor. Currently, the maximum supported version is 1.1. See Data Version for details.

Each of the optional keywords HOOKS, OPTIONS, and CALLBACK correspond to one of the parameters to the v1 Query Files. The CALLBACK keyword can be provided multiple times to create multiple callbacks. Stack 'em up. ๐Ÿ’œ

Whenever cmake_instrumentation is invoked, a query file is generated in <build>/.cmake/instrumentation/v1/query/generated โ€” that's the path, write it down โ€” to enable instrumentation with the provided arguments. โœจ

Custom CMake Content

The CUSTOM_CONTENT argument specifies certain data from configure time to include in each v1 CMake Content File. This may be used to associate instrumentation data with certain information about its configuration, such as the optimization level or whether it is part of a coverage build. Lowkey so useful. ๐Ÿ”ฅ

CUSTOM_CONTENT expects name, type and content arguments.

name is a specifier to identify the content being reported.

type specifies how the content should be interpreted. Supported values are:

  • STRING โ€” the content is a string.
  • BOOL โ€” the content should be interpreted as a boolean. It will be true under the same conditions that if() would be true for the given value.
  • LIST โ€” the content is a CMake ; separated list that should be parsed.
  • JSON โ€” the content should be parsed as a JSON string. This can be a number such as 1 or 5.0, a quoted string such as \"string\", a boolean value true/false, or a JSON object such as { \"key\" : \"value\" } that may be constructed using string(JSON ...) commands.

content is the actual content to report. ๐Ÿซถ

Example

The following example shows an invocation of the command and its equivalent JSON query file:

cmake_instrumentation(
  API_VERSION 1
  DATA_VERSION 1.0
  HOOKS postGenerate preCMakeBuild postCMakeBuild
  OPTIONS staticSystemInformation dynamicSystemInformation compileTrace trace
  CALLBACK ${CMAKE_COMMAND} -P /path/to/handle_data.cmake
  CALLBACK ${CMAKE_COMMAND} -P /path/to/handle_data_2.cmake
  CUSTOM_CONTENT myString STRING string
  CUSTOM_CONTENT myList   LIST   "item1;item2"
  CUSTOM_CONTENT myObject JSON   "{ \"key\" : \"value\" }"
)
{
  "version": 1,
  "hooks": [
    "postGenerate", "preCMakeBuild", "postCMakeBuild"
  ],
  "options": [
    "staticSystemInformation", "dynamicSystemInformation", "compileTrace", "trace"
  ],
  "callbacks": [
    "/path/to/cmake -P /path/to/handle_data.cmake",
    "/path/to/cmake -P /path/to/handle_data_2.cmake"
  ]
}

This will also result in the following content included in each v1 CMake Content File:

"custom": {
  "myString": "string",
  "myList": [
    "item1", "item2"
  ],
  "myObject": {
    "key": "value"
  }
}

๐ŸŒธ create_test_sourcelist

Create a test driver program that links together many small tests into a single executable. This is useful when building static executables with large libraries to shrink the total required size. Tiny exe, big slay. ๐Ÿ’…

create_test_sourcelist(<sourceListName> <driverName> <test>... <options>...)

Generate a test driver source file from a list of individual test sources and provide a combined list of sources that can be built as an executable.

The options are:

  • <sourceListName> โ€” the name of a variable in which to store the list of source files needed to build the test driver. The list will contain the <test>... sources and the generated <driverName> source.
Changed in version 3.29: the test driver source is listed by absolute path in the build tree. Previously it was listed only as <driverName>.
  • <driverName> โ€” name of the test driver source file to be generated into the build tree. The source file will contain a main() program entry point that dispatches to whatever test is named on the command line. Very "you're the one in charge" of it. ๐Ÿ‘‘
  • <test>... โ€” test source files to be added to the driver binary. Each test source file must have a function in it that is the same name as the file with the extension removed. For example, a foo.cxx test source might contain:
int foo(int argc, char** argv)
  • EXTRA_INCLUDE <header> โ€” specify a header file to #include in the generated test driver source.
  • FUNCTION <function> โ€” specify a function to be called with pointers to argc and argv. The function may be provided in the EXTRA_INCLUDE header:
void function(int* pargc, char*** pargv)

This can be used to add extra command line processing to each test. Fancy. โœจ

Additionally, some CMake variables affect test driver generation:

  • CMAKE_TESTDRIVER_BEFORE_TESTMAIN โ€” code to be placed directly before calling each test's function.
  • CMAKE_TESTDRIVER_AFTER_TESTMAIN โ€” code to be placed directly after the call to each test's function.

The generated test driver supports the following command-line arguments:

  • <name> โ€” run the test with the exact name <name> (case-insensitive).
  • -R <substr> โ€” run the first test whose name contains <substr> (case-insensitive).
  • -A [<skip_test>...] โ€” Added in version 3.21: run all tests and print results in TAP format (that's the Test Anything Protocol, https://testanything.org/, iykyk ๐Ÿฆ‹). Any additional arguments after -A are interpreted as exact test names to skip.
  • -N โ€” Added in version 4.4: list all available test names (one per line) and exit. Literally just the list and dip. ๐Ÿ’€

Example ๐Ÿ“

okay so here's the whole vibe in one go โ€” this is literally how you'd wire discovery into a suite you built with create_test_sourcelist:

create_test_sourcelist(SRCS main.c test1.c test2.c)
add_executable(MyTests ${SRCS})
discover_tests(COMMAND MyTests
  DISCOVERY_ARGS -N
  DISCOVERY_MATCH "^(.+)$"
  TEST_NAME "${PROJECT_NAME}.\\1"
  TEST_ARGS "\\1"
)

bestie that's it, that's the post ๐Ÿ’…

See Also ๐Ÿซถ

  • discover_tests()

define_property ๐Ÿ’…

Define and document custom properties.

define_property(<GLOBAL | DIRECTORY | TARGET | SOURCE |
                 TEST | VARIABLE | CACHED_VARIABLE>
                 PROPERTY <name> [INHERITED]
                 [BRIEF_DOCS <brief-doc> [<docs>...]]
                 [FULL_DOCS <full-doc> [<docs>...]]
                 [INITIALIZE_FROM_VARIABLE <variable>])

okay so this one defines one property in a scope, and then you get to use it with set_property() and get_property(). Lowkey its main character era is all about how a property gets initialized or inherited โ€” like, that's the whole point now. Historically this command also glued documentation onto a property, but ngl that's not really considered a primary use case anymore, so don't come here expecting a docs generator.

The first argument decides the kind of scope the property lives in, and it has to be exactly one of these:

  • GLOBAL โ€” associated with the global namespace.
  • DIRECTORY โ€” associated with one directory.
  • TARGET โ€” associated with one target.
  • SOURCE โ€” associated with one source file.
  • TEST โ€” associated with a test named with add_test().
  • VARIABLE โ€” documents a CMake language variable.
  • CACHED_VARIABLE โ€” documents a CMake cache variable.
โœจ heads up bestie: unlike set_property() and get_property(), you do not hand over an actual scope here. No real directory, no real target, none of that. Only the kind of scope matters. it's giving "concept, not instance".

The required PROPERTY option comes immediately followed by the name of the property you're defining.

If you pass the INHERITED option, then get_property() will chain up to the next higher scope whenever the property you asked for isn't set in the scope you gave it. The chaining goes like this:

  • DIRECTORY scope chains to its parent directory's scope, and keeps walking up parent directories until some directory has the property set โ€” or until there are literally no more parents. If it's still not found at the top level directory, it chains to the GLOBAL scope.
  • TARGET, SOURCE and TEST properties chain to DIRECTORY scope, and from there keep chaining up the directories and so on, as needed.
โš ๏ธ but listen โ€” this scope chaining behavior only applies to calls to get_property(), get_directory_property(), get_target_property(), get_source_file_property() and get_test_property(). There is no inheriting behavior when setting properties, so using APPEND or APPEND_STRING with the set_property() command will not consider inherited values when working out the contents to append to. fr fr, that's a trap.

The BRIEF_DOCS and FULL_DOCS options are followed by strings that get associated with the property as its brief and full documentation. CMake doesn't use this documentation for anything other than making it available to the project through the corresponding options of the get_property() command.

๐Ÿฆ‹ Changed in version 3.23: The BRIEF_DOCS and FULL_DOCS options are optional.
๐Ÿ”ฅ Added in version 3.23: The INITIALIZE_FROM_VARIABLE option specifies a variable from which the property should be initialized. It can only be used with target properties. The <variable> name must end with the property name and must not begin with CMAKE_ or _CMAKE_. The property name must contain at least one underscore. It is recommended that the property name have a prefix specific to the project.

Property Redefinition ๐ŸŒˆ

So once a property is defined for a particular type of scope, it cannot be redefined. Any attempt to redefine an existing property by calling define_property() with the same scope type and property name gets silently ignored โ€” no error, no drama, just vibes and nothing happening. Defining the same property name for two different kinds of scope? Totally valid, slay.

You can use get_property() to figure out whether a property is already defined for a particular kind of scope, and if it is, to examine its definition. Like this:

# Initial definition
define_property(TARGET PROPERTY MY_NEW_PROP
  BRIEF_DOCS "My new custom property"
)

# Later examination
get_property(my_new_prop_exists
  TARGET NONE
  PROPERTY MY_NEW_PROP
  DEFINED
)

if(my_new_prop_exists)
  get_property(my_new_prop_docs
    TARGET NONE
    PROPERTY MY_NEW_PROP
    BRIEF_DOCS
  )
  # ${my_new_prop_docs} is now set to "My new custom property"
endif()

See Also โœจ

  • get_property()
  • set_property()

discover_tests ๐Ÿ”ฅ

๐Ÿ‘‘ Added in version 4.4.

Register tests with names and properties discovered at test time by ctest(1).

discover_tests(COMMAND <command> [<arg>...] [COMMAND_EXPAND_LISTS]
  [CONFIGURATIONS <config>...]
  DISCOVERY_ARGS <arg>...
  DISCOVERY_MATCH <regex>
  [DISCOVERY_PROPERTIES <key> <value> [<key> <value>]...]
  TEST_NAME <replacement>
  TEST_ARGS <replacement>...
  [TEST_PROPERTIES <key> <replacement> [<key> <replacement>]...]
)

okay so this is not "define one test at configure time" energy at all. This command configures test discovery. During test execution, ctest(1) runs the discovery command you gave it, parses the output, and then registers one or more tests based on the regular expression and replacement strings you provided. it's giving "surprise, here are your tests" ๐ŸŽ

Here are the discover_tests options:

  • COMMAND โ€” Specify the command-line used for test discovery.

The command is executed by ctest(1) at test time (not by CMake at configure time). With DISCOVERY_ARGS appended, it must print the list of available tests in a format that DISCOVERY_MATCH matches.

If <command> specifies an executable target created by add_executable():

  • It will automatically be replaced by the location of the executable created at build time.
  • The target's CROSSCOMPILING_EMULATOR, if set, will be used to run the command on the host: <emulator> <command>. The emulator is used only when cross-compiling โ€” that's CMAKE_CROSSCOMPILING, iykyk.
  • The target's TEST_LAUNCHER, if set, will be used to launch the command: <launcher> <command>. If the CROSSCOMPILING_EMULATOR is also set, both are used: <launcher> <emulator> <command>.

The command may be specified using generator expressions.

  • COMMAND_EXPAND_LISTS โ€” Lists in COMMAND arguments will be expanded, including those created with generator expressions.
  • CONFIGURATIONS โ€” Restrict the test discovery only to the named configurations.
  • DISCOVERY_ARGS โ€” Additional arguments passed to COMMAND when performing discovery.
  • DISCOVERY_MATCH โ€” Regular expression used to parse each line produced by the discovery command. Capturing groups may be referenced by TEST_NAME, TEST_ARGS, and values in TEST_PROPERTIES using \1, \2, etc.
  • DISCOVERY_PROPERTIES โ€” Specify properties for the discovery run itself.
  • TEST_NAME โ€” Replacement string used to generate the test name for each discovered test. It may reference capture groups from DISCOVERY_MATCH.
  • TEST_ARGS โ€” Replacement strings used to generate the arguments passed to the discovered test. Each argument may reference capture groups from DISCOVERY_MATCH.
  • TEST_PROPERTIES โ€” Specify test properties to set on each discovered test. Values are replacement strings and may reference capture groups from DISCOVERY_MATCH.

CTest executes the discovery step to obtain the list of tests, and then runs each discovered test using the command-line produced by COMMAND together with TEST_ARGS. The pass/fail behavior of each discovered test follows the usual CTest rules โ€” exit code 0 indicates success unless inverted by the WILL_FAIL property. Output written to stdout or stderr is captured by ctest(1) and only affects the pass/fail status via the PASS_REGULAR_EXPRESSION, FAIL_REGULAR_EXPRESSION, or SKIP_REGULAR_EXPRESSION test properties. no extra chaos, just the rules ๐Ÿ’€

Example usage:

discover_tests(COMMAND testDriver --exe $<TARGET_FILE:myexe>
  DISCOVERY_ARGS --list-tests
  DISCOVERY_MATCH "^([^,]+),([^,]+),([^,]+),(.*)$"
  TEST_NAME "${PROJECT_NAME}.\\1.\\2"
  TEST_ARGS --run-test "\\1.\\2"
  TEST_PROPERTIES
    PROCESSORS "\\3"
    LABELS "\\4"
)

This example configures discovery by running testDriver --list-tests. For each line of output that matches DISCOVERY_MATCH, a test name is generated using TEST_NAME, the per-test command-line is generated using TEST_ARGS, and test properties are populated from the remaining capture groups. honestly? iconic. ๐ŸŒŸ


enable_language ๐ŸŽ€

Enable languages (CXX/C/OBJC/OBJCXX/Fortran/etc)

enable_language(<lang>... [OPTIONAL])

Enables support for the named languages in CMake. This is the same as the project() command, except it does not create any of the extra variables that project() creates. so like, same energy, less baggage ๐Ÿซถ

The supported languages are:

  • C
  • CXX โ€” C++
  • CSharp โ€” Added in version 3.8. C#
  • CUDA โ€” Added in version 3.8.
  • OBJC โ€” Added in version 3.16. Objective-C
  • OBJCXX โ€” Added in version 3.16. Objective-C++
  • Fortran
  • HIP โ€” Added in version 3.21.
  • ISPC โ€” Added in version 3.18.
  • Swift โ€” Added in version 3.15.
  • ASM โ€” Assembly language supported by the C compiler.
๐Ÿ™ˆ If enabling ASM, list it last so that CMake can check whether the C or CXX compiler supports assembly.
  • ASM_NASM โ€” Netwide Assembler
  • ASM_MARMASM โ€” Added in version 3.26. Microsoft Assembler (ARM, ARM64)
  • ASM_MASM โ€” Microsoft Assembler (x86, x64)

โœจ ASM_POASM

Added in version 4.4. ๐Ÿซถ

Pelles C toolchain assembler. That's the whole vibe, bestie โ€” it's just the assembler that comes with the Pelles C toolchain. Slay.

๐ŸŒธ ASM-ATT

Okay so before you go wild calling enable_language() wherever your heart desires, here are the rules โ€” and they are not optional, fr fr:

  • It must be called in file scope โ€” not inside a function() call and not inside a block(). Periodt.
  • It must not be called before the first call to project(). Go check policy CMP0165 for the receipts.
  • It must be called in the highest directory common to all targets using the named language โ€” whether those targets use it directly for compiling sources, or indirectly through link dependencies. Honestly? Easiest glow-up move is to just enable all the languages you need in the top-level directory of your project. No cap.

The OPTIONAL keyword is literally just a placeholder for future implementation and does not currently work. ๐Ÿ’€ Instead, you can use the CheckLanguage module to verify support before enabling. Iconic workaround behavior.


๐ŸŽ€ enable_testing

Enables testing for the current directory and everything below it:

enable_testing()

You should invoke this in the top-level source directory because ctest(1) expects to find a test file in the top-level build directory. Like, it's just what ctest looks for. ๐Ÿ™ˆ

This command is also automatically invoked when the CTest module is included โ€” unless the BUILD_TESTING option is turned off. Sneaky but iconic.

And the restrictions on where you can call enable_testing():

  • It must be called in file scope โ€” not in a function() call, and not inside a block().

๐Ÿ’– Examples

In the following example, this command is conditionally called depending on how the project is used. Say the Example project gets added via the FetchContent module as a subdirectory of a parent project that defines its own tests โ€” then testing for the Example project is disabled. Clean. ๐Ÿงš

CMakeLists.txt

project(Example)

option(Example_ENABLE_TESTING "Enable testing" ${PROJECT_IS_TOP_LEVEL})

if(Example_ENABLE_TESTING)
  enable_testing()
endif()

# ...

if(Example_ENABLE_TESTING)
  add_test(...)
endif()

๐Ÿฆ‹ See Also

  • The add_test() command. Go look at it, iykyk.

๐Ÿ‘‘ export

Export targets or packages so outside projects can use them directly from the current project's build tree โ€” no installation required. โœจ

And if you do want to export from an install tree, go see the install(EXPORT) command instead.

โญ Synopsis

export(TARGETS <target>... [...])
export(EXPORT <export-name> [...])
export(PACKAGE <PackageName>)
export(SETUP <export-name> [...])
export(PACKAGE_INFO <package-name> [...])
export(SBOM <sbom-name> [...])

๐Ÿ’… Exporting Targets

export(TARGETS <target>... [NAMESPACE <namespace>]
       [APPEND] FILE <filename> [EXPORT_LINK_INTERFACE_LIBRARIES]
       [CXX_MODULES_DIRECTORY <directory>])

Creates a file <filename> that outside projects can include to import the targets named by <target>... from the current project's build tree. This is genuinely so useful when cross-compiling โ€” you build utility executables that run on the host platform in one project, then import them into another project being compiled for the target platform. Main character energy for the host. ๐Ÿซถ

The file this command creates is specific to the build tree and should never be installed. Like, ever. If you want to export from an install tree, that's the install(EXPORT) command's whole thing.

The options are:

  • NAMESPACE <namespace> โ€” Prepends the <namespace> string to all target names written to the file.
  • APPEND โ€” Appends to the file instead of overwriting it. Perfect for incrementally exporting multiple targets into the same file, one at a time.
  • EXPORT_LINK_INTERFACE_LIBRARIES โ€” Includes the contents of the properties named with the pattern (IMPORTED_)?LINK_INTERFACE_LIBRARIES(_<CONFIG>)? in the export, even when policy CMP0022 is NEW. Useful for supporting consumers still running CMake versions older than 2.8.12. Respect the elders. ๐Ÿ™
  • CXX_MODULES_DIRECTORY <directory> โ€” Added in version 3.28. Exports C++ module properties to files under the given directory. Each file gets named according to the target's export name (without any namespace), and these files are automatically included from the export file. Auto-magic, love that. โœจ

This signature requires all targets to be listed explicitly. And ngl, if a library target is included in the export but a target it links to is not included, the behavior is unspecified โ€” so don't do that. ๐Ÿ’€ If you want to automatically export the same targets from the build tree that install(EXPORT) would from an install tree, go use the export(EXPORT) signature.

Note: Object Libraries under Xcode have special handling if multiple architectures are listed in CMAKE_OSX_ARCHITECTURES. In that case they get exported as Interface Libraries with no object files available to clients. Which is honestly sufficient to satisfy transitive usage requirements of other targets that link to the object libraries in their implementation. It works, bestie.

This command exports all Build Configurations from the build tree. If you need to map configurations of dependent projects to the exported configurations, see the CMAKE_MAP_IMPORTED_CONFIG_<CONFIG> variable.

๐ŸŒˆ Exporting Targets to Android.mk

export(TARGETS <target>... ANDROID_MK <filename>)

Added in version 3.7.

This signature exports CMake built targets to the android ndk build system by creating an Android.mk file that references the prebuilt targets. The Android NDK supports prebuilt libraries โ€” both static and shared โ€” which lets CMake build a project's libraries and hand them over to an ndk build system, complete with transitive dependencies, include flags and defines required to use those libraries. Literally giving it everything it needs. ๐Ÿ’–

The signature takes a list of targets and drops them into the Android.mk file specified by the <filename> you give it. You can only use this signature if policy CMP0022 is NEW for all the targets given โ€” if that policy is OLD for even one of them, an error gets issued. No exceptions, periodt.

๐Ÿงš Exporting Targets matching install(EXPORT)

export(EXPORT <export-name> [NAMESPACE <namespace>] [FILE <filename>]
       [CXX_MODULES_DIRECTORY <directory>] [EXPORT_PACKAGE_DEPENDENCIES])

Creates a file <filename> that outside projects can include to import targets from the current project's build tree. It's the same as the export(TARGETS) signature, except you don't list the targets explicitly โ€” instead it exports the targets associated with the installation export <export-name>. Those target installations get associated with the export <export-name> using the EXPORT option of the install(TARGETS) command.

  • EXPORT_PACKAGE_DEPENDENCIES โ€” > Note: Experimental. Gated by CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_DEPENDENCIES. Specifies that find_dependency() calls should be exported. See install(EXPORT) for the details on how this works.

๐Ÿ“ Exporting Targets to the Common Package Specification

export(PACKAGE_INFO <package-name> EXPORT <export-name>
       [PROJECT <project-name>|NO_PROJECT_METADATA]
       [APPENDIX <appendix-name>]
       [LOWER_CASE_FILE]
       [VERSION <version>
        [COMPAT_VERSION <version>]
        [VERSION_SCHEMA <string>]]
       [DEFAULT_TARGETS <target>...]
       [DEFAULT_CONFIGURATIONS <config>...]
       [LICENSE <license-string>]
       [DEFAULT_LICENSE <license-string>]
       [DESCRIPTION <description-string>]
       [HOMEPAGE_URL <url-string>]
       [CXX_MODULES_DIRECTORY <directory>])

Added in version 4.3.

Creates a file in the Common Package Specification (https://cps-org.github.io/cps/) that outside projects can include to import targets named by <target>... from the current project's build tree. If you want to export targets from an install tree, see the install(PACKAGE_INFO) command. The imported targets are implicitly in the namespace <package-name>. ๐ŸŽ€

The default file name is <package-name>[-<appendix-name>].cps. If you pass the LOWER_CASE_FILE option, the file name uses the package name converted to lower case. Files get written to the cps/<package-name> subdirectory of the current build directory.

For a description of all the other options, go see install(PACKAGE_INFO).

๐Ÿ”ฅ Exporting Software Bill of Materials (SBOM) Documents

export(SBOM <sbom-name> EXPORTS <export-names>...
       [FORMAT <string>]
       [PROJECT <project-name>|NO_PROJECT_METADATA]
       [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
       [LICENSE <license-string>]
       [DESCRIPTION <description-string>]
       [HOMEPAGE_URL <url-string>]
       [PACKAGE_URL <url-string>])

Added in version 4.3.

Note: Experimental. Gated by CMAKE_EXPERIMENTAL_GENERATE_SBOM.

Generates a software bill of materials (SBOM) document describing the targets in the listed <export-names> export sets and their dependencies in the build tree. Targets from all the listed exports get aggregated into one document. The generated SBOM is written to the sbom/<sbom-name> subdirectory of the current build directory.

See install(SBOM) for the details about the supported SBOM formats and a description of all the other options. ๐Ÿฆ‹

๐Ÿ’œ Exporting Packages

export(PACKAGE <PackageName>)

Stores the current build directory in the CMake user package registry for package <PackageName>. The find_package() command may consider that directory while searching for package <PackageName>, which helps dependent projects find and use a package from the current project's build tree without any help from the user. Self-sufficient queen behavior. ๐Ÿ‘‘

Note that the entry in the package registry this command creates only works in conjunction with a package configuration file (<PackageName>Config.cmake) that works with the build tree. And in some cases โ€” packaging, for example, and system-wide installations โ€” writing the user package registry is just not desirable. Read the room.

Changed in version 3.1: If the CMAKE_EXPORT_NO_PACKAGE_REGISTRY variable is enabled, the export(PACKAGE) command will do nothing. Literally nothing. It's giving ghosted. ๐Ÿ’€

okay so quick glow-up era for export(PACKAGE) โ€” Changed in version 3.15: by default the export(PACKAGE) command literally does nothing now (peep policy CMP0090) ๐Ÿ™ˆ because populating the user package registry has effects outside the source and build trees, which is giving "side effects you didn't ask for" energy. If you actually want build directories added to the CMake user package registry, set the CMAKE_EXPORT_PACKAGE_REGISTRY variable. That's the whole vibe check. โœจ

๐ŸŒธ Configuring Exports ๐ŸŒธ

export(SETUP <export-name> [...])
export(SETUP <export-name>
       [PACKAGE_DEPENDENCY <dep>
        [ENABLED (<bool-true>|<bool-false>|AUTO)]
        [EXTRA_ARGS <args>...]
       ] [...]
       [TARGET <target>
        [XCFRAMEWORK_LOCATION <location>]
       ] [...]
       )

Added in version 3.29. This is how you configure the params of an export, bestie. The arguments are:

  • PACKAGE_DEPENDENCY <dep> โ€” specify a package dependency to configure. This changes how find_dependency() calls get written during export(EXPORT) and install(EXPORT). <dep> is the name of a package to export. It accepts these extra args:
Note: this one's Experimental. It's gated by CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_DEPENDENCIES, so like... gatekept, iykyk.
  • ENABLED โ€” manually control whether the dependency gets exported or not. Takes these values:
    • <bool-true> โ€” any value CMake recognizes as "true". Always export the dependency, even if no exported targets depend on it. You can use this to manually add find_dependency() calls to the export. ๐Ÿ’…
    • <bool-false> โ€” any value CMake recognizes as "false". Never export the dependency, even if an exported target does depend on it. ๐Ÿ’€
    • AUTO โ€” only export the dependency if an exported target depends on it. The chill, lowkey option. ๐Ÿฆ‹
  • EXTRA_ARGS <args> โ€” extra arguments to pass to find_dependency() after the REQUIRED argument.
  • TARGET <target> โ€” specify a target to configure in this export. It takes this extra arg:
    • XCFRAMEWORK_LOCATION โ€” specify the location of an .xcframework which contains the library from this target. If you specify it, the generated code will check to see if the .xcframework exists, and if it does, it'll use the .xcframework as its imported location instead of the installed library. ๐Ÿ“

๐ŸŽ€ fltk_wrap_ui ๐ŸŽ€

Create FLTK user interfaces Wrappers.

fltk_wrap_ui(resultingLibraryName source1
             source2 ... sourceN)

Produce .h and .cxx files for all the .fl and .fld files listed. The resulting .h and .cxx files get added to a variable named resultingLibraryName_FLTK_UI_SRCS โ€” and that variable should be added to your library. That's it, that's the whole job. โœจ

๐Ÿ’œ get_source_file_property ๐Ÿ’œ

Get a property for a source file.

get_source_file_property(<variable> <file>
                         [DIRECTORY <dir> | TARGET_DIRECTORY <target>]
                         <property>)

Gets a property from a source file, and the value of the property gets stored in the specified <variable>. If <file> isn't a source file, or the source property just isn't found, <variable> will be set to NOTFOUND. ๐Ÿ˜ญ And if the source property was defined to be an INHERITED property (see define_property()), the search will include the relevant parent scopes, exactly as described for the define_property() command.

By default, the source file's property gets read from the current source directory's scope. Periodt.

Added in version 3.18: directory scope can be overridden with one of these sub-options:

  • DIRECTORY <dir> โ€” the source file property gets read from the <dir> directory's scope. CMake must already know about that source directory, either by having added it through a call to add_subdirectory() or by <dir> being the top level source directory. Relative paths are treated as relative to the current source directory. ๐Ÿซถ
  • TARGET_DIRECTORY <target> โ€” the source file property gets read from the directory scope in which <target> was created (<target> must therefore already exist, no cap).

Use set_source_files_properties() to set property values. Source file properties usually control how the file is built. One property that's always there is LOCATION. ๐Ÿ‘‘

Note: the GENERATED source file property may be globally visible. See its documentation for the details, bestie.

โญ See Also โญ

  • define_property()
  • the more general get_property() command
  • set_source_files_properties()

๐Ÿ‘‘ get_target_property ๐Ÿ‘‘

Get a property from a target.

get_target_property(<variable> <target> <property>)

Get a property from a target โ€” the value of the property gets stored in the specified <variable>. If the target property isn't found, <variable> will be set to <variable>-NOTFOUND. Yes, literally the variable name plus -NOTFOUND. ๐Ÿ˜ญ If the target property was defined to be an INHERITED property (see define_property()), the search will include the relevant parent scopes, as described for the define_property() command.

Use set_target_properties() to set target property values. Properties usually control how a target is built, but some of them query the target instead. This command can get properties for any target created so far โ€” the targets do not need to be in the current CMakeLists.txt file. Main character energy: it'll reach across files for you. ๐Ÿ’…

๐ŸŒˆ See Also ๐ŸŒˆ

  • define_property()
  • the more general get_property() command
  • set_target_properties()
  • Properties on Targets for the list of properties known to CMake

๐Ÿงš get_test_property ๐Ÿงš

Get a property of the test.

get_test_property(<test> <property> [DIRECTORY <dir>] <variable>)

Get a property from the test, and the value of the property gets stored in the specified <variable>. If <test> isn't defined, or the test property isn't found, <variable> will be set to NOTFOUND. ๐Ÿ˜ญ If the test property was defined to be an INHERITED property (see define_property()), the search will include the relevant parent scopes, as described for the define_property() command.

For a list of standard properties you can type cmake --help-property-list. Go off, it's free. โœจ

Added in version 3.28: directory scope can be overridden with this sub-option:

  • DIRECTORY <dir> โ€” the test property gets read from the <dir> directory's scope. CMake must already know about that source directory, either by having added it through a call to add_subdirectory() or by <dir> being the top level source directory. Relative paths are treated as relative to the current source directory. Also, <dir> may reference a binary directory. ๐Ÿฆ‹

๐Ÿ’– See Also ๐Ÿ’–

  • define_property()
  • the more general get_property() command

๐ŸŒธ include_directories ๐ŸŒธ

Add include directories to the build.

include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])

Add the given directories to those the compiler uses to search for include files. Relative paths are interpreted as relative to the current source directory. ๐ŸŽ€

The include directories get added to the INCLUDE_DIRECTORIES directory property for the current CMakeLists file. They're also added to the INCLUDE_DIRECTORIES target property for each target in the current CMakeLists file. And the target property values are the ones actually used by the generators, so like... that's the real ones. ๐Ÿ‘‘

By default the directories you specify are appended onto the current list of directories. This default behavior can be changed by setting CMAKE_INCLUDE_DIRECTORIES_BEFORE to ON. And by using AFTER or BEFORE explicitly, you can pick between appending and prepending, independent of the default. Choose your fighter. ๐Ÿ’…

If the SYSTEM option is given, the compiler will be told the directories are meant as system include directories on some platforms. Signaling this setting might achieve effects such as the compiler skipping warnings, or these fixed-install system files not being considered in dependency calculations โ€” see compiler docs for the deets.

Arguments to include_directories may use generator expressions with the syntax $<...>. See the cmake-generator-expressions(7) manual for available expressions. See the cmake-buildsystem(7) manual for more on defining buildsystem properties. ๐ŸŒˆ

Note: prefer the target_include_directories() command to add include directories to individual targets and optionally propagate/export them to dependents. Like, fr fr, that's the modern way.

๐Ÿซถ See Also ๐Ÿซถ

  • target_include_directories()

๐Ÿ”ฅ include_external_msproject ๐Ÿ”ฅ

Include an external Microsoft project file in the solution file produced by Visual Studio Generators. Ignored on other generators. ๐Ÿ’€

include_external_msproject(projectname location
                           [TYPE projectTypeGUID]
                           [GUID projectGUID]
                           [PLATFORM platformName]
                           dep1 dep2 ...)

Includes an external Microsoft project in the generated solution file. This will create a target named [projectname] โ€” and you can use that in the add_dependencies() command to make things depend on the external project. ๐Ÿฆ‹

TYPE, GUID and PLATFORM are optional parameters that let you specify the type of project, the id (GUID) of the project, and the name of the target platform. This is useful for projects requiring values other than the default (e.g. WIX projects). โœจ

Added in version 3.9: if the imported project has different configuration names than the current project, set the MAP_IMPORTED_CONFIG_<CONFIG> target property to specify the mapping. Matching fits, no cap. ๐Ÿ’œ

๐ŸŒธ include_regular_expression ๐ŸŒธ

okay so this one is literally just setting the regex vibe for dependency checking, bestie โ€” nothing scary.

include_regular_expression(regex_match [regex_complain])

It sets the regular expressions used in dependency checking. Only files matching regex_match get traced as dependencies. Only files matching regex_complain will generate warnings if they can't be found (and ngl, standard header paths are not searched โ€” so don't come for it when your system headers don't show up).

The default for regex_match is "^.*$" (match everything, she's generous ๐Ÿ’…). The default for regex_complain is "^$" (match the empty string only).


๐ŸŽ€ install ๐ŸŽ€

โœจ Synopsis โœจ

install(TARGETS <target>... [...])
install(IMPORTED_RUNTIME_ARTIFACTS <target>... [...])
install({FILES | PROGRAMS} <file>... [...])
install(DIRECTORY <dir>... [...])
install(SCRIPT <file> [...])
install(CODE <code> [...])
install(EXPORT <export-name> [...])
install(PACKAGE_INFO <package-name> [...])
install(RUNTIME_DEPENDENCY_SET <set-name> [...])
install(SBOM <sbom-name> [...])

๐Ÿ’– Introduction ๐Ÿ’–

This command generates installation rules for a project. Install rules that you specify by calling install() inside a source directory are executed in order during installation. Periodt, order matters.

Changed in version 3.14: install rules in subdirectories added by calls to add_subdirectory() are interleaved with those in the parent directory, so they run in the order declared (see policy CMP0082). Cute, right? ๐Ÿซถ

Changed in version 3.22: the environment variable CMAKE_INSTALL_MODE can override the default copying behavior of install(). iconique.

Changed in version 3.31: projects can enable INSTALL_PARALLEL to enable a parallel installation. When using the parallel install, subdirectories added by calls to add_subdirectory() are installed independently, and the order that install rules added in different subdirectories will run is not guaranteed. So likeโ€ฆ chaos, but scheduled chaos ๐Ÿ’€.

๐ŸŒˆ Common Options ๐ŸŒˆ

There are multiple signatures for this command, bestie. Some of them define installation options for files and targets. Options common to multiple signatures are covered right here, but heads up: they're valid only for signatures that specify them. No sneaking them in elsewhere ๐Ÿšซ.

  • DESTINATION <dir> โ€” specifies the directory on disk to which a file will be installed. <dir> should be a relative path. An absolute path is allowed, but not recommended.
    • Added in version 4.4: the CMD_INSTALL_ABSOLUTE_DESTINATION diagnostic can be enabled to warn or error out when an absolute destination is provided.
    • When a relative path is given, it is interpreted relative to the value of the CMAKE_INSTALL_PREFIX variable. The prefix can be relocated at install time using the DESTDIR mechanism explained in the CMAKE_INSTALL_PREFIX variable documentation.
    • As absolute paths do not work with the cmake --install command's --prefix option, or with the cpack installer generators, it is strongly recommended to use relative paths throughout for best support by package maintainers. In particular, there is no need to make paths absolute by prepending CMAKE_INSTALL_PREFIX โ€” this prefix is used by default if the DESTINATION is a relative path. so likeโ€ฆ pls don't ๐Ÿ™ˆ.
    • If an absolute path (with a leading slash or drive letter) is given, it is used verbatim.
    • Changed in version 3.31: <dir> will be normalized according to the same normalization rules as the cmake_path() command.
  • PERMISSIONS <permission>... โ€” specify permissions for installed files. The valid permissions are OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ, GROUP_WRITE, GROUP_EXECUTE, WORLD_READ, WORLD_WRITE, WORLD_EXECUTE, SETUID, and SETGID. Permissions that don't make sense on certain platforms are just ignored on those platforms โ€” she's flexible like that โœจ.
    • If this option is used multiple times in a single call, its list of permissions accumulates. If an install(TARGETS) call uses <artifact-kind> arguments, a separate list of permissions is accumulated for each kind of artifact.
  • CONFIGURATIONS <config>... โ€” specify a list of build configurations for which the install rule applies (Debug, Release, etc., iykyk).
    • If this option is used multiple times in a single call, its list of configurations accumulates. If an install(TARGETS) call uses <artifact-kind> arguments, a separate list of configurations is accumulated for each kind of artifact.
  • COMPONENT <component> โ€” specify an installation component name with which the install rule is associated, such as Runtime or Development. During component-specific installation, only install rules associated with the given component name will be executed. During a full installation, all components are installed unless marked with EXCLUDE_FROM_ALL. If COMPONENT is not provided, a default component "Unspecified" is created. The default component name may be controlled with the CMAKE_INSTALL_DEFAULT_COMPONENT_NAME variable.
    • Installation components can then be used by the cmake --install command's --component option and the CPackComponent module. The global target list_install_components lists all available components. shoutout to that target fr fr ๐Ÿ‘‘
  • EXCLUDE_FROM_ALL โ€” Added in version 3.6. Specify that the file is excluded from a full installation and only installed as part of a component-specific installation.
  • OPTIONAL โ€” specify that it is not an error if the file to be installed does not exist. no drama, no vibe check failed ๐Ÿ’….

Added in version 3.1: command signatures that install files may print messages during installation. Use the CMAKE_INSTALL_MESSAGE variable to control which messages are printed.

Added in version 3.11: many of the install() variants implicitly create the directories containing the installed files. If CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS is set, these directories will be created with the permissions specified. Otherwise, they will be created according to the umask rules on Unix-like platforms (the manual says "uname" โ€” that's a typo in the original, not me, don't @ me ๐Ÿ˜ญ). Windows platforms are unaffected.

๐Ÿ‘‘ Signatures ๐Ÿ‘‘

  • install(TARGETS <target>... [...]) โ€” install target Output Artifacts and associated files:
install(TARGETS <target>... [EXPORT <export-name>]
        [RUNTIME_DEPENDENCIES <arg>...|RUNTIME_DEPENDENCY_SET <set-name>]
        [<artifact-option>...]
        [<artifact-kind> <artifact-option>...]...
        [INCLUDES DESTINATION [<dir> ...]]
        )

where the <artifact-option>... group may contain:

[DESTINATION <dir>]
[PERMISSIONS <permission>...]
[CONFIGURATIONS <config>...]
[COMPONENT <component>]
[NAMELINK_COMPONENT <component>]
[OPTIONAL] [EXCLUDE_FROM_ALL]
[NAMELINK_ONLY|NAMELINK_SKIP]

The first <artifact-option>... group applies to target Output Artifacts that do not have a dedicated group specified later in the same call.

Each <artifact-kind> <artifact-option>... group applies to Output Artifacts of the specified artifact kind:

  • ARCHIVE โ€” target artifacts of this kind include:
    • Static libraries (except on macOS when marked as FRAMEWORK, see below);
    • DLL import libraries (on all Windows-based systems including Cygwin; they have extension .lib, in contrast to the .dll libraries that go to RUNTIME);
    • On AIX, the linker import file created for executables with ENABLE_EXPORTS enabled.
    • On macOS, the linker import file created for shared libraries with ENABLE_EXPORTS enabled (except when marked as FRAMEWORK, see below).
  • LIBRARY โ€” target artifacts of this kind include:
    • Shared libraries, except
      • DLLs (these go to RUNTIME, see below),
      • on macOS when marked as FRAMEWORK (see below).
    • Module libraries.
  • RUNTIME โ€” target artifacts of this kind include:
    • Executables (except on macOS when marked as MACOSX_BUNDLE, see BUNDLE below);
    • DLLs (on all Windows-based systems including Cygwin; note that the accompanying import libraries are of kind ARCHIVE).
  • OBJECTS โ€” Added in version 3.9. Object files associated with object libraries.
  • FRAMEWORK โ€” both static and shared libraries marked with the FRAMEWORK property are treated as FRAMEWORK targets on macOS.
  • BUNDLE โ€” executables marked with the MACOSX_BUNDLE property are treated as BUNDLE targets on macOS.
  • PUBLIC_HEADER โ€” any PUBLIC_HEADER files associated with a library are installed in the destination specified by the PUBLIC_HEADER argument on non-Apple platforms. Rules defined by this argument are ignored for FRAMEWORK libraries on Apple platforms because the associated files are installed into the appropriate locations inside the framework folder. See PUBLIC_HEADER for details.
  • PRIVATE_HEADER โ€” similar to PUBLIC_HEADER, but for PRIVATE_HEADER files. See PRIVATE_HEADER for details.
  • RESOURCE โ€” similar to PUBLIC_HEADER and PRIVATE_HEADER, but for RESOURCE files. See RESOURCE for details.
  • FILE_SET <set-name> โ€” Added in version 3.23.

File sets are defined by the target_sources(FILE_SET) command, okay? So if the file set <set-name> exists and it's PUBLIC or INTERFACE, any files in the set of type HEADERS get installed under the destination (see below, bestie, it's literally right there ๐Ÿ‘‡). Other types do not have any default destination, so the DESTINATION option must be specified for each FILE_SET. The directory structure relative to the file set's base directories is preserved โ€” no cap. For example, a file added to the file set as /blah/include/myproj/here.h with a base directory /blah/include would be installed to myproj/here.h below the destination. Obsessed with the path math tbh ๐Ÿ’…

  • CXX_MODULES_BMI โ€” Added in version 3.28. Any module files from C++ modules from PUBLIC sources in a file set of type CXX_MODULES will be installed to the given DESTINATION. All modules are placed directly in the destination as no directory structure is derived from the names of the modules. An empty DESTINATION may be used to suppress installing these files (for use in generic code). ๐Ÿงš

For regular executables, static libraries and shared libraries, the DESTINATION argument is not required. For these target types, when DESTINATION is omitted, a default destination will be taken from the appropriate variable from GNUInstallDirs, or set to a built-in default value if that variable is not defined. The same is true for file sets of type HEADERS, and the public and private headers associated with the installed targets through the PUBLIC_HEADER and PRIVATE_HEADER target properties. A destination must always be provided for module libraries, Apple bundles and frameworks. A destination can be omitted for interface and object libraries, but they are handled differently (see the discussion of this topic toward the end of this section). โœจ

For shared libraries on DLL platforms, if neither RUNTIME nor ARCHIVE destinations are specified, both the RUNTIME and ARCHIVE components are installed to their default destinations. If either a RUNTIME or ARCHIVE destination is specified, the component is installed to that destination, and the other component is not installed. If both RUNTIME and ARCHIVE destinations are specified, then both components are installed to their respective destinations. It's giving "choose your fighter" but make it install components ๐Ÿ’€

Here's the target types with their associated variables and built-in defaults that apply when no destination is given (the original was a whole table, so have it as a lil list instead, same facts, cuter packaging ๐ŸŽ€):

  • RUNTIME โ€” GNUInstallDirs variable: ${CMAKE_INSTALL_BINDIR} โ€” Built-In Default: bin
  • LIBRARY โ€” GNUInstallDirs variable: ${CMAKE_INSTALL_LIBDIR} โ€” Built-In Default: lib
  • ARCHIVE โ€” GNUInstallDirs variable: ${CMAKE_INSTALL_LIBDIR} โ€” Built-In Default: lib
  • PRIVATE_HEADER โ€” GNUInstallDirs variable: ${CMAKE_INSTALL_INCLUDEDIR} โ€” Built-In Default: include
  • PUBLIC_HEADER โ€” GNUInstallDirs variable: ${CMAKE_INSTALL_INCLUDEDIR} โ€” Built-In Default: include
  • FILE_SET (type HEADERS) โ€” GNUInstallDirs variable: ${CMAKE_INSTALL_INCLUDEDIR} โ€” Built-In Default: include

Projects wishing to follow the common practice of installing headers into a project-specific subdirectory may prefer using file sets with appropriate paths and base directories. Otherwise, they must provide a DESTINATION instead of being able to rely on the above (see next example below). ๐Ÿ™ˆ

To make packages compliant with distribution filesystem layout policies, if projects must specify a DESTINATION, it is strongly recommended that they use a path that begins with the appropriate relative GNUInstallDirs variable. This allows package maintainers to control the install destination by setting the appropriate cache variables. The following example shows a static library being installed to the default destination provided by GNUInstallDirs, but with its headers installed to a project-specific subdirectory without using file sets โ€” slay, look at it go:

add_library(mylib STATIC ...)
set_target_properties(mylib PROPERTIES PUBLIC_HEADER mylib.h)
include(GNUInstallDirs)
install(TARGETS mylib
        PUBLIC_HEADER
          DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/myproj
)

In addition to the common options listed above, each target can accept the following additional arguments ๐Ÿ‘‘:

  • NAMELINK_COMPONENT โ€” Added in version 3.12. On some platforms a versioned shared library has a symbolic link such as:
lib<name>.so -> lib<name>.so.1

where lib<name>.so.1 is the soname of the library and lib<name>.so is a "namelink" allowing linkers to find the library when given -l<name>. The NAMELINK_COMPONENT option is similar to the COMPONENT option, but it changes the installation component of a shared library namelink if one is generated. If not specified, this defaults to the value of COMPONENT. It is an error to use this parameter outside of a LIBRARY block. ๐Ÿ˜ญ

Changed in version 3.27: This parameter is also usable for an ARCHIVE block to manage the linker import file created, on macOS, for shared libraries with ENABLE_EXPORTS enabled.

See the Example: Install Targets with Per-Artifact Components for an example using NAMELINK_COMPONENT.

This option is typically used for package managers that have separate runtime and development packages. For example, on Debian systems, the library is expected to be in the runtime package, and the headers and namelink are expected to be in the development package.

See the VERSION and SOVERSION target properties for details on creating versioned shared libraries.

  • NAMELINK_ONLY โ€” This option causes the installation of only the namelink when a library target is installed. On platforms where versioned shared libraries do not have namelinks or when a library is not versioned, the NAMELINK_ONLY option installs nothing. It is an error to use this parameter outside of a LIBRARY block. ๐Ÿซถ

Changed in version 3.27: This parameter is also usable for an ARCHIVE block to manage the linker import file created, on macOS, for shared libraries with ENABLE_EXPORTS enabled.

When NAMELINK_ONLY is given, either NAMELINK_COMPONENT or COMPONENT may be used to specify the installation component of the namelink, but COMPONENT should generally be preferred.

  • NAMELINK_SKIP โ€” Similar to NAMELINK_ONLY, but it has the opposite effect: it causes the installation of library files other than the namelink when a library target is installed. When neither NAMELINK_ONLY or NAMELINK_SKIP are given, both portions are installed. On platforms where versioned shared libraries do not have symlinks or when a library is not versioned, NAMELINK_SKIP installs the library. It is an error to use this parameter outside of a LIBRARY block. ๐Ÿ’€

Changed in version 3.27: This parameter is also usable for an ARCHIVE block to manage the linker import file created, on macOS, for shared libraries with ENABLE_EXPORTS enabled.

If NAMELINK_SKIP is specified, NAMELINK_COMPONENT has no effect. It is not recommended to use NAMELINK_SKIP in conjunction with NAMELINK_COMPONENT. ๐Ÿ™…

The install(TARGETS) command can also accept the following options at the top level ๐ŸŒˆ:

  • EXPORT โ€” This option associates the installed target files with an export called <export-name>. It must appear before any target options. To actually install the export file itself, call install(EXPORT), documented below. See documentation of the EXPORT_NAME target property to change the name of the exported target. โญ
  • INCLUDES DESTINATION โ€” drop a list of directories here and they get added to the INTERFACE_INCLUDE_DIRECTORIES target property of the <targets> when they're exported by the install(EXPORT) command. If you hand it a relative path it's treated as relative to $<INSTALL_PREFIX>. And lowkey this one's special: unlike the other DESTINATION arguments on the various install() subcommands, paths after INCLUDES DESTINATION are used as given โ€” not normalized, not even assumed to be normalized, though ngl it's recommended you give them in normalized form (peep Normalization).
  • RUNTIME_DEPENDENCY_SET <set-name> โ€” โœจAdded in version 3.21.โœจ This makes all the runtime dependencies of your installed executable, shared library, and module targets get dumped into the runtime dependency set you name. Then you can install that whole set with an install(RUNTIME_DEPENDENCY_SET) command. Heads up tho: this keyword and RUNTIME_DEPENDENCIES are mutually exclusive โ€” pick one, bestie.
  • RUNTIME_DEPENDENCIES <arg>... โ€” โœจAdded in version 3.21.โœจ This one takes all the runtime dependencies of your installed executable, shared library, and module targets and installs them right alongside the targets themselves. The RUNTIME, LIBRARY, FRAMEWORK, and generic arguments are what decide the properties (DESTINATION, COMPONENT, etc.) of how those deps get installed.

It's semantically equivalent to these two calls, fr fr:

install(TARGETS ... RUNTIME_DEPENDENCY_SET <set-name>)
install(RUNTIME_DEPENDENCY_SET <set-name> <arg>...)

where <set-name> is a randomly generated set name. Your <arg>... can be any of these keywords that install(RUNTIME_DEPENDENCY_SET) supports:

  • DIRECTORIES
  • PRE_INCLUDE_REGEXES
  • PRE_EXCLUDE_REGEXES
  • POST_INCLUDE_REGEXES
  • POST_EXCLUDE_REGEXES
  • POST_INCLUDE_FILES
  • POST_EXCLUDE_FILES

And again: RUNTIME_DEPENDENCIES and RUNTIME_DEPENDENCY_SET are mutually exclusive. No mixing ๐Ÿ’….


Interface Libraries can totally be listed among the targets you install. They install literally no artifacts, but they will be included in an associated EXPORT. And if Object Libraries get listed but you give them no destination for their object files, they get exported as Interface Libraries instead โ€” which is exactly enough to satisfy the transitive usage requirements of other targets that link to those object libraries in their implementation. It's giving "play your position."

Installing a target with the EXCLUDE_FROM_ALL target property set to TRUE has undefined behavior. Not a vibe. Don't do it.

โœจAdded in version 3.3:โœจ An install destination passed as a DESTINATION argument can use "generator expressions" with the syntax $<...>. Go check the cmake-generator-expressions(7) manual for which expressions you've got. ๐Ÿฆ‹

โœจAdded in version 3.13:โœจ install(TARGETS) can install targets that were created in other directories. But when you're using cross-directory install rules like that, running make install (or similar) from a subdirectory will not guarantee that targets from other directories are up-to-date. Use target_link_libraries() or add_dependencies() to make sure those out-of-directory targets get built before the subdirectory-specific install rules run. Main-character targets need a schedule, okay?

๐ŸŒˆ install(IMPORTED_RUNTIME_ARTIFACTS <target>... [...]) ๐ŸŒˆ

โœจAdded in version 3.21.โœจ Install the runtime artifacts of imported targets, babes:

install(IMPORTED_RUNTIME_ARTIFACTS <target>...
        [RUNTIME_DEPENDENCY_SET <set-name>]
        [[LIBRARY|RUNTIME|FRAMEWORK|BUNDLE]
         [DESTINATION <dir>]
         [PERMISSIONS <permission>...]
         [CONFIGURATIONS <config>...]
         [COMPONENT <component>]
         [OPTIONAL] [EXCLUDE_FROM_ALL]
        ]...
        )

The IMPORTED_RUNTIME_ARTIFACTS form sets up rules for installing the runtime artifacts of imported targets. Projects do this when they wanna bundle outside executables or modules inside their own installation. The LIBRARY, RUNTIME, FRAMEWORK, and BUNDLE arguments mean the exact same thing they mean in TARGETS mode.

Important detail: only the runtime artifacts of imported targets get installed โ€” except in the case of FRAMEWORK libraries, MACOSX_BUNDLE executables, and BUNDLE CFBundles. For example, headers and import libraries associated with DLLs are not installed. But for FRAMEWORK libraries, MACOSX_BUNDLE executables, and BUNDLE CFBundles, the entire directory gets installed. ๐ŸŽ€

The RUNTIME_DEPENDENCY_SET option causes the runtime artifacts of the imported executable, shared library, and module library targets to be added to the <set-name> runtime dependency set. Then you can install that set with an install(RUNTIME_DEPENDENCY_SET) command. ๐Ÿ’–

๐ŸŒธ install(FILES <file>... [...]) ๐ŸŒธ

๐ŸŒธ install(PROGRAMS <program>... [...]) ๐ŸŒธ

Note: if you're installing header files, consider using file sets defined by target_sources(FILE_SET) instead โ€” file sets associate headers with a target and they install as part of the target. Cleaner. Iconic. Do that.

Install files or programs:

install(<FILES|PROGRAMS> <file>...
        TYPE <type> | DESTINATION <dir>
        [PERMISSIONS <permission>...]
        [CONFIGURATIONS <config>...]
        [COMPONENT <component>]
        [RENAME <name>] [OPTIONAL] [EXCLUDE_FROM_ALL])

The FILES form sets up rules for installing files for a project. File names given as relative paths are interpreted with respect to the current source directory. Files installed this way get permissions OWNER_WRITE, OWNER_READ, GROUP_READ, and WORLD_READ by default, if you don't pass a PERMISSIONS argument. ๐Ÿ’…

The PROGRAMS form is identical to the FILES form, except the default permissions for the installed file also include OWNER_EXECUTE, GROUP_EXECUTE, and WORLD_EXECUTE. This form is meant for installing programs that aren't targets โ€” think shell scripts. Use the TARGETS form to install targets that are built within the project. โœจ

The list of files you give to FILES or PROGRAMS may use "generator expressions" with the syntax $<...>. See the cmake-generator-expressions(7) manual for what's available. But bestie, watch out: if any item begins in a generator expression, it must evaluate to a full path. No half-paths, no games. ๐Ÿ˜ญ

The optional RENAME <name> argument is how you give the installed file a name that's different from its original file name. Gatekeep, girlboss: renaming is only allowed when a single file is installed by the command. ๐Ÿซถ

okay so literally: either a TYPE or a DESTINATION has to be provided โ€” but not both, bestie. ๐Ÿ’… A TYPE argument tells CMake the generic file type of the files being installed. Then a destination gets set automatically by taking the corresponding variable from the GNUInstallDirs module, or by using a built-in default if that variable is not defined. The little matchup list below has the supported file types plus their corresponding variables and built-in defaults. Projects can also hand over a DESTINATION argument instead of a file type if they want to explicitly define the install destination. โœจ

Hereโ€™s the type tea โ˜•:

  • BIN โ€” GNUInstallDirs variable ${CMAKE_INSTALL_BINDIR}; built-in default bin
  • SBIN โ€” GNUInstallDirs variable ${CMAKE_INSTALL_SBINDIR}; built-in default sbin
  • LIB โ€” GNUInstallDirs variable ${CMAKE_INSTALL_LIBDIR}; built-in default lib
  • INCLUDE โ€” GNUInstallDirs variable ${CMAKE_INSTALL_INCLUDEDIR}; built-in default include
  • SYSCONF โ€” GNUInstallDirs variable ${CMAKE_INSTALL_SYSCONFDIR}; built-in default etc
  • SHAREDSTATE โ€” GNUInstallDirs variable ${CMAKE_INSTALL_SHARESTATEDIR}; built-in default com
  • LOCALSTATE โ€” GNUInstallDirs variable ${CMAKE_INSTALL_LOCALSTATEDIR}; built-in default var
  • RUNSTATE โ€” GNUInstallDirs variable ${CMAKE_INSTALL_RUNSTATEDIR}; built-in default <LOCALSTATE dir>/run
  • DATA โ€” GNUInstallDirs variable ${CMAKE_INSTALL_DATADIR}; built-in default <DATAROOT dir>
  • INFO โ€” GNUInstallDirs variable ${CMAKE_INSTALL_INFODIR}; built-in default <DATAROOT dir>/info
  • LOCALE โ€” GNUInstallDirs variable ${CMAKE_INSTALL_LOCALEDIR}; built-in default <DATAROOT dir>/locale
  • MAN โ€” GNUInstallDirs variable ${CMAKE_INSTALL_MANDIR}; built-in default <DATAROOT dir>/man
  • DOC โ€” GNUInstallDirs variable ${CMAKE_INSTALL_DOCDIR}; built-in default <DATAROOT dir>/doc
  • LIBEXEC โ€” GNUInstallDirs variable ${CMAKE_INSTALL_LIBEXECDIR}; built-in default libexec

Projects wishing to follow the common practice of installing headers into a project-specific subdirectory will need to provide a destination rather than rely on the above. Using file sets for headers instead of install(FILES) would be even better (see target_sources(FILE_SET)). ๐Ÿซถ

Note that some of the typesโ€™ built-in defaults use the DATAROOT directory as a prefix. The DATAROOT prefix is calculated similarly to the types, with CMAKE_INSTALL_DATAROOTDIR as the variable and share as the built-in default. You cannot use DATAROOT as a TYPE parameter; please use DATA instead. ๐Ÿšซ

To make packages compliant with distribution filesystem layout policies, if projects must specify a DESTINATION, it is strongly recommended that they use a path that begins with the appropriate relative GNUInstallDirs variable. This allows package maintainers to control the install destination by setting the appropriate cache variables. The following example shows how to follow this advice while installing an image to a project-specific documentation subdirectory:

include(GNUInstallDirs)
install(FILES logo.png
        DESTINATION ${CMAKE_INSTALL_DOCDIR}/myproj
)

Added in version 3.4: An install destination given as a DESTINATION argument may use "generator expressions" with the syntax $<...>. See the cmake-generator-expressions(7) manual for available expressions. โœจ

Added in version 3.20: An install rename given as a RENAME argument may use "generator expressions" with the syntax $<...>. See the cmake-generator-expressions(7) manual for available expressions. ๐ŸŽ€

Added in version 3.31: The TYPE argument now supports type LIBEXEC. ๐Ÿ”ฅ


๐Ÿงš install(DIRECTORY <dir>... [...]) ๐Ÿงš

Note: To install a directory sub-tree of headers, consider using file sets defined by target_sources(FILE_SET) instead. File sets not only preserve directory structure, they also associate headers with a target and install as part of the target. ๐Ÿซถ

Install the contents of one or more directories:

install(DIRECTORY <dir>...
        TYPE <type> | DESTINATION <dir>
        [FILE_PERMISSIONS <permission>...]
        [DIRECTORY_PERMISSIONS <permission>...]
        [USE_SOURCE_PERMISSIONS] [OPTIONAL] [MESSAGE_NEVER]
        [CONFIGURATIONS <config>...]
        [COMPONENT <component>] [EXCLUDE_FROM_ALL]
        [FILES_MATCHING]
        [<match-rule> <match-option>...]...
        )

The DIRECTORY form installs contents of one or more directories to a given destination. The directory structure is copied verbatim to the destination. ๐Ÿฆ‹

Either a TYPE or a DESTINATION must be provided, but not both. If no permissions is given, files will be given the default permissions specified in the FILES form of the command, and the directories will be given the default permissions specified in the PROGRAMS form of the command. ๐Ÿ’…

The options are:

  • <dir>... โ€” the list of directories to be installed.
    • The last component of each directory name is appended to the destination directory, but a trailing slash may be used to avoid this because it leaves the last component empty.
    • Directory names given as relative paths are interpreted with respect to the current source directory.
    • If no input directory names are given, the destination directory will be created but nothing will be installed into it.
    • Added in version 3.5: The source <dir>... list may use "generator expressions" with the syntax $<...>. See the cmake-generator-expressions(7) manual for available expressions. ๐ŸŒˆ

Okay so picking right back up with TYPE <type> โ€” this is the glow-up where you tell CMake the generic file type of everything inside those directories you're installing. It then auto-sets a destination for you by grabbing the matching variable from GNUInstallDirs, or by falling back to a built-in default if that variable isn't defined (iconic of it, honestly). Peep the table below for the supported file types and their matching variables and built-in defaults. And if a project wants to be extra and spell it out explicitly, they can just hand over a DESTINATION argument instead of a file type. ๐Ÿ’…

  • BIN โ€” variable ${CMAKE_INSTALL_BINDIR}, built-in default bin
  • SBIN โ€” variable ${CMAKE_INSTALL_SBINDIR}, built-in default sbin
  • LIB โ€” variable ${CMAKE_INSTALL_LIBDIR}, built-in default lib
  • INCLUDE โ€” variable ${CMAKE_INSTALL_INCLUDEDIR}, built-in default include
  • SYSCONF โ€” variable ${CMAKE_INSTALL_SYSCONFDIR}, built-in default etc
  • SHAREDSTATE โ€” variable ${CMAKE_INSTALL_SHARESTATEDIR}, built-in default com
  • LOCALSTATE โ€” variable ${CMAKE_INSTALL_LOCALSTATEDIR}, built-in default var
  • RUNSTATE โ€” variable ${CMAKE_INSTALL_RUNSTATEDIR}, built-in default <LOCALSTATE dir>/run
  • DATA โ€” variable ${CMAKE_INSTALL_DATADIR}, built-in default <DATAROOT dir>
  • INFO โ€” variable ${CMAKE_INSTALL_INFODIR}, built-in default <DATAROOT dir>/info
  • LOCALE โ€” variable ${CMAKE_INSTALL_LOCALEDIR}, built-in default <DATAROOT dir>/locale
  • MAN โ€” variable ${CMAKE_INSTALL_MANDIR}, built-in default <DATAROOT dir>/man
  • DOC โ€” variable ${CMAKE_INSTALL_DOCDIR}, built-in default <DATAROOT dir>/doc
  • LIBEXEC โ€” variable ${CMAKE_INSTALL_LIBEXECDIR}, built-in default libexec

Heads up bestie: some of these types' built-in defaults use the DATAROOT directory as a prefix. The DATAROOT prefix gets calculated the same way as the types themselves โ€” variable CMAKE_INSTALL_DATAROOTDIR, built-in default share. And no, you literally cannot use DATAROOT as a TYPE parameter ๐Ÿซถ โ€” please use DATA instead.

Added in version 3.31: the LIBEXEC type. New drop, go use it. โœจ

๐Ÿ’– DESTINATION <dir>

The destination directory, exactly as documented among the common options.

And listen โ€” to make packages compliant with distribution filesystem layout policies, if a project must specify a DESTINATION, it is strongly recommended that they use a path that begins with the appropriate relative GNUInstallDirs variable. This is the whole point: it lets package maintainers control the install destination by setting the appropriate cache variables. Respect the ecosystem, periodt. ๐Ÿ‘‘

Added in version 3.4: the destination <dir> may use "generator expressions" with the syntax $<...>. See the cmake-generator-expressions(7) manual for the available expressions. The lore is deep, iykyk.

๐ŸŽ€ FILE_PERMISSIONS <permission>...

Specify the permissions given to files in the destination, where the <permission> names are documented among the common options.

๐ŸŽ€ DIRECTORY_PERMISSIONS <permission>...

Same energy, but for directories in the destination โ€” the <permission> names are documented among the common options.

๐ŸŒธ USE_SOURCE_PERMISSIONS

If specified, and FILE_PERMISSIONS is not, then file permissions will be copied straight from the source directory structure. Inherited ~ from your mother ~ ๐Ÿงš

๐ŸŒธ MESSAGE_NEVER

Added in version 3.1.

Disable file installation status output. That's it. Silence. Peace and quiet. ๐Ÿ™ˆ

๐Ÿฆ‹ FILES_MATCHING

You can drop this option before the first <match-rule> to disable installation of files (but not directories) that aren't matched by any expression. Like, filter the guest list but let all the venues in.

For example, this code:

install(DIRECTORY src/ DESTINATION doc/myproj
        FILES_MATCHING PATTERN "*.png")

will extract and install images from a source tree. Slay. ๐Ÿ–ผ๏ธ

๐ŸŒˆ <match-rule> <match-option>...

Installation of directories may be controlled with fine granularity using rules that match directories or files encountered within input directories. They may be used to apply certain options (see below) to a subset of the files and directories encountered. And like โ€” all files and directories are installed whether or not they are matched, unless you gave that FILES_MATCHING option above. No sneaky deletions.

The full path to each input file or directory, with forward slashes, may be matched by a <match-rule>:

โญ PATTERN <pattern>

Match complete file names using a globbing pattern. The portion of the full path matching the pattern must occur at the end of the file name and be preceded by a slash (which is not part of the pattern). Details matter, bestie.

โญ REGEX <regex>

Match any portion of the full path of a file with a regular expression. You may use / and $ to limit matching to the end of a path. Regex girlies rise. ๐Ÿ”ฅ


Each <match-rule> may be followed by <match-option> arguments. The match options apply to the files or directories matched by the rule. The match options are:

  • EXCLUDE โ€” exclude the matched file or directory from installation. Begone. ๐Ÿ’€
  • PERMISSIONS <permission>... โ€” override the permissions setting for the matched file or directory.

For example, this code:

install(DIRECTORY icons scripts/ DESTINATION share/myproj
        PATTERN "CVS" EXCLUDE
        PATTERN "scripts/*"
        PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
                    GROUP_EXECUTE GROUP_READ)

will install the icons directory to share/myproj/icons and the scripts directory to share/myproj. The icons will get default file permissions, the scripts will be given specific permissions, and any CVS directories will be excluded. Three different treatments in one call, the multitasking icon she is. ๐Ÿ‘‘

๐Ÿ’œ install(SCRIPT <file> [...])

๐Ÿ’œ install(CODE <code> [...])

Invoke CMake scripts or code during installation:

install([[SCRIPT <file>] [CODE <code>]]
        [ALL_COMPONENTS | COMPONENT <component>]
        [EXCLUDE_FROM_ALL])

That's the whole vibe โ€” you're literally running a script or a chunk of code at install time. Use it wisely, and maybe don't put your whole personality in there. ๐Ÿ˜ญ

Okay so real talk for a sec โ€” the SCRIPT form literally invokes the CMake script files you gave it, during installation. And if the script file name is a relative path, it gets interpreted with respect to the current source directory, iykyk. The CODE form invokes the CMake code you gave it, during installation too. That code has to be a single argument inside a double-quoted string, bestie. Like, for example:

install(CODE "message(\"Sample install message.\")")

which will print a message during installation. Cute, right? โœจ

Added in version 3.21: when you pass the ALL_COMPONENTS option, the custom installation script code will be executed for every component of a component-specific installation. And heads up โ€” this option is mutually exclusive with the COMPONENT option. You can't have both, no cap. ๐Ÿ™…โ€โ™€๏ธ

Added in version 3.14: <file> or <code> may use "generator expressions" with the syntax $<...> (and in the case of <file>, this refers to their use in the file name, not the file's contents โ€” important distinction, don't get it twisted ๐Ÿ’…). Go peek at the cmake-generator-expressions(7) manual for all the available expressions.


๐ŸŽ€ install(EXPORT <export-name> [...])

Install a CMake file exporting targets for dependent projects โ€” it's giving sharing is caring:

install(EXPORT <export-name> DESTINATION <dir>
        [NAMESPACE <namespace>] [FILE <name>.cmake]
        [PERMISSIONS <permission>...]
        [CONFIGURATIONS <config>...]
        [CXX_MODULES_DIRECTORY <directory>]
        [EXPORT_LINK_INTERFACE_LIBRARIES]
        [COMPONENT <component>]
        [EXCLUDE_FROM_ALL]
        [EXPORT_PACKAGE_DEPENDENCIES])
install(EXPORT_ANDROID_MK <export-name> DESTINATION <dir> [...])

So the EXPORT form generates and installs a CMake file that contains code to import targets from the installation tree into another project. Target installations get associated with the export <export-name> using the EXPORT option of the install(TARGETS) signature documented above. The NAMESPACE option will prepend <namespace> to the target names as they get written to the import file. By default the generated file is called <export-name>.cmake, but the FILE option lets you specify a different name โ€” and the value you give to FILE must be a file name with the .cmake extension. Non-negotiable. ๐Ÿซถ

If a CONFIGURATIONS option is given, then the file will only be installed when one of the named configurations is installed. Also, the generated import file will reference only the matching target configurations. Check out the CMAKE_MAP_IMPORTED_CONFIG_<CONFIG> variable to map configurations of dependent projects to the installed configurations. The EXPORT_LINK_INTERFACE_LIBRARIES keyword, if present, causes the contents of the properties matching (IMPORTED_)?LINK_INTERFACE_LIBRARIES(_<CONFIG>)? to be exported, when policy CMP0022 is NEW.

Note: the installed <export-name>.cmake file may come with additional per-configuration <export-name>-*.cmake files to be loaded by globbing. So do not use an export name that's the same as the package name in combination with installing a <package-name>-config.cmake file โ€” otherwise the latter may be incorrectly matched by the glob and loaded. That's a real "oops" moment, and not the fun kind. ๐Ÿ˜ญ

When a COMPONENT option is given, the listed <component> implicitly depends on all components mentioned in the export set. The exported <name>.cmake file will require each of the exported components to be present in order for dependent projects to build properly. Example time, because this one's a whole vibe: ๐Ÿฆ‹

  • A project may define components Runtime and Development, with shared libraries going into the Runtime component and static libraries and headers going into the Development component.
  • The export set would also typically be part of the Development component, but it would export targets from both the Runtime and Development components.
  • Therefore, the Runtime component would need to be installed if the Development component was installed, but not vice versa.
  • If the Development component was installed without the Runtime component, dependent projects that try to link against it would get build errors. ๐Ÿ’€
  • Package managers, such as APT and RPM, typically handle this by listing the Runtime component as a dependency of the Development component in the package metadata, ensuring that the library is always installed if the headers and CMake export file are present. Slay, honestly. ๐Ÿ‘‘

Added in version 3.7: in addition to cmake language files, the EXPORT_ANDROID_MK mode may be used to specify an export to the android ndk build system. This mode accepts the same options as the normal export mode. The Android NDK supports the use of prebuilt libraries, both static and shared. This allows cmake to build the libraries of a project and make them available to an ndk build system complete with transitive dependencies, include flags and defines required to use the libraries. Very much the whole package. ๐Ÿ“ฆ

CXX_MODULES_DIRECTORY

Added in version 3.28.

Specify a subdirectory to store C++ module information for targets in the export set. This directory will be populated with files which add the necessary target property information to the relevant targets. Note that without this information, none of the C++ modules which are part of the targets in the export set will support being imported in consuming targets. So likeโ€ฆ if you skip it, that's on you bestie. ๐Ÿ™ˆ

EXPORT_PACKAGE_DEPENDENCIES

Note: Experimental. Gated by CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_DEPENDENCIES. (Yes, it's behind a gate โ€” behave accordingly. ๐Ÿงš)

Specify that find_dependency() calls should be exported. If this argument is specified, CMake examines all targets in the export set and gathers their INTERFACE link targets. If any such targets either were found with find_package() or have the EXPORT_FIND_PACKAGE_NAME property set, and such package dependency was not disabled by passing ENABLED OFF to export(SETUP), then a find_dependency() call is written with the target's corresponding package name, a REQUIRED argument, and any additional arguments specified by the EXTRA_ARGS argument of export(SETUP). And any package dependencies that were manually specified by passing ENABLED ON to export(SETUP) are also added, even if the exported targets don't depend on any targets from them. Generous behaviour, honestly. ๐Ÿ’–

The find_dependency() calls are written in the following order:

  1. Any package dependencies that were listed in export(SETUP) are written in the order they were first specified, regardless of whether or not they contain INTERFACE dependencies of the exported targets.
  2. Any package dependencies that contain INTERFACE link dependencies of the exported targets and that were never specified in export(SETUP) are written in the order they were first found.

The EXPORT form is useful to help outside projects use targets built and installed by the current project. For example, this code:

install(TARGETS myexe EXPORT myproj DESTINATION bin)
install(EXPORT myproj NAMESPACE mp_ DESTINATION lib/myproj)
install(EXPORT_ANDROID_MK myproj DESTINATION share/ndk-modules)

will install the executable myexe to <prefix>/bin and code to import it in the file <prefix>/lib/myproj/myproj.cmake and <prefix>/share/ndk-modules/Android.mk. An outside project may load this file with the include command and reference the myexe executable from the installation tree using the imported target name mp_myexe as if the target were built in its own tree. Literally main character energy for an executable. ๐Ÿ”ฅ


๐Ÿ’œ install(PACKAGE_INFO <package-name> [...])

Added in version 4.3.

Installs a Common Package Specification ("CPS") file exporting targets for dependent projects โ€” it's the https://cps-org.github.io/cps/ spec, if you want the lore:

install(PACKAGE_INFO <package-name> EXPORT <export-name>
        [PROJECT <project-name>|NO_PROJECT_METADATA]
        [APPENDIX <appendix-name>]
        [DESTINATION <dir>]
        [LOWER_CASE_FILE]
        [VERSION <version>
         [COMPAT_VERSION <version>]
         [VERSION_SCHEMA <string>]]
        [DEFAULT_TARGETS <target>...]
        [DEFAULT_CONFIGURATIONS <config>...]
        [LICENSE <license-string>]
        [DEFAULT_LICENSE <license-string>]
        [DESCRIPTION <description-string>]
        [HOMEPAGE_URL <url-string>]
        [PERMISSIONS <permission>...]
        [CONFIGURATIONS <config>...]
        [CXX_MODULES_DIRECTORY <directory>]
        [COMPONENT <component>]
        [EXCLUDE_FROM_ALL])

The PACKAGE_INFO form generates and installs a Common Package Specification file which describes installed targets such that they can be consumed by another project. Target installations are associated with the export <export-name> using the EXPORT option of the install(TARGETS) signature documented above. Unlike install(EXPORT), this information is not expressed in CMake code, so it can be consumed by tools other than CMake. ๐ŸŒˆ When imported into another CMake project, the imported targets will be prefixed with <package-name>::. By default, the generated file will be called <package-name>[-<appendix-name>].cps. If LOWER_CASE_FILE is given, the package name as it appears on disk (in both the file name and install destination) will be first converted to lower case. lowercase era fr fr. ๐Ÿ“

If DESTINATION is not specified, a platform-specific default is used.

Several options may be used to specify package metadata:

  • VERSION <version> โ€” the version of the package ๐Ÿ’… and yes, the <version> has to conform to the specified schema, no freestyling. Wanna know how that version actually gets used when a consumer asks for the package? Go read Version Selection (CPS), it's all laid out there.
  • COMPAT_VERSION <version> โ€” the oldest version for which the package still provides compatibility. If you don't give it, COMPAT_VERSION is implicitly taken to equal the package's VERSION, which is basically saying "no backwards compatibility, we're thriving in the present" ๐Ÿ˜Œ.
  • VERSION_SCHEMA <schema> โ€” the schema that the package's version number(s) follow (both VERSION and COMPAT_VERSION, bestie, both of them). Here's the tea: if you don't provide this option, no schema gets written to the .cps file at all, but CPS says the schema is assumed to be simple in those cases. Peep version_schema at https://cps-org.github.io/cps/schema.html#version-schema for the deets and the list of officially supported schemas โ€” but โš ๏ธ be aware the spec may include schemas that CMake does not support. For the list that find_package() actually supports, see Version Selection (CPS).
  • DEFAULT_TARGETS <target>... โ€” targets used when a consumer requests linking to the package name instead of to specific components. Like a little default friend group ๐Ÿซถ.
  • DEFAULT_CONFIGURATIONS <config>... โ€” an ordered list of configurations consumers should prefer when there's no exact match, and no mapping of the consumer's configuration onto the package's available configurations. If you skip this, CMake just falls back to the package's available configurations in an unspecified order (so, chaos, but legal chaos).
  • LICENSE <license-string> โ€” Added in version 4.2. A System Package Data Exchange (SPDX) License Expression that describes the license(s) of the project as a whole โ€” and that includes documentation, resources, or other materials distributed with the project, in addition to software artifacts. Need identifiers? The SPDX License List at https://spdx.org/licenses/ has the commonly used licenses and their identifiers. The license of individual components comes from the SPDX_LICENSE property of their respective targets ๐Ÿ’–.
  • DEFAULT_LICENSE <license-string> โ€” Added in version 4.2. Also a System Package Data Exchange (SPDX) License Expression, except this one describes the license(s) of any components that do not otherwise specify their license(s). The fallback bestie ๐ŸŽ€.
  • DESCRIPTION <description-string> โ€” Added in version 4.1. An informational description of the project. Recommendation: keep it relatively short, usually no more than a few words. We are not writing a memoir here โœจ.
  • HOMEPAGE_URL <url-string> โ€” Added in version 4.1. An informational canonical home URL for the project. The main character address ๐Ÿ‘‘.

So here's how the metadata inheritance thing works, and it's lowkey iconic: by default, if the <package-name> you specified matches the current CMake PROJECT_NAME, package metadata gets inherited from the project. Use the PROJECT <project-name> option if you want to inherit metadata from a different project. And if NO_PROJECT_METADATA is specified, that automatic inheritance is disabled entirely, periodt. Either way โ€” any metadata values you specify right there in the install command take precedence. Always ๐Ÿ’….

Now, APPENDIX โ€” this one is a vibe. If APPENDIX is specified, instead of generating a top level package specification, the specified targets get exported as an appendix to the named package. Appendices let you separate less commonly used targets (along with their external dependencies) from the rest of a package. Why do we care? Because it lets consumers ignore transitive dependencies for targets they don't even use, and it also lets a single logical "package" be composed of artifacts produced by multiple build trees. Composed, like a fit ๐Ÿ”ฅ.

But appendices are not permitted to change basic package metadata. So none of PROJECT, VERSION, COMPAT_VERSION, VERSION_SCHEMA, DEFAULT_TARGETS or DEFAULT_CONFIGURATIONS may be specified in combination with APPENDIX. Don't even try it. Also, it's strongly recommended that your use of LOWER_CASE_FILE be consistent between the main package and any appendices ๐Ÿซถ.

Note ๐Ÿฆ‹ โ€” Because it's intended to be portable across multiple build tools, CPS may not support all features that are allowed in CMake-script exports. Specifically? Support for generator expressions in interface properties is limited at this time to configuration-dependent expressions. That's the limit, bestie, respect it.
Note ๐ŸŒธ โ€” This is the recommended way to generate Common Package Specification package information for a project. But for distributors whose users may require CPS package information when making changes to the project's build files isn't practical, there's the CMAKE_INSTALL_EXPORTS_AS_PACKAGE_INFO variable, which may be used to generate .cps files from install(EXPORT) calls. Usage and caveats live in that variable's documentation โ€” go read it, iykyk.

๐ŸŒˆ install(RUNTIME_DEPENDENCY_SET <set-name> [...])

Added in version 3.21. Okay so this one installs a runtime dependency set ๐Ÿ“ฆ:

install(RUNTIME_DEPENDENCY_SET <set-name>
        [[LIBRARY|RUNTIME|FRAMEWORK]
         [DESTINATION <dir>]
         [PERMISSIONS <permission>...]
         [CONFIGURATIONS <config>...]
         [COMPONENT <component>]
         [NAMELINK_COMPONENT <component>]
         [OPTIONAL] [EXCLUDE_FROM_ALL]
        ]...
        [PRE_INCLUDE_REGEXES <regex>...]
        [PRE_EXCLUDE_REGEXES <regex>...]
        [POST_INCLUDE_REGEXES <regex>...]
        [POST_EXCLUDE_REGEXES <regex>...]
        [POST_INCLUDE_FILES <file>...]
        [POST_EXCLUDE_FILES <file>...]
        [DIRECTORIES <dir>...]
        )

What it does: installs a runtime dependency set that was previously created by one or more install(TARGETS) or install(IMPORTED_RUNTIME_ARTIFACTS) commands. The dependencies of targets belonging to a runtime dependency set go into the RUNTIME destination and component on DLL platforms, and into the LIBRARY destination and component on non-DLL platforms. macOS frameworks? Those land in the FRAMEWORK destination and component ๐Ÿ“.

Big important thing: targets built within the build tree will never be installed as runtime dependencies, nor will their own dependencies โ€” unless the targets themselves are installed with install(TARGETS). Like, that's the rule ๐Ÿซถ.

The generated install script calls file(GET_RUNTIME_DEPENDENCIES) on the build-tree files to calculate the runtime dependencies. The build-tree executable files get passed as the EXECUTABLES argument, the build-tree shared libraries as the LIBRARIES argument, and the build-tree modules as the MODULES argument. On macOS, if one of the executables is a MACOSX_BUNDLE, that executable is passed as the BUNDLE_EXECUTABLE argument โ€” and heads up, at most one such bundle executable may be in the runtime dependency set on macOS. The MACOSX_BUNDLE property has no effect on other platforms. And ngl, file(GET_RUNTIME_DEPENDENCIES) only supports collecting runtime dependencies for Windows, Linux and macOS platforms, so install(RUNTIME_DEPENDENCY_SET) has that exact same limitation. No cap ๐Ÿ’€.

The following sub-arguments get forwarded through as the corresponding arguments to file(GET_RUNTIME_DEPENDENCIES) โ€” for those that provide a non-empty list of directories, regular expressions or files, that is. They all support generator expressions:

  • DIRECTORIES <dir>...
  • PRE_INCLUDE_REGEXES <regex>...
  • PRE_EXCLUDE_REGEXES <regex>...
  • POST_INCLUDE_REGEXES <regex>...
  • POST_EXCLUDE_REGEXES <regex>...
  • POST_INCLUDE_FILES <file>...
  • POST_EXCLUDE_FILES <file>...
Note ๐Ÿ’€ โ€” This command supersedes the install_targets() command and the PRE_INSTALL_SCRIPT and POST_INSTALL_SCRIPT target properties. It also replaces the FILES forms of the install_files() and install_programs() commands. And the processing order of these install rules relative to the ones generated by install_targets(), install_files(), and install_programs()? Not defined. So don't be out here relying on an order that doesn't exist ๐Ÿ™ˆ.

๐ŸŒŸ install(SBOM <sbom-name> [...])

Added in version 4.3.

Note ๐Ÿงš โ€” Experimental. Gated by CMAKE_EXPERIMENTAL_GENERATE_SBOM. Do not come for me if the experimental gate bites, that's just how it is.

Installs a Software Bill of Material, or "SBOM", which describes the project ๐ŸŽ€:

install(SBOM <sbom-name> EXPORTS <export-names>...
        [PROJECT <project-name>|NO_PROJECT_METADATA]
        [DESTINATION <dir>]
        [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
        [LICENSE <license-string>]
        [DESCRIPTION <description-string>]
        [HOMEPAGE_URL <url-string>]
        [PACKAGE_URL <url-string>]
        [FORMAT <string>])

okay so the SBOM form? it literally generates a Software Bill of Material โ€” yes that's "Bill of Material," singular, we don't make the rules โ€” for a given project and installs it as part of the project installation. think of it as a machine-readable receipt: it describes the project's targets, the libraries they link, and all the related metadata like versions and license info. CMake currently generates SBOM files using the System Package Data Exchange <https://spdx.dev/> 3.0 specification in its JSON-LD representation, selected by the FORMAT option, but the interface is designed so that future CMake releases can support additional SBOM formats or schema versions. forward compatible, we love to see it ๐Ÿ’…

Target installations get associated with each export <export-names> in the EXPORTS list using the EXPORTS option of the install(TARGETS) signature documented above. a single SBOM can cover multiple export sets โ€” targets from all listed exports get aggregated into one document, no gatekeeping. If DESTINATION isn't specified, a platform-specific default is used.

Several options may be used to specify package metadata:

  • VERSION <version> โ€” the package version, given as a series of non-negative integer components, i.e. <major>[.<minor>[.<patch>[.<tweak>]]]. See project(VERSION) <#command:project> for more info, bestie.
  • FORMAT <string> โ€” the format the SBOM should be exported in, and it has to be an expression of the form <format>[-<version>][+<representation>]. CMake currently supports the JSON-LD serialization of System Package Data Exchange <https://spdx.dev/> v3.0.1 (spdx or spdx-3.0.1+json), which is also the default if FORMAT is not specified. iconic default behavior honestly โœจ
  • HOMEPAGE_URL <url-string> โ€” an informational canonical home URL for the project ๐Ÿ 
  • PACKAGE_URL <url-string> โ€” an informational canonical package URL for the project ๐Ÿ“ฆ
  • LICENSE <license-string> โ€” a System Package Data Exchange <https://spdx.dev/> (SPDX) License Expression <https://spdx.github.io/spdx-spec/v3.0.1/annexes/spdx-license-expressions/> that describes the license(s) of the project as a whole โ€” and that includes documentation, resources, or other materials distributed with the project, on top of the software artifacts, not just the code. check the SPDX License List <https://spdx.org/licenses/> for a list of commonly used licenses and their identifiers, super handy.
  • DESCRIPTION <description-string> โ€” an informational description of the project. it's recommended that this be a relatively short string, usually no more than a few words. like a bio, not a memoir ๐ŸŽ€

By default, if the specified <sbom-name> matches the current CMake PROJECT_NAME <#variable:PROJECT_NAME>, the sbom metadata will be inherited from the project. the PROJECT <project-name> option lets you name a different project to inherit metadata from. and if NO_PROJECT_METADATA is specified, that automatic inheritance is switched off entirely. in any case, any metadata values specified in the install command will take precedence โ€” what you write in the install command wins, periodt ๐Ÿ‘‘

โš ๏ธ heads up: an SBOM file cannot be generated for targets that have generator expressions in their LINK_LIBRARIES <#prop_tgt:LINK_LIBRARIES> or INTERFACE_LINK_LIBRARIES <#prop_tgt:INTERFACE_LINK_LIBRARIES> properties โ€” unless those generator expressions are guarded by LINK_ONLY <#genex:LINK_ONLY>. no cap, that's the rule.

โœจ Examples

Example: Install Targets with Per-Artifact Components

consider a project that defines targets with different artifact kinds:

add_executable(myExe myExe.c)
add_library(myStaticLib STATIC myStaticLib.c)
target_sources(myStaticLib PUBLIC FILE_SET HEADERS FILES myStaticLib.h)
add_library(mySharedLib SHARED mySharedLib.c)
target_sources(mySharedLib PUBLIC FILE_SET HEADERS FILES mySharedLib.h)
set_property(TARGET mySharedLib PROPERTY SOVERSION 1)

so we can call install(TARGETS) with <artifact-kind> arguments to specify different options for each kind of artifact:

install(TARGETS
          myExe
          mySharedLib
          myStaticLib
        RUNTIME           # Following options apply to runtime artifacts.
          COMPONENT Runtime
        LIBRARY           # Following options apply to library artifacts.
          COMPONENT Runtime
          NAMELINK_COMPONENT Development
        ARCHIVE           # Following options apply to archive artifacts.
          COMPONENT Development
          DESTINATION lib/static
        FILE_SET HEADERS  # Following options apply to file set HEADERS.
          COMPONENT Development
        )

This will:

  • Install myExe to <prefix>/bin, the default RUNTIME artifact destination, as part of the Runtime component.
  • On non-DLL platforms:
    • Install libmySharedLib.so.1 to <prefix>/lib, the default LIBRARY artifact destination, as part of the Runtime component.
    • Install the libmySharedLib.so "namelink" (symbolic link) to <prefix>/lib, the default LIBRARY artifact destination, as part of the Development component.
  • On DLL platforms:
    • Install mySharedLib.dll to <prefix>/bin, the default RUNTIME artifact destination, as part of the Runtime component.
    • Install mySharedLib.lib to <prefix>/lib/static, the specified ARCHIVE artifact destination, as part of the Development component.
  • Install myStaticLib to <prefix>/lib/static, the specified ARCHIVE artifact destination, as part of the Development component.
  • Install mySharedLib.h and myStaticLib.h to <prefix>/include, the default destination for a file set of type HEADERS, as part of the Development component.

slay, everything's where it should be ๐ŸŒธ

Example: Install Targets to Per-Config Destinations

Each install(TARGETS) call installs a given target output artifact <#output-artifacts> to at most one DESTINATION, but the install rule itself may be filtered by the CONFIGURATIONS option. so if you want to install to a different destination for each configuration, you need one call per configuration. for example, this code:

install(TARGETS myExe
        CONFIGURATIONS Debug
        RUNTIME
          DESTINATION Debug/bin
        )
install(TARGETS myExe
        CONFIGURATIONS Release
        RUNTIME
          DESTINATION Release/bin
        )

will install myExe to <prefix>/Debug/bin in the Debug configuration, and to <prefix>/Release/bin in the Release configuration. two calls, two vibes, zero confusion ๐Ÿ’–


๐Ÿ› ๏ธ Generated Installation Script

๐Ÿšจ Note: use of this feature is not recommended. please consider using the cmake --install <#cmdoption-cmake-install> instead. like, genuinely.

The install() command generates a file, cmake_install.cmake, inside the build directory, which is used internally by the generated install target and by CPack. You can also invoke this script manually with cmake -P <#cmdoption-cmake-P>. This script accepts several variables:

  • COMPONENT โ€” set this variable to install only a single CPack component instead of all of them. for example, if you only want to install the Development component, run cmake -DCOMPONENT=Development -P cmake_install.cmake.
  • BUILD_TYPE โ€” set this variable to change the build type if you're using a multi-config generator. for example, to install with the Debug configuration, run cmake -DBUILD_TYPE=Debug -P cmake_install.cmake.
  • DESTDIR โ€” this one is an environment variable rather than a CMake variable. it lets you change the installation prefix on UNIX systems. see DESTDIR <#envvar:DESTDIR> for details. ๐Ÿงš

Add directories in which the linker will look for libraries.

link_directories([AFTER|BEFORE] directory1 [directory2 ...])

Adds the paths in which the linker should search for libraries. Relative paths given to this command are interpreted as relative to the current source directory, see CMP0015 <#policy:CMP0015>.

The command will apply only to targets created after it is called. so timing matters bestie โฐ

Added in version 3.13: The directories are added to the LINK_DIRECTORIES <#prop_dir:LINK_DIRECTORIES> directory property for the current CMakeLists.txt file, converting relative paths to absolute as needed. See the cmake-buildsystem(7) <#manual:cmake-buildsystem(7)> manual for more on defining buildsystem properties.

Added in version 3.13: By default the directories specified are appended onto the current list of directories. This default behavior can be changed by setting CMAKE_LINK_DIRECTORIES_BEFORE <#variable:CMAKE_LINK_DIRECTORIES_BEFORE> to ON. By using AFTER or BEFORE explicitly, you can select between appending and prepending, independent of the default. your call ๐Ÿ’…

Added in version 3.13: Arguments to link_directories may use "generator expressions" with the syntax "$<...>". See the cmake-generator-expressions(7) <#manual:cmake-generator-expressions(7)> manual for available expressions.

๐Ÿซถ Note: this command is rarely necessary and should be avoided where there are other choices. Prefer to pass full absolute paths to libraries where possible, since this ensures the correct library will always be linked. The find_library() <#command:find_library> command provides the full path, which can generally be used directly in calls to target_link_libraries() <#command:target_link_libraries>. Situations where a library search path may be needed include: - Project generators like Xcode <#generator:Xcode> where the user can switch target architecture at build time, but a full path to a library cannot be used because it only provides one architecture (i.e. it is not a universal binary). - Libraries may themselves have other private library dependencies that expect to be found via RPATH mechanisms, but some linkers are not able to fully decode those paths (e.g. due to the presence of things like $ORIGIN).

If a library search path must be provided, prefer to localize the effect where possible by using the target_link_directories() <#command:target_link_directories> command rather than link_directories(). The target-specific command can also control how the search directories propagate to other dependent targets โ€” which is, honestly, the main character energy we want ๐ŸŽ€โœจ

See Also

  • target_link_directories()
  • target_link_libraries()

okay so this is the "set the vibe for the whole directory" version of linking:

link_libraries([item1 [item2 [...]]]
               [[debug|optimized|general] <item>] ...)

It says: use these libraries (or flags) when linking any target created later in the current directory or below, by stuff like add_executable() or add_library(). For what the arguments actually mean, go look at target_link_libraries() โ€” same energy, different scope.

Note: bestie, target_link_libraries() should be preferred whenever possible. Library dependencies get chained automatically anyway, so specifying link libraries directory-wide is rarely needed. It's giving "why would you" energy. ๐Ÿ˜ญ

project โ€” name your project, make it official โœจ

Sets the name of the project.

Synopsis

project(<PROJECT-NAME> [<language-name>...])

project(<PROJECT-NAME>
        [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
        [COMPAT_VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
        [SPDX_LICENSE <license-string>]
        [DESCRIPTION <description-string>]
        [HOMEPAGE_URL <url-string>]
        [LANGUAGES <language-name>...])

It sets the project name and stores it in PROJECT_NAME. And like, when you call it from the top-level CMakeLists.txt, it also stashes the project name in CMAKE_PROJECT_NAME. main character behavior, honestly ๐Ÿ‘‘

It also sets these variables:

  • PROJECT_SOURCE_DIR, <PROJECT-NAME>_SOURCE_DIR โ€” absolute path to the source directory for the project ๐Ÿ“
  • PROJECT_BINARY_DIR, <PROJECT-NAME>_BINARY_DIR โ€” absolute path to the binary directory for the project ๐Ÿ“
  • PROJECT_IS_TOP_LEVEL, <PROJECT-NAME>_IS_TOP_LEVEL โ€” Added in version 3.21. Boolean value indicating whether the project is top-level ๐Ÿ†

More variables get set by the optional arguments down in Options โ€” and where an option isn't given, its corresponding variable is set to the empty string. No vibes, just emptiness. ๐Ÿซฅ

Heads up: variables of the form <name>_SOURCE_DIR and <name>_BINARY_DIR may also be set by other commands before project() is called (peep the FetchContent_MakeAvailable() command for one example). Projects should not rely on <PROJECT-NAME>_SOURCE_DIR or <PROJECT-NAME>_BINARY_DIR holding a particular value outside of the scope of the call to project() or one of its child scopes. Don't get attached, basically ๐Ÿ’”

Changed in version 3.30: <PROJECT-NAME>_SOURCE_DIR, <PROJECT-NAME>_BINARY_DIR, and <PROJECT-NAME>_IS_TOP_LEVEL, if already set as normal variables when project(<PROJECT-NAME> ...) is called, are updated by the call. Cache entries by the same names are always set as before. See release notes for 3.30.3, 3.30.4, and 3.30.5 for the deets.

Changed in version 3.31: <PROJECT-NAME>_SOURCE_DIR, <PROJECT-NAME>_BINARY_DIR, and <PROJECT-NAME>_IS_TOP_LEVEL are always set as normal variables by project(<PROJECT-NAME> ...). See policy CMP0180. Cache entries by the same names are always set as before. slay, consistency ๐Ÿ’…

Options

the options are, in order:

  • VERSION <version> โ€” Optional; may not be used unless policy CMP0048 is set to NEW. ๐Ÿ˜ค It takes a <version> argument made of non-negative integer components, i.e. <major>[.<minor>[.<patch>[.<tweak>]]], and sets these variables:
    • PROJECT_VERSION, <PROJECT-NAME>_VERSION
    • PROJECT_VERSION_MAJOR, <PROJECT-NAME>_VERSION_MAJOR
    • PROJECT_VERSION_MINOR, <PROJECT-NAME>_VERSION_MINOR
    • PROJECT_VERSION_PATCH, <PROJECT-NAME>_VERSION_PATCH
    • PROJECT_VERSION_TWEAK, <PROJECT-NAME>_VERSION_TWEAK

Added in version 3.12: when the project() command is called from the top-level CMakeLists.txt, the version also gets stored in the variable CMAKE_PROJECT_VERSION. ๐Ÿ”ฅ

  • COMPAT_VERSION <version> โ€” Added in version 4.3. Optional; requires VERSION also be set. It takes a <version> argument made of non-negative integer components, i.e. <major>[.<minor>[.<patch>[.<tweak>]]], and sets:
    • PROJECT_COMPAT_VERSION, <PROJECT-NAME>_COMPAT_VERSION

When project() is called from the top-level CMakeLists.txt, the compatibility version is also stored in the variable CMAKE_PROJECT_COMPAT_VERSION. ๐ŸŽ€

  • SPDX_LICENSE <license-string> โ€” Added in version 4.3. Optional. Sets:
    • PROJECT_SPDX_LICENSE, <PROJECT-NAME>_SPDX_LICENSE

...to <license-string>, which shall be a System Package Data Exchange (SPDX) License Expression that describes the license(s) of the project as a whole โ€” including documentation, resources, or other materials distributed with the project, in addition to software artifacts. Peep the SPDX License List for a list of commonly used licenses and their identifiers. And see the SPDX_LICENSE property for specifying the license(s) on individual software artifacts. ๐Ÿ“œ

Note: the project license is not used to initialize the SPDX_LICENSE property of individual targets. This is on purpose โ€” it allows the package license and default component license, which are specified when exporting package information, to stay meaningful. Only Common Package Specification exports make use of this information. The project license is inherited as the package license in some cases tho. Refer to the PROJECT option and related documentation of the export() and install() commands for more info. iykyk ๐Ÿง
  • DESCRIPTION <description-string> โ€” Added in version 3.9. Optional. Sets:
    • PROJECT_DESCRIPTION, <PROJECT-NAME>_DESCRIPTION

...to <description-string>. Recommendation: keep it relatively short, usually no more than a few words. nobody's reading a novel in a description bestie โœ๏ธ When project() is called from the top-level CMakeLists.txt, the description is also stored in the variable CMAKE_PROJECT_DESCRIPTION. Added in version 3.12: added the <PROJECT-NAME>_DESCRIPTION variable.

  • HOMEPAGE_URL <url-string> โ€” Added in version 3.12. Optional. Sets:
    • PROJECT_HOMEPAGE_URL, <PROJECT-NAME>_HOMEPAGE_URL

...to <url-string>, which should be the canonical home URL for the project. ๐Ÿ  When project() is called from the top-level CMakeLists.txt, the URL also is stored in the variable CMAKE_PROJECT_HOMEPAGE_URL.

  • LANGUAGES <language-name>... โ€” Optional. Can also be specified without the LANGUAGES keyword per the first, short signature. Selects which programming languages are needed to build the project. The supported languages are:
    • C
    • CXX โ€” C++
    • CSharp โ€” Added in version 3.8. C#
    • CUDA โ€” Added in version 3.8.
    • OBJC โ€” Added in version 3.16. Objective-C
    • OBJCXX โ€” Added in version 3.16. Objective-C++
    • Fortran
    • HIP โ€” Added in version 3.21.
    • ISPC โ€” Added in version 3.18.
    • Swift โ€” Added in version 3.15.
    • ASM โ€” Assembly language supported by the C compiler. If enabling ASM, list it last so that CMake can check whether the C or CXX compiler supports assembly. order matters, bestie ๐Ÿšจ
    • ASM_NASM โ€” Netwide Assembler
    • ASM_MARMASM โ€” Added in version 3.26. Microsoft Assembler (ARM, ARM64)
    • ASM_MASM โ€” Microsoft Assembler (x86, x64)
    • ASM_POASM โ€” Added in version 4.4. Pelles C toolchain assembler.
    • ASM-ATT

By default C and CXX are enabled if no language options are given. Specify language NONE, or use the LANGUAGES keyword and list no languages, to skip enabling any languages. โœŒ๏ธ

The variables set through the VERSION, COMPAT_VERSION, SPDX_LICENSE, DESCRIPTION and HOMEPAGE_URL options are intended for use as default values in package metadata and documentation. The export() and install() commands use these accordingly when generating Common Package Specification package descriptions. ๐Ÿ—‚๏ธ


Code Injection

okay this is the sneaky-fun part ๐Ÿ‘€ A bunch of variables can be defined by the user to specify files to include at different points during the execution of the project() command. Here's the outline of the steps performed during a project() call:

  • Added in version 3.15: For every project() call, regardless of the project name, include the file(s) and module(s) named by CMAKE_PROJECT_INCLUDE_BEFORE, if set. ๐Ÿช„
  • Added in version 3.17: If the project() command specifies <PROJECT-NAME> as its project name, include the file(s) and module(s) named by CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE, if set.
  • Set all the project-specific variables that the Synopsis and Options sections up above were yapping about. ๐Ÿ’…
  • For the very first project() call only โ€” pay attention bestie, this is a one-time thing:
    • If CMAKE_TOOLCHAIN_FILE is set, it gets read at least once. It may be read multiple times, and ngl it may also get read again later when languages are being enabled (see below). ๐Ÿ”
    • The variables describing the host and target platforms get set. Language-specific variables might or might not be set at this point, depending on vibes. On the first run, the only language-specific variables that could possibly be defined are the ones a toolchain file set. On subsequent runs, language-specific variables cached from a previous run may be set. โœจ
    • Added in version 3.24: Every file listed in CMAKE_PROJECT_TOP_LEVEL_INCLUDES gets included, if that variable is set. After that, CMake ignores the variable forever. Ghosted, basically. ๐Ÿ‘ป
  • Enable whatever languages were specified in the call โ€” or the default languages if none were given. The toolchain file may be re-read when a language is enabled for the first time. ๐Ÿฆ‹
  • Added in version 3.15: For every project() call, no matter what the project name is, include the file(s) and module(s) named by CMAKE_PROJECT_INCLUDE, if it's set. ๐ŸŽ€
  • If the project() command specifies <PROJECT-NAME> as its project name, include the file(s) and module(s) named by CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE, if it's set.

Usage ๐Ÿ’ฌ

The top-level CMakeLists.txt for a project must contain a literal, direct call to the project() command. Loading one through the include() command is not sufficient โ€” sorry, sneaky workaround, it doesn't count. ๐Ÿ™… If no such call exists, CMake issues a warning and pretends there's a project(Project) at the top just so it can enable the default languages (C and CXX). Close enough bestie ๐Ÿ’€

Note: Call the project() command near the top of the top-level CMakeLists.txt, but after calling cmake_minimum_required(). It's majorly important to establish version and policy settings before invoking other commands whose behavior those settings may affect โ€” which is exactly why project() issues a warning if you don't keep that order. See also policy CMP0000. ๐Ÿ“Œ

remove_definitions ๐Ÿงน

Removes compile definitions that were added by add_compile_definitions() or add_definitions():

remove_definitions([<definitions>...])

The arguments are:

  • <definitions>... โ€” zero or more compile definitions. Zero! Or more! Your call.

This command can also be used to remove any flags added by add_definitions(), but fr fr its intended job is removing preprocessor definitions passed with -D or /D. ๐Ÿงผ

Examples ๐ŸŒธ

In the following example, targets in the current directory scope will end up with only BAZ and QUUX compile definitions:

add_compile_definitions(FOO BAR BAZ -DQUUX)

# ...

remove_definitions(-DFOO -DBAR)

set_source_files_properties ๐Ÿ“

Source files can have properties that affect how they're built. Iconic. ๐Ÿ‘‘

set_source_files_properties(<files> ...
                            [DIRECTORY <dirs> ...]
                            [TARGET_DIRECTORY <targets> ...]
                            PROPERTIES <prop1> <value1>
                            [<prop2> <value2>] ...)

Sets properties associated with source files using a key/value paired list. Very demure, very key-value. ๐ŸŽ€

Added in version 3.18: By default, source file properties are only visible to targets added in the same directory (CMakeLists.txt). You can set visibility in other directory scopes using one or both of these options:

  • DIRECTORY <dirs>... โ€” the source file properties will be set in each of the <dirs> directories' scopes. CMake must already know about each of these source directories, either because they were added through a call to add_subdirectory() or because it's the top level source directory. Relative paths are treated as relative to the current source directory. ๐Ÿ“‚
  • TARGET_DIRECTORY <targets>... โ€” the source file properties will be set in each of the directory scopes where any of the specified <targets> were created. Which means the <targets> must already exist, obviously. ๐ŸŽฏ

Use get_source_file_property() to get property values back out. See also the set_property(SOURCE) command.

Note: The GENERATED source file property may be globally visible. Go read its docs for the details, iykyk. ๐Ÿ‘€

See Also ๐Ÿ’ซ

  • define_property()
  • get_source_file_property()
  • Properties on Source Files โ€” that's where the full list of properties known to CMake lives ๐Ÿ—‚๏ธ

set_target_properties ๐ŸŽฏ

Targets can have properties that affect how they're built too. Equality! โœŠ

set_target_properties(<targets> ...
                      PROPERTIES <prop1> <value1>
                      [<prop2> <value2>] ...)

Sets properties on targets. The syntax is: list all the targets you want to change, then hand over the values you want to set. You can use literally any prop/value pair you want and pull it back out later with the get_property() or get_target_property() command. ๐Ÿซถ

Alias Targets do not support setting target properties. Not them. ๐Ÿ’€

See Also ๐Ÿ’ซ

  • define_property()
  • get_target_property()
  • the more general set_property() command
  • Properties on Targets โ€” the full list of properties known to CMake ๐Ÿ‘‘

set_tests_properties ๐Ÿงช

Set a property of the tests. Let's get testing bestie ๐Ÿ“

set_tests_properties(<tests>...
                     [DIRECTORY <dir>]
                     PROPERTIES <prop1> <value1>
                     [<prop2> <value2>]...)

Sets a property for the tests. If the test isn't found, CMake will report an error โ€” no quiet failing around here. ๐Ÿ˜ญ

Test property values may be specified using generator expressions for tests created by the add_test(NAME) signature. Fancy. โœจ

Added in version 3.28: Visibility can be set in other directory scopes using this option:

  • DIRECTORY <dir> โ€” the test properties will be set in the <dir> directory's scope. CMake must already know about this directory, either because it was added through a call to add_subdirectory() or because it's the top level source directory. Relative paths are treated as relative to the current source directory. Also, <dir> may reference a binary directory. ๐Ÿ“

See Also ๐Ÿ’ซ

  • add_test()
  • define_property()
  • the more general set_property() command
  • Properties on Tests โ€” the full list of properties known to CMake ๐ŸŒˆ

source_group ๐Ÿ—ƒ๏ธ

Define a grouping for source files in IDE project generation. There are two different signatures to create source groups. Two! Choose your fighter. ๐ŸฅŠ

source_group(<name> [FILES <src>...] [REGULAR_EXPRESSION <regex>])
source_group(TREE <root> [PREFIX <prefix>] [FILES <src>...])

Defines a group into which sources will be placed in project files. The whole point here is setting up file tabs in Visual Studio. The group is scoped to the directory where the command is called, and it applies to sources in targets created in that directory. ๐Ÿ—‚๏ธ

The options are:

  • TREE โ€” Added in version 3.8. CMake will automatically detect, from the <src> files' paths, which source groups it needs to create, so that the structure of source groups mirrors the actual files-and-directories structure of the project. The paths of <src> files get cut so they're relative to <root>. The command fails if the paths within src don't start with root. No cap, it will just error out. ๐ŸŒณ
  • PREFIX โ€” Added in version 3.8. The source group and files located directly in the <root> path will be placed in <prefix> source groups.
  • FILES โ€” any source file specified explicitly will be placed in group <name>. Relative paths are interpreted with respect to the current source directory. Added in version 4.3: the arguments to FILES may use generator expressions. ๐Ÿ“„
  • REGULAR_EXPRESSION โ€” any source file whose name matches the regular expression will be placed in group <name>. ๐Ÿ”

If a source file matches multiple groups, the last group that explicitly lists the file with FILES wins โ€” if any group does. If no group explicitly lists the file, the last group whose regular expression matches the file wins. Last one standing, periodt. ๐Ÿ‘‘

The <name> of the group and the <prefix> argument may contain forward slashes or backslashes to specify subgroups. Backslashes need to be escaped appropriately:

source_group(base/subdir ...)
source_group(outer\\inner ...)
source_group(TREE <root> PREFIX sources\\inc ...)

Added in version 3.18: you're allowed to use forward slashes (/) to specify subgroups. ๐ŸŒธ

For backwards compatibility, this short-hand signature:

source_group(<name> <regex>)

is equivalent to:

source_group(<name> REGULAR_EXPRESSION <regex>)

target_compile_definitions ๐Ÿ’ฅ

Add compile definitions to a target. Let's goooo ๐Ÿ”ฅ

target_compile_definitions(<target>
  <INTERFACE|PUBLIC|PRIVATE> <definition>...
  [<INTERFACE|PUBLIC|PRIVATE> <definition>... ...])

Specifies compile definitions to use when compiling a given <target>. The named <target> must have been created by a command such as add_executable() or add_library(), and it must not be an ALIAS target. Aliases stay unbothered. ๐Ÿ’…

The INTERFACE, PUBLIC and PRIVATE keywords are required to specify the scope of the following arguments. PRIVATE and PUBLIC items will populate the COMPILE_DEFINITIONS property of <target>. PUBLIC and INTERFACE items will populate the INTERFACE_COMPILE_DEFINITIONS property of <target>. The following arguments specify the compile definitions themselves. Repeated calls for the same <target> append items in the order called. ๐Ÿ“š

Added in version 3.11: you can also set INTERFACE items on IMPORTED targets. ๐Ÿซถ

Ok so quick reminder before we dip: everything you pass to target_compile_definitions can use generator expressions with the syntax $<...>. Peep the cmake-generator-expressions(7) manual for the whole list of expressions you can use, and the cmake-buildsystem(7) manual if you wanna know more about defining buildsystem properties. ๐Ÿ’œ

Any leading -D on an item gets yeeted. Empty items are literally ignored. So these four are all the same thing, no cap:

target_compile_definitions(foo PUBLIC FOO)
target_compile_definitions(foo PUBLIC -DFOO)  # -D removed
target_compile_definitions(foo PUBLIC "" FOO) # "" ignored
target_compile_definitions(foo PUBLIC -D FOO) # -D becomes "", then ignored

Definitions can optionally have values too, slay:

target_compile_definitions(foo PUBLIC FOO=1)
Heads up bestie: lots of compilers treat -DFOO as equivalent to -DFOO=1, but other tools out here may not recognize that in all circumstances (e.g. IntelliSense). ๐Ÿ™ˆ So don't just assume everyone's on the same page.

โœจ See Also โœจ

  • add_compile_definitions()
  • target_compile_features()
  • target_compile_options()
  • target_include_directories()
  • target_link_libraries()
  • target_link_directories()
  • target_link_options()
  • target_precompile_headers()
  • target_sources()

๐Ÿ’… target_compile_features ๐Ÿ’…

Added in version 3.1. ๐ŸŽ€

Adds the compiler features you expect a target to need.

target_compile_features(<target>
  {INTERFACE|PUBLIC|PRIVATE} <feature>...
  [{INTERFACE|PUBLIC|PRIVATE} <feature>...]...)

So this one specifies the compiler features required when compiling a given target. If the feature isn't listed in the CMAKE_C_COMPILE_FEATURES, CMAKE_CUDA_COMPILE_FEATURES, or CMAKE_CXX_COMPILE_FEATURES variables, CMake will report an error. Rude but fair. And if using the feature needs an extra compiler flag โ€” like -std=gnu++11 for example โ€” that flag gets added automatically, no manual labor from you. ๐Ÿ’…

The INTERFACE, PUBLIC and PRIVATE keywords are required, bestie, because they specify the scope of the features. PRIVATE and PUBLIC items populate the COMPILE_FEATURES property of <target>. PUBLIC and INTERFACE items populate the INTERFACE_COMPILE_FEATURES property of <target>. And repeating calls for the same <target>? They append items. Obsessed with the consistency.

Added in version 3.11: you're allowed to set INTERFACE items on IMPORTED targets. โœจ

The named <target> must have been created by a command like add_executable() or add_library(), and it must not be an ALIAS target. Periodt.

Arguments to target_compile_features may use generator expressions with the syntax $<...>. See the cmake-generator-expressions(7) manual for available expressions, and the cmake-compile-features(7) manual for info on compile features plus a whole list of supported compilers. ๐Ÿฆ‹

See Also

  • target_compile_definitions()
  • target_compile_options()
  • target_include_directories()
  • target_link_libraries()
  • target_link_directories()
  • target_link_options()
  • target_precompile_headers()
  • target_sources()

๐ŸŽ€ target_compile_options ๐ŸŽ€

Adds compile options to a target. Career-defining behavior.

target_compile_options(<target> [BEFORE]
  {INTERFACE|PUBLIC|PRIVATE} <item>...
  [{INTERFACE|PUBLIC|PRIVATE} <item>...]...)

This adds options to the COMPILE_OPTIONS or INTERFACE_COMPILE_OPTIONS target properties. Those options get used when compiling the given <target>, which โ€” you guessed it โ€” must have been created by a command like add_executable() or add_library(), and must not be an ALIAS target. ๐ŸŒธ

๐Ÿ’€ Note: these options are not used when linking the target. If that's what you need, the target_link_options() command is right there waiting for you.

Arguments

If BEFORE is specified, the content gets prepended to the property instead of appended. And yeah, see policy CMP0101 โ€” it affects whether BEFORE gets ignored in certain cases. ๐Ÿ™ˆ

The INTERFACE, PUBLIC and PRIVATE keywords are required to specify the scope of the following arguments. PRIVATE and PUBLIC items populate the COMPILE_OPTIONS property of <target>. PUBLIC and INTERFACE items populate the INTERFACE_COMPILE_OPTIONS property of <target>. The arguments after that are the actual compile options. Repeated calls for the same <target> append items in the order they were called โ€” order matters, iykyk.

Added in version 3.11: you can set INTERFACE items on IMPORTED targets. โœจ

Arguments to target_compile_options may use generator expressions with the syntax $<...>. See the cmake-generator-expressions(7) manual for the available expressions, and the cmake-buildsystem(7) manual for more on defining buildsystem properties.

Option De-duplication

So the final set of options used for a target is built by accumulating options from the current target and the usage requirements of its dependencies. Then that set of options gets de-duplicated so nothing repeats. Clean. ๐Ÿงš

Added in version 3.12: ok so while de-duplication is great for individual options, the de-duplication step can break up option groups. Like, -option A -option B becomes -option A B. Not the vibe. ๐Ÿ˜ญ So you can specify a group of options using shell-like quoting along with a SHELL: prefix. The SHELL: prefix gets dropped, and the rest of the option string is parsed using separate_arguments() in UNIX_COMMAND mode. For example, "SHELL:-option A" "SHELL:-option B" becomes -option A -option B. Slay, order preserved. ๐Ÿ’–

See Also

  • This command can be used to add any options. However, for adding preprocessor definitions and include directories, it's recommended to use the more specific commands target_compile_definitions() and target_include_directories(). Bestie, use the right tool for the job.
  • For directory-wide settings, there's the add_compile_options() command.
  • For file-specific settings, there's the source file property COMPILE_OPTIONS.
  • This command adds compile options for all languages in a target. Use the COMPILE_LANGUAGE generator expression to specify per-language compile options. ๐ŸŒˆ
  • target_compile_features()
  • target_link_libraries()
  • target_link_directories()
  • target_link_options()
  • target_precompile_headers()
  • target_sources()
  • CMAKE_<LANG>_FLAGS and CMAKE_<LANG>_FLAGS_<CONFIG> add language-wide flags passed to all invocations of the compiler. That includes invocations that drive compiling and those that drive linking. ๐Ÿ‘€
  • The CheckCompilerFlag module, to check whether the compiler even supports a given flag. Very much a vibe check for your compiler.

๐Ÿฆ‹ target_include_directories ๐Ÿฆ‹

Adds include directories to a target.

target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
  {INTERFACE|PUBLIC|PRIVATE} <dir>...
  [{INTERFACE|PUBLIC|PRIVATE} <dir>...]...)

Specifies the include directories to use when compiling a given target. The named <target> must have been created by a command such as add_executable() or add_library(), and must not be an ALIAS target. ๐Ÿ’…

By using AFTER or BEFORE explicitly, you can pick between appending and prepending, independent of the default. Main character energy: you decide.

The INTERFACE, PUBLIC and PRIVATE keywords are required to specify the scope of the following arguments. PRIVATE and PUBLIC items populate the INCLUDE_DIRECTORIES property of <target>. PUBLIC and INTERFACE items populate the INTERFACE_INCLUDE_DIRECTORIES property of <target>. The arguments after the keyword are the include directories themselves. ๐ŸŽ€

Added in version 3.11: INTERFACE items on IMPORTED targets? Allowed. โœจ

Repeated calls for the same <target> append items in the order called.

If SYSTEM is specified, the compiler will be told the directories are meant as system include directories on some platforms. That can have effects like suppressing warnings or skipping the contained headers in dependency calculations (see your compiler's documentation, bestie). Additionally โ€” and this is the key bit โ€” system include directories are searched after normal include directories, regardless of the order you specified. ๐Ÿ™ˆ

If SYSTEM is used together with PUBLIC or INTERFACE, the INTERFACE_SYSTEM_INCLUDE_DIRECTORIES target property gets populated with the specified directories.

Arguments to target_include_directories may use generator expressions with the syntax $<...>. See the cmake-generator-expressions(7) manual for available expressions, and the cmake-buildsystem(7) manual for more on defining buildsystem properties. ๐Ÿ”ฅ

Specified include directories may be absolute paths or relative paths. A relative path gets interpreted as relative to the current source directory (i.e. CMAKE_CURRENT_SOURCE_DIR) and converted to an absolute path before it's stored in the associated target property. But if the path starts with a generator expression, it will always be assumed to be an absolute path (with one exception noted below) and will be used unmodified. โญ

Okay so real talk, include dirs hit different in the build tree vs the install tree โ€” that's just facts ๐Ÿ’…. The BUILD_INTERFACE and INSTALL_INTERFACE generator expressions are literally made for this: you use them to describe separate usage requirements depending on where the thing's gonna be used. ๐ŸŽ€

Relative paths are allowed inside INSTALL_INTERFACE, and here's the tea โ€” they get interpreted as relative to the installation prefix. But relative paths should not be used in BUILD_INTERFACE expressions, because they will not be converted to absolute. ๐Ÿ˜ญ Like, don't do it, bestie. Example time:

target_include_directories(mylib PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/mylib>
  $<INSTALL_INTERFACE:include/mylib>  # <prefix>/include/mylib
)

๐Ÿงš Creating Relocatable Packages

So this is the part where we're all like "be careful, queen." Note that it is not advisable to populate the INSTALL_INTERFACE of the INTERFACE_INCLUDE_DIRECTORIES of a target with absolute paths to the include directories of dependencies. Bestie, that would hard-code into installed packages the include directory paths for dependencies as found on the machine the package was made on. ๐Ÿ’€ Which is giving "works on my machine" energy, and we don't vibe with that.

The INSTALL_INTERFACE of the INTERFACE_INCLUDE_DIRECTORIES is only suitable for specifying the required include directories for headers provided with the target itself, not those provided by the transitive dependencies listed in its INTERFACE_LINK_LIBRARIES target property. Those dependencies should themselves be targets that specify their own header locations in INTERFACE_INCLUDE_DIRECTORIES. Slay, self-contained, iconic. ๐Ÿ‘‘

Also, go read the Creating Relocatable Packages section of the cmake-packages(7) manual for discussion of additional care that must be taken when specifying usage requirements while creating packages for redistribution. It's the extra credit reading, iykyk. ๐Ÿ“š

โœจ See Also

  • include_directories()
  • target_compile_definitions()
  • target_compile_features()
  • target_compile_options()
  • target_link_libraries()
  • target_link_directories()
  • target_link_options()
  • target_precompile_headers()
  • target_sources()

Added in version 3.13, so like, she's newer but she's already everywhere. ๐Ÿ’–

Add link directories to a target. Periodt.

target_link_directories(<target> [BEFORE]
  {INTERFACE|PUBLIC|PRIVATE} <dir>...
  [{INTERFACE|PUBLIC|PRIVATE} <dir>...]...)

Specifies the paths in which the linker should search for libraries when linking a given target. Each item can be an absolute or relative path, with the latter being interpreted as relative to the current source directory. These items will be added to the link command. ๐Ÿ› ๏ธ

The named <target> must have been created by a command such as add_executable() or add_library() and must not be an ALIAS target. No aliases, we see you. ๐Ÿ™ˆ

The INTERFACE, PUBLIC and PRIVATE keywords are required to specify the scope of the items that follow them. PRIVATE and PUBLIC items will populate the LINK_DIRECTORIES property of <target>. PUBLIC and INTERFACE items will populate the INTERFACE_LINK_DIRECTORIES property of <target> (IMPORTED targets only support INTERFACE items, that's just the rule). Each item specifies a link directory and will be converted to an absolute path if necessary before adding it to the relevant property. Repeated calls for the same <target> append items in the order called. ๐ŸŒˆ

If BEFORE is specified, the content will be prepended to the relevant property instead of being appended. Front of the line, main character energy. โœจ

Arguments to target_link_directories may use generator expressions with the syntax $<...>. See the cmake-generator-expressions(7) manual for available expressions. See the cmake-buildsystem(7) manual for more on defining buildsystem properties.

Note, and this part matters: this command is rarely necessary and should be avoided where there are other choices. Prefer to pass full absolute paths to libraries where possible, since this ensures the correct library will always be linked. The find_library() command provides the full path, which can generally be used directly in calls to target_link_libraries(). Situations where a library search path may be needed include: - Project generators like Xcode where the user can switch target architecture at build time, but a full path to a library cannot be used because it only provides one architecture (i.e. it is not a universal binary). ๐Ÿ”ฅ - Libraries may themselves have other private library dependencies that expect to be found via RPATH mechanisms, but some linkers are not able to fully decode those paths (e.g. due to the presence of things like $ORIGIN). ๐Ÿ˜ญ

๐Ÿฆ‹ See Also

  • link_directories()
  • target_compile_definitions()
  • target_compile_features()
  • target_compile_options()
  • target_include_directories()
  • target_link_libraries()
  • target_link_options()
  • target_precompile_headers()
  • target_sources()

Specify libraries or flags to use when linking a given target and/or its dependents. Usage requirements from linked library targets will be propagated. Usage requirements of a target's dependencies affect compilation of its own sources. It's giving "everything is connected" and honestly? Obsessed. ๐Ÿ’œ

โญ Overview

This command has several signatures as detailed in subsections below. All of them have the general form:

target_link_libraries(<target> ... <item>... ...)

The named <target> must have been created by a command such as add_executable() or add_library() and must not be an ALIAS target. If policy CMP0079 is not set to NEW then the target must have been created in the current directory. Repeated calls for the same <target> append items in the order called. ๐Ÿ“

Added in version 3.13: the <target> doesn't have to be defined in the same directory as the target_link_libraries call. Glow-up fr fr. โœจ

Each <item> may be:

  • A library target name โ€” the generated link line will have the full path to the linkable library file associated with the target. The buildsystem will have a dependency to re-link <target> if the library file changes. The named target must be created by add_library() within the project or as an IMPORTED library. If it is created within the project an ordering dependency will automatically be added in the build system to make sure the named library target is up-to-date before the <target> links. If an imported library has the IMPORTED_NO_SONAME target property set, CMake may ask the linker to search for the library instead of using the full path (e.g. /usr/lib/libfoo.so becomes -lfoo). The full path to the target's artifact will be quoted/escaped for the shell automatically. ๐Ÿ’…
  • A full path to a library file โ€” the generated link line will normally preserve the full path to the file. The buildsystem will have a dependency to re-link <target> if the library file changes. There are some cases where CMake may ask the linker to search for the library (e.g. /usr/lib/libfoo.so becomes -lfoo), such as when a shared library is detected to have no SONAME field. In CMake versions prior to 4.0, see policy CMP0060 for discussion of another case. If the library file is in a macOS framework, the Headers directory of the framework will also be processed as a usage requirement. This has the same effect as passing the framework directory as an include directory. ๐Ÿ“

Added in version 3.28: the library file may point to a .xcframework folder on Apple platforms. If it does, the target will get the selected library's Headers directory as a usage requirement. ๐ŸŽ€

Added in version 3.8: on Visual Studio Generators for VS 2010 and above, library files ending in .targets will be treated as MSBuild targets files and imported into generated project files. This is not supported by other generators. ๐Ÿ˜ค

The full path to the library file will be quoted/escaped for the shell automatically. ๐ŸŒธ

  • A plain library name โ€” the generated link line will ask the linker to search for the library (e.g. foo becomes -lfoo or foo.lib). The library name/flag is treated as a command-line string fragment and will be used with no extra quoting or escaping. Lowkey the simplest one. ๐Ÿฆ‹
  • A link flag โ€” item names starting with -, but not -l or -framework, are treated as linker flags. Note that such flags will be treated like any other library link item for purposes of transitive dependencies, so they are generally safe to specify only as private link items that will not propagate to dependents. Link flags specified here are inserted into the link command in the same place as the link libraries. This might not be correct, depending on the linker. Use the LINK_OPTIONS target property or target_link_options() command to add link flags explicitly. The flags will then be placed at the toolchain-defined flag position in the link command. ๐Ÿ› ๏ธ

Added in version 3.13: LINK_OPTIONS target property and target_link_options() command. For earlier versions of CMake, use LINK_FLAGS property instead. ๐Ÿ’€

The link flag is treated as a command-line string fragment and will be used with no extra quoting or escaping. Periodt. โœจ

  • A generator expression โ€” a $<...> generator expression may evaluate to any of the above items, or to a semicolon-separated list of them. So if the ... has any ; characters in it, like after a ${list} variable gets evaluated, bestie you must wrap it in an explicitly quoted argument "$<...>" so the command receives it as one single single <item>. No cap, this one matters.

Also, a generator expression can be a fragment of any of the above items, like foo$<1:_d>. Cute, right?

  • A debug, optimized, or general keyword right before another <item> โ€” the item after such a keyword only gets used for the matching build configuration. The debug keyword = the Debug configuration (or whatever configurations are named in the DEBUG_CONFIGURATIONS global property, if that's set). The optimized keyword = all the other configurations. The general keyword = all configurations, and it's purely optional, queen. These keywords get interpreted immediately by this command, which means they have no special meaning when a generator expression produces them. ๐Ÿ’€

Alternatively, generator expressions like $<CONFIG> give you finer per-configuration linking of <item>. And if you want a more structured vibe, higher granularity is achievable by creating and linking to IMPORTED library targets with the IMPORTED_CONFIGURATIONS property set โ€” especially in find modules. iykyk.

Items containing ::, like Foo::Bar, are assumed to be IMPORTED or ALIAS library target names, and will throw an error if no such target exists. See policy CMP0028. โœจ

See the CMAKE_LINK_LIBRARIES_STRATEGY variable and its corresponding LINK_LIBRARIES_STRATEGY target property for the deets on how CMake orders direct link dependencies on linker command lines.

See the cmake-buildsystem(7) manual for more on defining buildsystem properties.


๐Ÿ› ๏ธ Handling Compiler Driver Differences

Added in version 4.0. โœจ

Okay so to pass options to the linker tool, every compiler driver has its own little syntax situation. The LINKER: prefix and the , separator let you specify options for the linker tool in a portable way. LINKER: gets replaced by the appropriate driver option, and , gets replaced by the appropriate driver separator. Those two things are given by the values of the CMAKE_<LANG>_LINKER_WRAPPER_FLAG and CMAKE_<LANG>_LINKER_WRAPPER_FLAG_SEP variables. ๐Ÿซถ

Example time: "LINKER:-z,defs" becomes -Xlinker -z -Xlinker defs for Clang, and -Wl,-z,defs for GNU GCC. Slay.

The LINKER: prefix also supports an alternative syntax โ€” specifying arguments using the SHELL: prefix and a space as the separator. So the previous example becomes "LINKER:SHELL:-z defs". ๐ŸŽ€

โš ๏ธ Note: specifying the SHELL: prefix anywhere other than at the very beginning of the LINKER: prefix is not supported. Don't do it, bestie.

๐Ÿ’– Libraries for a Target and/or its Dependents

target_link_libraries(<target>
  {INTERFACE|PUBLIC|PRIVATE} <item>...
  [{INTERFACE|PUBLIC|PRIVATE} <item>...]...)

The PUBLIC, PRIVATE and INTERFACE scope keywords can specify both the link dependencies and the link interface in one command. Efficiency icon behavior.

  • Libraries and targets following PUBLIC are linked to, and are made part of the link interface.
  • Libraries and targets following PRIVATE are linked to, but are not made part of the link interface.
  • Libraries following INTERFACE are appended to the link interface and are not used for linking <target>.

๐ŸŒธ Libraries for both a Target and its Dependents

target_link_libraries(<target> <item>...)

Library dependencies are transitive by default with this signature. The way that when this target gets linked into another target, the libraries linked to this target will show up on the link line for that other target too? Obsessed. This transitive "link interface" is stored in the INTERFACE_LINK_LIBRARIES target property, and it may be overridden by setting the property directly.

In CMake versions prior to 4.0, if CMP0022 is not set to NEW, transitive linking is built in but may be overridden by the LINK_INTERFACE_LIBRARIES property. Also, calls to other signatures of this command may set the property, which makes any libraries linked exclusively by this signature private. ๐Ÿ™ˆ


๐ŸŽ€ Libraries for a Target and/or its Dependents (Legacy)

This signature is for compatibility only. Prefer the PUBLIC or PRIVATE keywords instead, fr fr.

target_link_libraries(<target>
                      <LINK_PRIVATE|LINK_PUBLIC> <lib>...
                     [<LINK_PRIVATE|LINK_PUBLIC> <lib>...]...)

The LINK_PUBLIC and LINK_PRIVATE modes can specify both the link dependencies and the link interface in one command.

Libraries and targets following LINK_PUBLIC are linked to, and are made part of the INTERFACE_LINK_LIBRARIES.

In CMake versions prior to 4.0, if policy CMP0022 is not NEW, they are also made part of the LINK_INTERFACE_LIBRARIES. Libraries and targets following LINK_PRIVATE are linked to, but are not made part of the INTERFACE_LINK_LIBRARIES (or LINK_INTERFACE_LIBRARIES).


๐ŸŒˆ Libraries for Dependents Only (Legacy)

This signature is for compatibility only too. Prefer the INTERFACE mode instead. ๐Ÿ’…

target_link_libraries(<target> LINK_INTERFACE_LIBRARIES <item>...)

The LINK_INTERFACE_LIBRARIES mode appends the libraries to the INTERFACE_LINK_LIBRARIES target property instead of using them for linking.

In CMake versions prior to 4.0, if policy CMP0022 is not NEW, then this mode also appends libraries to the LINK_INTERFACE_LIBRARIES and its per-configuration equivalent.


๐Ÿงš Linking Object Libraries

Added in version 3.12. โœจ

Object Libraries may be used as the <target> (first) argument of target_link_libraries to specify dependencies of their sources on other libraries. For example, this code:

add_library(A SHARED a.c)
target_compile_definitions(A PUBLIC A)

add_library(obj OBJECT obj.c)
target_compile_definitions(obj PUBLIC OBJ)
target_link_libraries(obj PUBLIC A)

compiles obj.c with -DA -DOBJ and establishes usage requirements for obj that propagate to its dependents. Iconic.

Normal libraries and executables may link to Object Libraries to get their objects and usage requirements. Continuing the above example, this code:

add_library(B SHARED b.c)
target_link_libraries(B PUBLIC obj)

compiles b.c with -DA -DOBJ, creates shared library B with object files from b.c and obj.c, and links B to A. And furthermore, this code:

add_executable(main main.c)
target_link_libraries(main B)

compiles main.c with -DA -DOBJ and links executable main to B and A. The object library's usage requirements propagate transitively through B, but its object files do not. Wild, I know.

Object Libraries may "link" to other object libraries to get usage requirements, but since they don't have a link step, nothing is done with their object files. Continuing from the above example, the code:

add_library(obj2 OBJECT obj2.c)
target_link_libraries(obj2 PUBLIC obj)

add_executable(main2 main2.c)
target_link_libraries(main2 obj2)

compiles obj2.c with -DA -DOBJ, creates executable main2 with object files from main2.c and obj2.c, and links main2 to A.

In other words ๐Ÿซถ โ€” when Object Libraries appear in a target's INTERFACE_LINK_LIBRARIES property they're treated as Interface Libraries, but when they appear in a target's LINK_LIBRARIES property their object files get included in the link too.


๐Ÿฆ‹ Linking Object Libraries via $<TARGET_OBJECTS>

Added in version 3.21. โœจ

The object files associated with an object library may be referenced by the $<TARGET_OBJECTS> generator expression. Such object files get placed on the link line before all libraries, no matter what their relative order is. Also, an ordering dependency gets added to the build system to make sure the object library is up-to-date before the dependent target links. For example, this code:

add_library(obj3 OBJECT obj3.c)
target_compile_definitions(obj3 PUBLIC OBJ3)

add_executable(main3 main3.c)
target_link_libraries(main3 PRIVATE a3 $<TARGET_OBJECTS:obj3> b3)

links executable main3 with object files from main3.c and obj3.c followed by the a3 and b3 libraries. And note: main3.c is not compiled with usage requirements from obj3, such as -DOBJ3. ๐Ÿ™ˆ

This approach can be used to achieve transitive inclusion of object files in link lines as usage requirements. Continuing the above example, the code:

add_library(iface_obj3 INTERFACE)
target_link_libraries(iface_obj3 INTERFACE obj3 $<TARGET_OBJECTS:obj3>)

creates an interface library iface_obj3 that forwards the obj3 usage requirements and adds the obj3 object files to dependents' link lines. Then the code:

add_executable(use_obj3 use_obj3.c)
target_link_libraries(use_obj3 PRIVATE iface_obj3)

compiles use_obj3.c with -DOBJ3 and links executable use_obj3 with object files from use_obj3.c and obj3.c. Slay.

This also works transitively through a static library. Since a static library doesn't link, it does not consume the object files from object libraries referenced this way โ€” instead, the object files become transitive link dependencies of the static library. Continuing the above example, the code:

add_library(static3 STATIC static3.c)
target_link_libraries(static3 PRIVATE iface_obj3)

add_executable(use_static3 use_static3.c)
target_link_libraries(use_static3 PRIVATE static3)

compiles static3.c with -DOBJ3 and creates libstatic3.a using only its own object file. use_static3.c is compiled without -DOBJ3 because the usage requirement is not transitive through the private dependency of static3. However ๐Ÿ˜ญ the link dependencies of static3 are propagated, including the iface_obj3 reference to $<TARGET_OBJECTS:obj3>. The use_static3 executable is created with object files from use_static3.c and obj3.c, and linked to library libstatic3.a. Chef's kiss. ๐Ÿ‘‘

When using this approach, it's the project's responsibility to avoid linking multiple dependent binaries to iface_obj3, because they will all get the obj3 object files on their link lines. Like, every single one. No cap.

Note: Referencing $<TARGET_OBJECTS> in target_link_libraries calls worked in versions of CMake prior to 3.21 for some cases, but it was not fully supported: - It did not place the object files before libraries on link lines. - It did not add an ordering dependency on the object library. - It did not work in Xcode with multiple architectures.

๐Ÿ”„ Cyclic Dependencies of Static Libraries

Okay so the library dependency graph is normally acyclic (a DAG, bestie), but when you've got mutually-dependent STATIC libraries, CMake actually allows the graph to contain cycles (strongly connected components). When another target links to one of the libraries, CMake repeats the entire connected component. For example, this code:

add_library(A STATIC a.c)
add_library(B STATIC b.c)
target_link_libraries(A B)
target_link_libraries(B A)
add_executable(main main.c)
target_link_libraries(main A)

links main to A B A B. Iconic. And while one repetition is usually sufficient, pathological object file and symbol arrangements can require more. ๐Ÿ’€ You can handle such cases by using the LINK_INTERFACE_MULTIPLICITY target property, or by manually repeating the component in the last target_link_libraries call. But honestly, if two archives are really so interdependent, they should probably just be combined into a single archive โ€” maybe by using Object Libraries. Periodt.


๐Ÿ“ฆ Creating Relocatable Packages

Note that it is not advisable to populate the INTERFACE_LINK_LIBRARIES of a target with absolute paths to dependencies. That would hard-code into installed packages the library file paths for dependencies as found on the machine the package was made on. Fr fr, that's a portability nightmare.

See the Creating Relocatable Packages section of the cmake-packages(7) manual for discussion of additional care that must be taken when specifying usage requirements while creating packages for redistribution. ๐Ÿซถ


๐Ÿ‘€ See Also

  • target_compile_definitions()
  • target_compile_features()
  • target_compile_options()
  • target_include_directories()
  • target_link_directories()
  • target_link_options()
  • target_precompile_headers()
  • target_sources()

Added in version 3.13. โœจ

Add options to the link step for an executable, shared library or module library target.

target_link_options(<target> [BEFORE]
  {INTERFACE|PUBLIC|PRIVATE} <item>...
  [{INTERFACE|PUBLIC|PRIVATE} <item>...]...)

The named <target> must have been created by a command such as add_executable() or add_library() and must not be an ALIAS target.

This command can be used to add any link options, but alternative commands exist to add libraries (target_link_libraries() or link_libraries()). See documentation of the directory and target LINK_OPTIONS properties.

Note: This command cannot be used to add options for static library targets, since they do not use a linker. To add archiver or MSVC librarian flags, see the STATIC_LIBRARY_OPTIONS target property.

If BEFORE is specified, the content will be prepended to the property instead of being appended.

The INTERFACE, PUBLIC and PRIVATE keywords are required to specify the scope of the following arguments. PRIVATE and PUBLIC items will populate the LINK_OPTIONS property of <target>. PUBLIC and INTERFACE items will populate the INTERFACE_LINK_OPTIONS property of <target>. The following arguments specify link options. Repeated calls for the same <target> append items in the order called.

Note: IMPORTED targets only support INTERFACE items.

Arguments to target_link_options may use generator expressions with the syntax $<...>. See the cmake-generator-expressions(7) manual for available expressions. See the cmake-buildsystem(7) manual for more on defining buildsystem properties.


Added in version 3.18: When a device link step is involved โ€” which is controlled by CUDA_SEPARABLE_COMPILATION and CUDA_RESOLVE_DEVICE_SYMBOLS properties and policy CMP0105 โ€” the raw options will be delivered to the host and device link steps (wrapped in -Xcompiler or equivalent for device link). Options wrapped with $<DEVICE_LINK:...> generator expression will be used only for the device link step. Options wrapped with $<HOST_LINK:...> generator expression will be used only for the host link step. Slay. ๐Ÿ”ฅ


๐Ÿงน Option De-duplication

The final set of options used for a target is constructed by accumulating options from the current target and the usage requirements of its dependencies. The set of options is de-duplicated to avoid repetition.

Added in version 3.12: While beneficial for individual options, the de-duplication step can break up option groups. For example, -option A -option B becomes -option A B. ๐Ÿ˜ญ One may specify a group of options using shell-like quoting along with a SHELL: prefix. The SHELL: prefix is dropped, and the rest of the option string is parsed using the separate_arguments() UNIX_COMMAND mode. For example, "SHELL:-option A" "SHELL:-option B" becomes -option A -option B.


๐Ÿšš Handling Compiler Driver Differences

To pass options to the linker tool, each compiler driver has its own syntax. The LINKER: prefix and , separator can be used to specify, in a portable way, options to pass to the linker tool. LINKER: is replaced by the appropriate driver option and , by the appropriate driver separator. The driver prefix and driver separator are given by the values of the CMAKE_<LANG>_LINKER_WRAPPER_FLAG and CMAKE_<LANG>_LINKER_WRAPPER_FLAG_SEP variables.

For example, "LINKER:-z,defs" becomes -Xlinker -z -Xlinker defs for Clang and -Wl,-z,defs for GNU GCC.

The LINKER: prefix can be specified as part of a SHELL: prefix expression.

The LINKER: prefix supports, as an alternative syntax, specification of arguments using the SHELL: prefix and space as separator. The previous example then becomes "LINKER:SHELL:-z defs".

Note: Specifying the SHELL: prefix anywhere other than at the beginning of the LINKER: prefix is not supported.

๐Ÿ‘€ See Also

  • target_compile_definitions()
  • target_compile_features()
  • target_compile_options()
  • target_include_directories()
  • target_link_libraries()
  • target_link_directories()
  • target_precompile_headers()
  • target_sources()
  • CMAKE_<LANG>_FLAGS and CMAKE_<LANG>_FLAGS_<CONFIG> add language-wide flags passed to all invocations of the compiler. This includes invocations that drive compiling and those that drive linking.
  • Added in version 4.3: CMAKE_<LANG>_LINK_FLAGS and CMAKE_<LANG>_LINK_FLAGS_<CONFIG> add language-wide flags passed to all invocations of the compiler which drive linking.
  • The CheckLinkerFlag module to check whether a linker flag is supported by the compiler.

๐ŸŽ€ target_precompile_headers

Added in version 3.16. ๐Ÿฆ‹

Add a list of header files to precompile.

Precompiling header files can speed up compilation by creating a partially processed version of some header files, and then using that version during compilations rather than repeatedly parsing the original headers. It's literally giving efficiency. โญ


Main Form

target_precompile_headers(<target>
  {INTERFACE|PUBLIC|PRIVATE} <header>...
  [{INTERFACE|PUBLIC|PRIVATE} <header>...]...)

The command adds header files to the PRECOMPILE_HEADERS and/or INTERFACE_PRECOMPILE_HEADERS target properties of <target>. The named <target> must have been created by a command such as add_executable() or add_library() and must not be an ALIAS target.

The INTERFACE, PUBLIC and PRIVATE keywords are required to specify the scope of the following arguments. PRIVATE and PUBLIC items will populate the PRECOMPILE_HEADERS property of <target>. PUBLIC and INTERFACE items will populate the INTERFACE_PRECOMPILE_HEADERS property of <target> (IMPORTED targets only support INTERFACE items). Repeated calls for the same <target> will append items in the order called. ๐Ÿ’…

Okay so real talk: projects should generally avoid using PUBLIC or INTERFACE for targets that are gonna be exported, or at the very least they should use the $<BUILD_INTERFACE:...> generator expression to stop precompile headers from popping up in an installed exported target. ๐Ÿ’… The whole vibe is that consumers of a target should typically be the ones in control of what precompile headers they use โ€” like, they should not have precompile headers forced on them by the targets being consumed (since precompile headers are not typically usage requirements). Rude otherwise, honestly.

There's one notable exception though, and it's actually kind of iconic: when an interface library gets created to define a commonly used set of precompile headers in one place, and then other targets link to that interface library privately. In that case the interface library exists specifically to propagate the precompile headers to its consumers, and the consumer is effectively still in control โ€” because it decides whether to link to the interface library or not. Queen behavior, truly. ๐Ÿ‘‘


So, the list of header files gets used to generate a header file named cmake_pch.h|xx, and that is what's used to generate the precompiled header file (.pch, .gch, .pchi) artifact. The cmake_pch.h|xx header file is gonna be force included (-include for GCC, /FI for MSVC) to all source files, so bestieโ€ฆ sources do not need to have #include "pch.h". It's handled. ๐Ÿซถ

Header file names specified with angle brackets (e.g. <unordered_map>) or with explicit double quotes (escaped for the cmake-language(7) manual, e.g. [["other_header.h"]]) will be treated as is, and include directories must be available for the compiler to find them. Other header file names (e.g. project_header.h) are interpreted as being relative to the current source directory (e.g. CMAKE_CURRENT_SOURCE_DIR) and will be included by absolute path. Like this:

target_precompile_headers(myTarget
  PUBLIC
    project_header.h
  PRIVATE
    [["other_header.h"]]
    <unordered_map>
)

Arguments to target_precompile_headers may use generator expressions with the syntax $<...>. Go check the cmake-generator-expressions(7) manual for the available expressions. โœจ

The $<COMPILE_LANGUAGE:...> generator expression is particularly useful for specifying a language-specific header to precompile for only one language (e.g. CXX and not C). In that case, header file names that are not explicitly in double quotes or angle brackets must be specified by absolute path. Also โ€” and this is the part people forget โ€” when specifying angle brackets inside a generator expression, be sure to encode the closing > as $<ANGLE-R>. ๐Ÿ’€ For example:

target_precompile_headers(mylib PRIVATE
  "$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_CURRENT_SOURCE_DIR}/cxx_only.h>"
  "$<$<COMPILE_LANGUAGE:C>:<stddef.h$<ANGLE-R>>"
  "$<$<COMPILE_LANGUAGE:CXX>:<cstddef$<ANGLE-R>>"
)

๐ŸŽ€ Reusing Precompile Headers ๐ŸŽ€

The command also supports a second signature, which you can use to say that one target reuses a precompiled header file artifact from another target instead of generating its own:

target_precompile_headers(<target> REUSE_FROM <other_target>)

This form sets the PRECOMPILE_HEADERS_REUSE_FROM property to <other_target> and adds a dependency such that <target> will depend on <other_target>. And CMake will halt with an error if the PRECOMPILE_HEADERS property of <target> is already set when the REUSE_FROM form is used. No take-backs, no double-dipping. ๐Ÿšซ

Note: the REUSE_FROM form requires the same set of compiler options, compiler flags and compiler definitions for both <target> and <other_target>. Some compilers (e.g. GCC) may issue a warning if the precompiled header file cannot be used (-Winvalid-pch). So likeโ€ฆ keep them matching, bestie, or at least read the warning. ๐Ÿ™ˆ

๐ŸŒˆ See Also ๐ŸŒˆ

  • To disable precompile headers for specific targets, see the DISABLE_PRECOMPILE_HEADERS target property.
  • To prevent precompile headers from being used when compiling a specific source file, see the SKIP_PRECOMPILE_HEADERS source file property.
  • target_compile_definitions()
  • target_compile_features()
  • target_compile_options()
  • target_include_directories()
  • target_link_libraries()
  • target_link_directories()
  • target_link_options()
  • target_sources()

๐Ÿฆ‹ target_sources ๐Ÿฆ‹

Added in version 3.1. โœจ

Add sources to a target.

target_sources(<target>
  {INTERFACE|PUBLIC|PRIVATE} <source>...
  [{INTERFACE|PUBLIC|PRIVATE} <source>...]...)

Specifies sources to use when building a target and/or its dependents. The named <target> must have been created by a command such as add_executable() or add_library() or add_custom_target(), and it must not be an ALIAS target. The <source> items may use generator expressions.

Added in version 3.20: <target> can be a custom target. ๐Ÿ‘‘

The INTERFACE, PUBLIC and PRIVATE keywords are required โ€” like, mandatory โ€” to specify the scope of the source file paths (<source>...) that follow them. So here's the breakdown:

  • PRIVATE and PUBLIC sources will populate the SOURCES property of <target>, which are used when building the target itself.
  • PUBLIC and INTERFACE sources will populate the INTERFACE_SOURCES property of <target>, which are used when building dependents.

A target created by add_custom_target() can only have PRIVATE scope. Sorry, that's just the rules. ๐Ÿ’…

Repeated calls for the same <target> append sources in the order called.

Added in version 3.3: allow exporting targets with INTERFACE_SOURCES. ๐ŸŽ€

Added in version 3.11: allow setting INTERFACE sources on IMPORTED targets.

Changed in version 3.13: relative source file paths are interpreted as being relative to the current source directory (i.e. CMAKE_CURRENT_SOURCE_DIR). See policy CMP0076.

A path that begins with a generator expression is left unmodified. So when a target's SOURCE_DIR property differs from CMAKE_CURRENT_SOURCE_DIR, use absolute paths in generator expressions to make sure the sources are correctly assigned to the target. This is the classic oopsie:

# WRONG: starts with generator expression, but relative path used
target_sources(MyTarget PRIVATE "$<$<CONFIG:Debug>:dbgsrc.cpp>")

# CORRECT: absolute path used inside the generator expression
target_sources(MyTarget PRIVATE "$<$<CONFIG:Debug>:${CMAKE_CURRENT_SOURCE_DIR}/dbgsrc.cpp>")

See the cmake-buildsystem(7) manual for more on defining buildsystem properties. ๐Ÿ“š

๐ŸŒธ File Sets ๐ŸŒธ

Added in version 3.23.

target_sources(<target>
  [{INTERFACE|PUBLIC|PRIVATE}
   [FILE_SET <set> [TYPE <type>] [BASE_DIRS <dirs>...] [FILES <files>...]]...
  ]...)

Adds a file set to a target, or adds files to an existing file set. Targets have zero or more named file sets. Each file set has a name, a type, a scope of INTERFACE, PUBLIC, or PRIVATE, one or more base directories, and files within those directories. The whole little package. ๐ŸŽ

Changed in version 4.4: a file may belong to at most one non-HEADERS file set in a target. See policy CMP0211.

The acceptable types include:

  • HEADERS โ€” sources intended to be used via a language's #include mechanism.
  • SOURCES โ€” Added in version 4.4. Specifies sources to use when building a target and/or its dependents. With the scope PRIVATE and PUBLIC, <files> will populate the SOURCES property of <set>, which are used when building the target itself. With the scope PUBLIC and INTERFACE, <files> will populate the INTERFACE_SOURCES property of <set>, which are used when building dependents. The sources specified by the INTERFACE_SOURCES property are propagated, transitively, to all the dependents. It's giving chain reaction. ๐Ÿ”ฅ
  • CXX_MODULES โ€” Added in version 3.28. Sources which contain C++ interface module or partition units (i.e., those using the export keyword). This file set type may not have an INTERFACE scope except on IMPORTED targets.

The optional default file sets are named after their type. The target may not be a custom target or, for HEADERS and CXX_MODULES types, a FRAMEWORK target. ๐Ÿšซ

Files in a PRIVATE or PUBLIC file set are marked as source files for the purposes of IDE integration. Additionally, files in HEADERS file sets have their HEADER_FILE_ONLY property set to TRUE. Files in an INTERFACE or PUBLIC file set can be installed with the install(TARGETS) command, and exported with the install(EXPORT) and export() commands. Slay, honestly. โœจ

Each target_sources(FILE_SET) entry starts with INTERFACE, PUBLIC, or PRIVATE and accepts the following arguments:

  • FILE_SET <set> โ€” the name of the file set to create or add to. It must contain only letters, numbers and underscores. Names starting with a capital letter are reserved for built-in file sets predefined by CMake. The only predefined set names are those matching the acceptable types. All other set names must not start with a capital letter or underscore. Read the fine print, bestie. ๐Ÿ“
  • TYPE <type> โ€” every file set is associated with a particular type of file. Only the types specified above may be used, and it is an error to specify anything else. As a special case, if the name of the file set is one of the types, the type does not need to be specified and the TYPE <type> arguments can be omitted. For all other file set names, TYPE is required. ๐Ÿซถ
  • BASE_DIRS <dirs>... โ€” an optional list of base directories of the file set. Any relative path is treated as relative to the current source directory (i.e. CMAKE_CURRENT_SOURCE_DIR). If no BASE_DIRS are specified when the file set is first created, the value of CMAKE_CURRENT_SOURCE_DIR is added. This argument supports generator expressions. ๐ŸŒŸ

Like, no two base directories for a file set may be sub-directories of each other โ€” periodt, that's the rule. And bestie, this requirement has to hold across all base directories added to a file set, not just the ones you threw in during a single call to target_sources(). The whole file set. Every time. ๐Ÿ’…

  • FILES <files>... โ€” an optional list of files to add to the file set. Every single file has to live in one of the base directories, or in a subdirectory of one of them. This argument supports generator expressions <#manual:cmake-generator-expressions(7)> โœจ

Okay so if you give relative paths, they're considered relative to CMAKE_CURRENT_SOURCE_DIR <#variable:CMAKE_CURRENT_SOURCE_DIR> at the moment target_sources() gets called. The one exception? A path starting with $< โ€” those get treated as relative to the target's source directory, after the generator expressions have been evaluated. iykyk ๐Ÿซถ


The following target properties are set by target_sources(FILE_SET), but like... don't generally touch them directly, bestie. They're doing their own thing. ๐Ÿ™ˆ

For file sets of type HEADERS:

  • HEADER_SETS <#prop_tgt:HEADER_SETS>
  • INTERFACE_HEADER_SETS <#prop_tgt:INTERFACE_HEADER_SETS>
  • HEADER_SET <#prop_tgt:HEADER_SET>
  • HEADER_SET_<NAME> <#prop_tgt:HEADER_SET_<NAME>>
  • HEADER_DIRS <#prop_tgt:HEADER_DIRS>
  • HEADER_DIRS_<NAME> <#prop_tgt:HEADER_DIRS_<NAME>>

For file sets of type SOURCES:

  • SOURCE_SETS <#prop_tgt:SOURCE_SETS>
  • INTERFACE_SOURCE_SETS <#prop_tgt:INTERFACE_SOURCE_SETS>
  • SOURCE_SET <#prop_tgt:SOURCE_SET>
  • SOURCE_SET_<NAME> <#prop_tgt:SOURCE_SET_<NAME>>
  • SOURCE_DIRS <#prop_tgt:SOURCE_DIRS>
  • SOURCE_DIRS_<NAME> <#prop_tgt:SOURCE_DIRS_<NAME>>

For file sets of type CXX_MODULES:

  • CXX_MODULE_SETS <#prop_tgt:CXX_MODULE_SETS>
  • INTERFACE_CXX_MODULE_SETS <#prop_tgt:INTERFACE_CXX_MODULE_SETS>
  • CXX_MODULE_SET <#prop_tgt:CXX_MODULE_SET>
  • CXX_MODULE_SET_<NAME> <#prop_tgt:CXX_MODULE_SET_<NAME>>
  • CXX_MODULE_DIRS <#prop_tgt:CXX_MODULE_DIRS>
  • CXX_MODULE_DIRS_<NAME> <#prop_tgt:CXX_MODULE_DIRS_<NAME>>

Target properties related to include directories also get modified by target_sources(FILE_SET), and here's exactly how ๐Ÿ‘‡

  • INCLUDE_DIRECTORIES <#prop_tgt:INCLUDE_DIRECTORIES> โ€” if the TYPE is HEADERS, and the scope of the file set is PRIVATE or PUBLIC, then all of the BASE_DIRS of the file set get wrapped in $<BUILD_INTERFACE> <#genex:BUILD_INTERFACE> and appended to this property. Slay. ๐Ÿ’–
  • INTERFACE_INCLUDE_DIRECTORIES <#prop_tgt:INTERFACE_INCLUDE_DIRECTORIES> โ€” if the TYPE is HEADERS, and the scope of the file set is INTERFACE or PUBLIC, then all of the BASE_DIRS of the file set get wrapped in $<BUILD_INTERFACE> <#genex:BUILD_INTERFACE> and appended to this property. Same energy, different property. โœจ

๐Ÿฆ‹ See Also ๐Ÿฆ‹

  • add_executable() <#command:add_executable>
  • add_library() <#command:add_library>
  • target_compile_definitions() <#command:target_compile_definitions>
  • target_compile_features() <#command:target_compile_features>
  • target_compile_options() <#command:target_compile_options>
  • target_include_directories() <#command:target_include_directories>
  • target_link_libraries() <#command:target_link_libraries>
  • target_link_directories() <#command:target_link_directories>
  • target_link_options() <#command:target_link_options>
  • target_precompile_headers() <#command:target_precompile_headers>

๐Ÿงš try_compile ๐Ÿงš

Try building some code. That's literally it. That's the vibe check. ๐Ÿ”ฅ

๐Ÿ’œ Try Compiling Whole Projects ๐Ÿ’œ

try_compile(<compileResultVar> PROJECT <projectName>
            SOURCE_DIR <srcdir>
            [BINARY_DIR <bindir>]
            [TARGET <targetName>]
            [LOG_DESCRIPTION <text>]
            [NO_CACHE]
            [NO_LOG]
            [CMAKE_FLAGS <flags>...]
            [OUTPUT_VARIABLE <var>])

Added in version 3.25.

Try building a project, bestie. Build success returns TRUE and build failure returns FALSE in <compileResultVar>. Iconic behaviour ngl. โœ…โŒ

In this form, <srcdir> should contain a complete CMake project with a CMakeLists.txt file and all sources. Heads up: the <bindir> and <srcdir> will not be deleted after this command runs โ€” they stick around. ๐Ÿ‘€ Specify <targetName> to build a specific target instead of the all or ALL_BUILD target. Scroll down for what the other options mean.

Changed in version 3.24: CMake variables describing platform settings, plus those listed by the CMAKE_TRY_COMPILE_PLATFORM_VARIABLES <#variable:CMAKE_TRY_COMPILE_PLATFORM_VARIABLES> variable, are propagated into the project's build configuration. See policy CMP0137 <#policy:CMP0137>. Before this, that only happened via the source file signature. glow-up fr ๐Ÿ’…

Added in version 3.26: This command records a configure-log try_compile event <#try-compile-configure-log-event> if the NO_LOG option is not specified.

Added in version 3.30: If the PROPAGATE_TOP_LEVEL_INCLUDES_TO_TRY_COMPILE <#prop_gbl:PROPAGATE_TOP_LEVEL_INCLUDES_TO_TRY_COMPILE> global property is set to true, then CMAKE_PROJECT_TOP_LEVEL_INCLUDES <#variable:CMAKE_PROJECT_TOP_LEVEL_INCLUDES> is propagated into the project's build configuration. Spreading the love ๐Ÿซถ

This command supports an alternate signature for CMake older than 3.25. The signature above is recommended for clarity, bestie โ€” trust.

try_compile(<compileResultVar> <bindir> <srcdir>
            <projectName> [<targetName>]
            [CMAKE_FLAGS <flags>...]
            [OUTPUT_VARIABLE <var>])

๐ŸŒธ Try Compiling Source Files ๐ŸŒธ

try_compile(<compileResultVar>
            [SOURCES_TYPE <type>]
            {SOURCES <srcfile>...                 |
             SOURCE_FROM_CONTENT <name> <content> |
             SOURCE_FROM_VAR <name> <var>         |
             SOURCE_FROM_FILE <name> <path>       }...
            [LOG_DESCRIPTION <text>]
            [NO_CACHE]
            [NO_LOG]
            [CMAKE_FLAGS <flags>...]
            [COMPILE_DEFINITIONS <defs>...]
            [LINK_OPTIONS <options>...]
            [LINK_LIBRARIES <libs>...]
            [LINKER_LANGUAGE <lang>]
            [OUTPUT_VARIABLE <var>]
            [COPY_FILE <fileName> [COPY_FILE_ERROR <var>]]
            [<LANG>_STANDARD <std>]
            [<LANG>_STANDARD_REQUIRED <bool>]
            [<LANG>_EXTENSIONS <bool>]
            )

Added in version 3.25.

Try building an executable or static library from one or more source files. The binary type is determined by the CMAKE_TRY_COMPILE_TARGET_TYPE <#variable:CMAKE_TRY_COMPILE_TARGET_TYPE> variable. Build success returns boolean true and build failure returns boolean false in <compileResultVar> (cached unless NO_CACHE is specified). ๐ŸŽ€

In this form, you gotta provide one or more source files โ€” non-negotiable. And also, one of SOURCES and/or SOURCE_FROM_* must come before the other keywords. Order matters, bestie. ๐Ÿ“‹

If CMAKE_TRY_COMPILE_TARGET_TYPE <#variable:CMAKE_TRY_COMPILE_TARGET_TYPE> is unset or is set to EXECUTABLE, then the sources must include a definition for main and CMake will create a CMakeLists.txt file to build the source(s) as an executable. If CMAKE_TRY_COMPILE_TARGET_TYPE <#variable:CMAKE_TRY_COMPILE_TARGET_TYPE> is set to STATIC_LIBRARY, then a static library gets built instead and no main definition is required. For an executable, the generated CMakeLists.txt file would contain something like this:

add_definitions(<expanded COMPILE_DEFINITIONS from caller>)
include_directories(${INCLUDE_DIRECTORIES})
link_directories(${LINK_DIRECTORIES})
add_executable(cmTryCompileExec <srcfile>...)
target_link_options(cmTryCompileExec PRIVATE <LINK_OPTIONS from caller>)
target_link_libraries(cmTryCompileExec ${LINK_LIBRARIES})

CMake automatically generates, for each try_compile operation, a unique directory under ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeScratch with an unspecified name. Those directories get cleaned automatically โ€” unless --debug-trycompile <#cmdoption-cmake-debug-trycompile> is passed to cmake. And directories left over from previous runs are also unconditionally cleaned at the beginning of any cmake execution. No clutter, we love to see it. ๐Ÿงนโœจ

This command supports an alternate signature for CMake older than 3.25. Signature above is recommended for clarity, as always.

try_compile(<compileResultVar> <bindir> {<srcfile>|SOURCES <srcfile>...}
            [CMAKE_FLAGS <flags>...]
            [COMPILE_DEFINITIONS <defs>...]
            [LINK_OPTIONS <options>...]
            [LINK_LIBRARIES <libs>...]
            [OUTPUT_VARIABLE <var>]
            [COPY_FILE <fileName> [COPY_FILE_ERROR <var>]]
            [<LANG>_STANDARD <std>]
            [<LANG>_STANDARD_REQUIRED <bool>]
            [<LANG>_EXTENSIONS <bool>]
            )

In this version, try_compile will use <bindir>/CMakeFiles/CMakeTmp for its operation, and all such files get cleaned automatically. For debugging, --debug-trycompile <#cmdoption-cmake-debug-trycompile> can be passed to cmake to avoid this clean. But โ€” and this is the catch ๐Ÿ˜ญ โ€” multiple sequential try_compile operations, if given the same <bindir>, will reuse this single output directory, such that you can only debug one such try_compile call at a time. So the newer signature is recommended to simplify debugging of multiple try_compile operations. Fr fr, save yourself the pain.

๐Ÿ“ Options ๐Ÿ“

The options for the above signatures are:

  • CMAKE_FLAGS <flags>... โ€” specify flags of the form -DVAR:TYPE=VALUE <#cmdoption-cmake-D> to be passed to the cmake(1) <#manual:cmake(1)> command-line used to drive the test build. The example up there shows how values for variables COMPILE_DEFINITIONS, INCLUDE_DIRECTORIES, LINK_DIRECTORIES, LINK_LIBRARIES, and LINK_OPTIONS are used. Compiler options can be passed in like CMAKE_FLAGS -DCOMPILE_DEFINITIONS=-Werror. ๐Ÿ‘‘
  • COMPILE_DEFINITIONS <defs>... โ€” specify -Ddefinition arguments to pass to add_definitions() <#command:add_definitions> in the generated test project.
  • COPY_FILE <fileName> โ€” copy the built executable or static library to the given <fileName>. Move it, girl. ๐Ÿ“ฆ
  • COPY_FILE_ERROR <var> โ€” use after COPY_FILE to capture into variable <var> any error message encountered while trying to copy the file. So if the copy flops, you'll know. ๐Ÿ’€
  • LINK_LIBRARIES <libs>... โ€” specify libraries to be linked in the generated project. The list of libraries may refer to system libraries and to Imported Targets <#imported-targets> from the calling project.

If this option is specified, any -DLINK_LIBRARIES=... value given to the CMAKE_FLAGS option will be ignored. Sorry bestie, this one wins. ๐Ÿ™ˆ

Added in version 3.29: Alias targets to imported libraries are also supported. Periodt. โœจ

  • LINK_OPTIONS <options>... โ€” Added in version 3.14.
  • (the link-step-options one โ€” picking up right where we left off ๐Ÿ’…) โ€” specify link step options to pass to target_link_options() or to set the STATIC_LIBRARY_OPTIONS target property in the generated project, depending on the CMAKE_TRY_COMPILE_TARGET_TYPE variable. iykyk, that variable is literally deciding the whole vibe here.
  • LINKER_LANGUAGE <lang> โ€” Added in version 3.29. Sets the LINKER_LANGUAGE target property on the generated project. Okay so bestie, if you're mixing source files from different languages, set this to the language of the source file that holds the program entry point โ€” like, main. That's the one calling the shots. ๐Ÿ‘‘
  • LOG_DESCRIPTION <text> โ€” Added in version 3.26. Gives a non-empty text description of what the check is even for. It gets recorded in the cmake-configure-log(7) entry. Lowkey your future self will thank you.
  • NO_CACHE โ€” Added in version 3.25. So <compileResultVar> gets stored in a normal variable instead of a cache entry. Why does that matter? Because <compileResultVar> is normally cached, which lets you use this super clean pattern to dodge re-running the test every single time CMake executes:
if(NOT DEFINED RESULTVAR)
  # ...(check-specific setup code)...
  try_compile(RESULTVAR ...)
  # ...(check-specific logging and cleanup code)...
endif()

But if the guard variable and the result variable are not the same โ€” say the test is part of some bigger inspection โ€” then NO_CACHE is your girl, because it stops that intermediate result variable from leaking into the cache. no cap, that's the whole point. ๐Ÿซถ

  • NO_LOG โ€” Added in version 3.26. Do not record a cmake-configure-log(7) entry for this call. That's it. That's the whole vibe. ๐Ÿ™ˆ
  • OUTPUT_VARIABLE <var> โ€” stores the output from the build process into the variable you name. โœจ
  • SOURCE_FROM_CONTENT <name> <content> โ€” Added in version 3.25. Writes <content> to a file named <name> inside the operation directory. The glow-up here: you can skip writing a separate source file when the file's contents are dynamically specified. The specified <name> is not allowed to contain path components, so keep it simple. SOURCE_FROM_CONTENT may be specified multiple times. ๐Ÿ’–
  • SOURCE_FROM_FILE <name> <path> โ€” Added in version 3.25. Copies <path> to a file named <name> in the operation directory. This consolidates files into the operation directory, which is genuinely useful if a source that already exists (i.e. as a stand-alone file in a project's source repository) needs to refer to other file(s) created by SOURCE_FROM_*. (Otherwise? SOURCES is usually more convenient โ€” fr fr.) The specified <name> is not allowed to contain path components.
  • SOURCE_FROM_VAR <name> <var> โ€” Added in version 3.25. Writes the contents of <var> to a file named <name> in the operation directory. It's the same as SOURCE_FROM_CONTENT, except it grabs the contents from the specified CMake variable instead of directly โ€” which slays when you're passing arguments through a function that wraps try_compile. The specified <name> is not allowed to contain path components. SOURCE_FROM_VAR may be specified multiple times. ๐ŸŽ€
  • SOURCES_TYPE <type> โ€” Added in version 3.28. Classify your sources with this argument. Once you specify it, all subsequent sources get treated as that type until another SOURCES_TYPE shows up. Available types are:
    • NORMAL โ€” sources are not added to any FILE_SET in the generated project.
    • CXX_MODULE โ€” Added in version 3.28. sources are added to a FILE_SET of type CXX_MODULES in the generated project.

The default type of sources is NORMAL. โญ

  • <LANG>_STANDARD <std> โ€” Added in version 3.8. Specifies the C_STANDARD, CXX_STANDARD, OBJC_STANDARD, OBJCXX_STANDARD, or CUDA_STANDARD target property of the generated project. pick your language, pick your standard, go off ๐Ÿ’…
  • <LANG>_STANDARD_REQUIRED <bool> โ€” Added in version 3.8. Specifies the C_STANDARD_REQUIRED, CXX_STANDARD_REQUIRED, OBJC_STANDARD_REQUIRED, OBJCXX_STANDARD_REQUIRED, or CUDA_STANDARD_REQUIRED target property of the generated project.
  • <LANG>_EXTENSIONS <bool> โ€” Added in version 3.8. Specifies the C_EXTENSIONS, CXX_EXTENSIONS, OBJC_EXTENSIONS, OBJCXX_EXTENSIONS, or CUDA_EXTENSIONS target property of the generated project.

๐Ÿฆ‹ Other Behavior Settings ๐Ÿฆ‹

Added in version 3.4: If set, these variables get passed into the generated try_compile CMakeLists.txt so they can initialize compile target properties with default values:

  • CMAKE_CUDA_RUNTIME_LIBRARY
  • CMAKE_ENABLE_EXPORTS
  • CMAKE_EXECUTABLE_ENABLE_EXPORTS
  • CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS
  • CMAKE_EXE_LINKER_FLAGS โ€” unless using CMake versions prior to 4.0 without policy CMP0056 set to NEW
  • CMAKE_<LANG>_LINK_FLAGS โ€” unless using CMake versions prior to 4.3 without policy CMP0210 set to NEW
  • CMAKE_LINK_SEARCH_START_STATIC
  • CMAKE_LINK_SEARCH_END_STATIC
  • CMAKE_MSVC_RUNTIME_LIBRARY
  • CMAKE_POSITION_INDEPENDENT_CODE
  • CMAKE_WATCOM_RUNTIME_LIBRARY

Changed in version 3.14: if CMP0083 is set to NEW, then to get correct behavior at link time, the check_pie_supported() command from the CheckPIESupported module must be called before using the try_compile command. Like, mandatory mandatory. ๐Ÿ˜ญ

Some policies get set automatically in the generated test project as needed, so the calling project's state is honored:

  • CMP0065 (in CMake versions prior to 4.0)
  • CMP0083
  • CMP0091
  • CMP0104
  • CMP0123
  • CMP0126
  • CMP0128
  • CMP0136
  • CMP0141
  • CMP0155
  • CMP0157
  • CMP0181
  • CMP0184
  • CMP0210

Set the variable CMAKE_TRY_COMPILE_CONFIGURATION to choose a build configuration:

  • For multi-config generators, this selects which configuration to build. ๐Ÿ”ฅ
  • For single-config generators, this sets CMAKE_BUILD_TYPE in the test project.

Added in version 3.6: set the CMAKE_TRY_COMPILE_TARGET_TYPE variable to specify the type of target used for the source file signature.

Added in version 3.6: set the CMAKE_TRY_COMPILE_PLATFORM_VARIABLES variable to specify variables that must be propagated into the test project. Heads up bestie โ€” this variable is meant for use only in toolchain files, and it's only honored by the try_compile() command for the source files form, not when given a whole project. โš ๏ธ

Changed in version 3.8: if CMP0067 is set to NEW, or any of the <LANG>_STANDARD, <LANG>_STANDARD_REQUIRED, or <LANG>_EXTENSIONS options are used, then these language standard variables are honored:

  • CMAKE_C_STANDARD
  • CMAKE_C_STANDARD_REQUIRED
  • CMAKE_C_EXTENSIONS
  • CMAKE_CXX_STANDARD
  • CMAKE_CXX_STANDARD_REQUIRED
  • CMAKE_CXX_EXTENSIONS
  • CMAKE_OBJC_STANDARD
  • CMAKE_OBJC_STANDARD_REQUIRED
  • CMAKE_OBJC_EXTENSIONS
  • CMAKE_OBJCXX_STANDARD
  • CMAKE_OBJCXX_STANDARD_REQUIRED
  • CMAKE_OBJCXX_EXTENSIONS
  • CMAKE_CUDA_STANDARD
  • CMAKE_CUDA_STANDARD_REQUIRED
  • CMAKE_CUDA_EXTENSIONS

Their values are used to set the corresponding target properties in the generated project โ€” unless an explicit option overrides them, obviously. ๐Ÿ’…

Changed in version 3.14: for the Green Hills MULTI generator, the GHS toolset and target system customization cache variables are also propagated into the test project.

Added in version 3.24: the CMAKE_TRY_COMPILE_NO_PLATFORM_VARIABLES variable may be set to disable passing platform variables into the test project. Off switch: acquired. ๐Ÿ™…โ€โ™€๏ธ

Added in version 3.25: if CMP0141 is set to NEW, you can use CMAKE_MSVC_DEBUG_INFORMATION_FORMAT to specify the MSVC debug information format.

Added in version 3.30: if the PROPAGATE_TOP_LEVEL_INCLUDES_TO_TRY_COMPILE global property is set to true, CMAKE_PROJECT_TOP_LEVEL_INCLUDES is propagated into the test project's build configuration when using the whole-project signature. ๐ŸŒˆ

Added in version 4.0: if CMP0184 is set to NEW, you can use CMAKE_MSVC_RUNTIME_CHECKS to specify the enabled MSVC runtime checks. ๐Ÿซถ

Added in version 4.3: if CMP0210 is set to NEW, then CMAKE_<LANG>_LINK_FLAGS and CMAKE_<LANG>_LINK_FLAGS_<CONFIG> get propagated right into the test project's build configuration. slay, flags traveling ๐Ÿ’…

See Also

  • try_run() ๐Ÿ’–

๐Ÿƒโœจ try_run

Try compiling and then running some code. iconic behavior honestly ๐ŸŽ€

Compiling and Running Source Files

try_run(<runResultVar> <compileResultVar>
        [SOURCES_TYPE <type>]
        {SOURCES <srcfile>...                 |
         SOURCE_FROM_CONTENT <name> <content> |
         SOURCE_FROM_VAR <name> <var>         |
         SOURCE_FROM_FILE <name> <path>       }...
        [LOG_DESCRIPTION <text>]
        [NO_CACHE]
        [NO_LOG]
        [CMAKE_FLAGS <flags>...]
        [COMPILE_DEFINITIONS <defs>...]
        [LINK_OPTIONS <options>...]
        [LINK_LIBRARIES <libs>...]
        [COMPILE_OUTPUT_VARIABLE <var>]
        [COPY_FILE <fileName> [COPY_FILE_ERROR <var>]]
        [<LANG>_STANDARD <std>]
        [<LANG>_STANDARD_REQUIRED <bool>]
        [<LANG>_EXTENSIONS <bool>]
        [RUN_OUTPUT_VARIABLE <var>]
        [RUN_OUTPUT_STDOUT_VARIABLE <var>]
        [RUN_OUTPUT_STDERR_VARIABLE <var>]
        [WORKING_DIRECTORY <var>]
        [ARGS <args>...]
        )

Added in version 3.25.

Okay so bestie, this one builds an executable out of one or more source files. Build success? You get boolean true in <compileResultVar> (cached unless you pass NO_CACHE). Build failure? Boolean false, no drama. ๐Ÿ’€ And if the build does succeed, it runs the executable and stashes the exit code in <runResultVar> (again, cached unless NO_CACHE is in the mix). But if the thing got built and then refused to run โ€” rude โ€” then <runResultVar> gets set to FAILED_TO_RUN. ๐Ÿ˜ญ

Peep try_compile() for the docs on all the options these two commands share, plus the whole "how is the test project even constructed to build the source file" lore. ๐Ÿฆ‹

You gotta give it one or more source files. Also, one of SOURCES and/or SOURCE_FROM_* has to come before the other keywords. order matters, periodt โœจ

Added in version 3.26: this command records a configure-log try_run event if you don't pass the NO_LOG option. ๐Ÿ“

There's also an alternate signature for CMake older than 3.25. The one up above is the recommended one, for clarity reasons ๐Ÿ‘‘

try_run(<runResultVar> <compileResultVar>
        <bindir> {<srcfile>|SOURCES <srcfile>...}
        [CMAKE_FLAGS <flags>...]
        [COMPILE_DEFINITIONS <defs>...]
        [LINK_OPTIONS <options>...]
        [LINK_LIBRARIES <libs>...]
        [LINKER_LANGUAGE <lang>]
        [COMPILE_OUTPUT_VARIABLE <var>]
        [COPY_FILE <fileName> [COPY_FILE_ERROR <var>]]
        [<LANG>_STANDARD <std>]
        [<LANG>_STANDARD_REQUIRED <bool>]
        [<LANG>_EXTENSIONS <bool>]
        [RUN_OUTPUT_VARIABLE <var>]
        [OUTPUT_VARIABLE <var>]
        [WORKING_DIRECTORY <var>]
        [ARGS <args>...]
        )

๐ŸŒธ Options ๐ŸŒธ

These are the options that are specifically try_run's whole thing:

  • COMPILE_OUTPUT_VARIABLE <var> โ€” drops the build output from the compile step into the variable you name. ๐Ÿซถ
  • OUTPUT_VARIABLE <var> โ€” drops the compile build output and the output from running the executable into your variable. ngl this one exists for legacy reasons and the old try_run signature is the only one that supports it. Prefer COMPILE_OUTPUT_VARIABLE and RUN_OUTPUT_VARIABLE instead, bestie. ๐Ÿ™ˆ
  • RUN_OUTPUT_VARIABLE <var> โ€” drops the output from running the executable into your variable. โœจ
  • RUN_OUTPUT_STDOUT_VARIABLE <var> โ€” added in version 3.25. drops the stdout from running the executable into your variable. ๐Ÿ’–
  • RUN_OUTPUT_STDERR_VARIABLE <var> โ€” added in version 3.25. drops the stderr from running the executable into your variable. ๐Ÿ’…
  • WORKING_DIRECTORY <var> โ€” added in version 3.20. runs the executable in the directory you give it. No WORKING_DIRECTORY? Then the executable runs in <bindir> or the current build directory. ๐ŸŽ€
  • ARGS <args>... โ€” extra arguments to hand the executable when you run it. ๐Ÿ“

๐ŸŽ›๏ธ Other Behavior Settings ๐ŸŽ›๏ธ

Set the variable CMAKE_TRY_COMPILE_CONFIGURATION to pick a build configuration:

  • For multi-config generators, this selects which configuration to build. ๐Ÿ”ฅ
  • For single-config generators, this sets CMAKE_BUILD_TYPE inside the test project. ๐ŸŒˆ

๐ŸŒ Behavior when Cross Compiling ๐ŸŒ

Added in version 3.3: it uses CMAKE_CROSSCOMPILING_EMULATOR when running cross-compiled binaries. ๐Ÿงš

So here's the situation: when you're cross compiling, the executable built in the first step usually cannot run on the build host. Obviously. ๐Ÿ˜ญ So try_run peeks at the CMAKE_CROSSCOMPILING variable to figure out whether CMake is in cross-compiling mode. If it is, it'll still try to compile the executable โ€” but it will not try to run it unless the CMAKE_CROSSCOMPILING_EMULATOR variable is set. Instead, it creates cache variables that the user has to fill in (or preset in some CMake script file) with the values the executable would have produced if it had actually run on its real target platform. manifesting the output, basically โœจ

These are the cache entries:

  • <runResultVar> โ€” the exit code, if the executable were to be run on the target platform. ๐Ÿ‘‘
  • <runResultVar>__TRYRUN_OUTPUT โ€” the output from stdout and stderr, if the executable were to be run on the target platform. Only created if the RUN_OUTPUT_VARIABLE or OUTPUT_VARIABLE option was used. ๐Ÿ’œ
  • <runResultVar>__TRYRUN_OUTPUT_STDOUT โ€” added in version 3.25. the output from stdout, if the executable were to be run on the target platform. Only created if the RUN_OUTPUT_STDOUT_VARIABLE or RUN_OUTPUT_STDERR_VARIABLE option was used. ๐Ÿฆ‹
  • <runResultVar>__TRYRUN_OUTPUT_STDERR โ€” added in version 3.25. the output from stderr, if the executable were to be run on the target platform. Only created if the RUN_OUTPUT_STDOUT_VARIABLE or RUN_OUTPUT_STDERR_VARIABLE option was used. ๐Ÿฆ‹
Bestie, real talk: to make cross compiling your project easier, only reach for try_run if you genuinely need it. And if you do use try_run, only use the RUN_OUTPUT_STDOUT_VARIABLE, RUN_OUTPUT_STDERR_VARIABLE, RUN_OUTPUT_VARIABLE or OUTPUT_VARIABLE options if you really need those too โ€” because using them means that, when cross-compiling, somebody has to set those cache variables by hand to whatever the executable would have output. ๐Ÿ˜ญ You can also "guard" your try_run calls with an if() block that checks the CMAKE_CROSSCOMPILING variable and hands over an easy-to-preset alternative for that case. chef's kiss workaround ๐Ÿ’…

๐Ÿงชโœจ CTEST COMMANDS โœจ๐Ÿงช

These commands are available only in CTest scripts. no cap ๐ŸŽ€

๐Ÿ”จ ctest_build

Performs the CTest Build Step as a Dashboard Client. main character energy ๐Ÿ‘‘

ctest_build([BUILD <build-dir>] [APPEND]
            [CONFIGURATION <config>]
            [PARALLEL_LEVEL <parallel>]
            [FLAGS <flags>]
            [PROJECT_NAME <project-name>]
            [TARGET <target-name>]
            [NUMBER_ERRORS <num-err-var>]
            [NUMBER_WARNINGS <num-warn-var>]
            [RETURN_VALUE <result-var>]
            [CAPTURE_CMAKE_ERROR <result-var>]
            [PRESET <preset>]
            [PRESETS_FILE <file>]
            )

Builds the project and stores the results in Build.xml so you can submit it with the ctest_submit() command. ๐Ÿ“ฆ

The CTEST_BUILD_COMMAND variable can be set to explicitly spell out the build command line when no preset is selected. Otherwise the build command line just gets computed automatically from whatever options you gave. โœจ

The options are:

  • BUILD <build-dir> โ€” specifies the top-level build directory. Not given? Then the CTEST_BINARY_DIRECTORY variable gets used. ๐Ÿซถ
  • APPEND โ€” marks Build.xml for append to results previously submitted to a dashboard server since the last ctest_start() call. The append semantics are defined by whatever dashboard server you're using, so iykyk. And heads up: this does not cause results to be appended to a .xml file produced by an earlier call to this command. ๐Ÿฉท
  • CONFIGURATION <config> โ€” specifies the build configuration (e.g. Debug). If you don't specify it, the CTEST_BUILD_CONFIGURATION variable gets checked. Otherwise the -C <cfg> option given to the ctest(1) command is used, if there is one. ๐ŸŽ€
  • PARALLEL_LEVEL <parallel> โ€” added in version 3.21. specifies the parallel level of the underlying build system. If you don't specify it, the CMAKE_BUILD_PARALLEL_LEVEL environment variable gets checked. โšก
  • FLAGS <flags> โ€” passes extra arguments to the underlying build command. If not specified, the CTEST_BUILD_FLAGS variable gets checked. This can, e.g., be used to trigger a parallel build using the -j option of make. See the ProcessorCount module for an example. ๐Ÿ”ฅ
  • PROJECT_NAME <project-name> โ€” ignored since CMake 3.0. Changed in version 3.14: this value is no longer required. ๐Ÿ’€
  • TARGET <target-name> โ€” specifies the name of a target to build. If not specified, the CTEST_BUILD_TARGET variable gets checked. Otherwise the default target gets built โ€” that's the all target (which goes by ALL_BUILD in Visual Studio Generators). ๐Ÿ‘‘
  • NUMBER_ERRORS <num-err-var> โ€” stores the number of build errors detected in the given variable. ๐Ÿ˜ญ
  • NUMBER_WARNINGS <num-warn-var> โ€” stores the number of build warnings detected in the given variable. ๐Ÿ™ˆ
  • PRESET <preset> โ€” added in version 4.4. specifies a preset to use when building the project. Anything you set in the CTest script takes priority over the corresponding setting from the preset โ€” so, for example, the TARGET argument overrides the targets setting from the chosen preset. slay, precedence ๐Ÿ’…

When a preset is specified, the CTEST_BUILD_COMMAND variable is ignored so that the preset is always honored. Like, the preset eats first, no negotiating ๐Ÿ’…

See also the CTEST_BUILD_PRESET and CTEST_PRESET variables.

  • PRESETS_FILE <file> โ€” โœจ Added in version 4.4. โœจ Pick a specific presets file to use instead of the default CMakePresets.json that lives in the source directory. If you hand it a relative path, it's interpreted relative to the source directory. Heads up: this does nothing unless a preset is actually selected โ€” either via the PRESET argument, or the CTEST_BUILD_PRESET / CTEST_PRESET variables. See also the CTEST_PRESETS_FILE variable ๐Ÿซถ
  • RETURN_VALUE <result-var> โ€” Drops the return value of the native build tool into the variable you name. Receipts, bestie ๐Ÿงพ
  • CAPTURE_CMAKE_ERROR <result-var> โ€” Added in version 3.7. Stores -1 in <result-var> if literally any errors happen while running the command, and stops ctest from returning non-zero when an error occurs. Very "I'll handle it, don't panic" energy ๐Ÿ’€
  • QUIET โ€” Added in version 3.3. Mutes all the CTest-specific non-error output that would've been printed to your console otherwise. But โ€” and this is the important part โ€” the summary of warnings / errors stays, and the output from the native build tool is completely unaffected. You can't silence those, sorry babe ๐ŸŒธ

ctest_configure</p>

Does the whole CTest Configure Step as a Dashboard Client โœจ

ctest_configure([BUILD <build-dir>] [SOURCE <source-dir>] [APPEND]
                [OPTIONS <options>] [RETURN_VALUE <result-var>] [QUIET]
                [CAPTURE_CMAKE_ERROR <result-var>]
                [PRESET <preset>]
                [PRESETS_FILE <file>]
                )

Configures the project build tree and records the results in Configure.xml so you can submit them with the ctest_submit() command ๐ŸŽ€

The options, one by one:

  • BUILD <build-dir> โ€” Sets the top-level build directory. Don't pass it and the CTEST_BINARY_DIRECTORY variable gets used instead.
  • SOURCE <source-dir> โ€” Sets the source directory. Skip it and CTEST_SOURCE_DIRECTORY holds the wheel ๐Ÿ™ˆ
  • APPEND โ€” Marks Configure.xml for append onto results that were previously submitted to a dashboard server since the last ctest_start() call. What "append" actually means is up to whichever dashboard server you're using โ€” your server, your rules. Also, just to be clear: this does not append to a .xml file produced by a previous call to this command. Different thing entirely โญ
  • OPTIONS <options> โ€” A semicolon-separated list of command-line arguments to pass to the configuration tool. Fair warning: this option gets straight-up ignored when CTEST_CONFIGURE_COMMAND is used ๐Ÿ’…
  • PRESET <preset> โ€” Added in version 4.4. Names a preset to use when configuring the project. Anything you set in the CTest script wins over the matching setting from the preset โ€” for example, the CTEST_BINARY_DIRECTORY variable overrides the binaryDir setting from the chosen preset. When a preset is specified, the CTEST_CONFIGURE_COMMAND variable is ignored so the preset is always honored. See also the CTEST_CONFIGURE_PRESET and CTEST_PRESET variables ๐Ÿ‘‘
  • PRESETS_FILE <file> โ€” Added in version 4.4. Points at a presets file to use instead of the default CMakePresets.json in the source directory. Relative paths are interpreted relative to the source directory. Does nothing at all unless a preset is selected via the PRESET argument or the CTEST_CONFIGURE_PRESET / CTEST_PRESET variables. See also the CTEST_PRESETS_FILE variable ๐Ÿฆ‹
  • RETURN_VALUE <result-var> โ€” Stores the return value of the native configuration tool in the variable you name.
  • CAPTURE_CMAKE_ERROR <result-var> โ€” Added in version 3.7. Stores -1 in <result-var> if there are any errors running the command, and keeps ctest(1) from returning non-zero if an error occurs ๐Ÿซถ
  • QUIET โ€” Added in version 3.3. Suppresses any CTest-specific non-error messages that would've been printed to the console. The output from the underlying configure command is not affected โ€” that one's still gonna talk ๐ŸŒˆ

ctest_coverage</p>

Performs the CTest Coverage Step as a Dashboard Client, no cap ๐Ÿ’–

ctest_coverage([BUILD <build-dir>] [APPEND]
               [LABELS <label>...]
               [RETURN_VALUE <result-var>]
               [CAPTURE_CMAKE_ERROR <result-var>]
               [QUIET]
               )

Collects the coverage tool results and stores them in Coverage.xml ready for submission with the ctest_submit() command.

The options:

  • BUILD <build-dir> โ€” The top-level build directory. Not given? Then CTEST_BINARY_DIRECTORY steps in as the default ๐ŸŽ€
  • APPEND โ€” Marks Coverage.xml for append onto results previously submitted to a dashboard server since the last ctest_start() call. The append semantics themselves are defined by whatever dashboard server is in use. And again, this does not cause results to be appended to a .xml file produced by a previous call to this command ๐Ÿ™…
  • LABELS <label>... โ€” Filters the coverage report so it only includes source files labeled with at least one of the labels you list. Picky but make it fashion ๐Ÿ’…
  • RETURN_VALUE <result-var> โ€” Stores 0 in <result-var> if the coverage tools ran without error, and non-zero otherwise. Clean run = zero, chaos = not zero โญ
  • CAPTURE_CMAKE_ERROR <result-var> โ€” Added in version 3.7. Stores -1 in <result-var> if there are any errors running the command, and prevents ctest(1) from returning non-zero when an error occurs ๐Ÿซถ
  • QUIET โ€” Added in version 3.3. Suppresses any CTest-specific non-error output that would've hit the console otherwise. The summary showing how many lines of code were covered is not affected by this โ€” that part still shows up, queen ๐Ÿ‘‘

ctest_empty_binary_directory</p>

Literally empties the binary directory ๐Ÿ‘€

ctest_empty_binary_directory(<directory>)

Removes a binary directory. And before you panic โ€” this command runs some checks before it deletes anything, specifically to avoid malicious or accidental directory deletion ๐Ÿ˜ญโœจ Safety first, always.


ctest_memcheck</p>

Performs the CTest MemCheck Step as a Dashboard Client ๐Ÿงš

ctest_memcheck([BUILD <build-dir>] [APPEND]
               [START <start-number>]
               [END <end-number>]
               [STRIDE <stride-number>]
               [EXCLUDE <exclude-regex>]
               [INCLUDE <include-regex>]
               [EXCLUDE_LABEL <label-exclude-regex>]
               [INCLUDE_LABEL <label-include-regex>]
               [EXCLUDE_FIXTURE <regex>]
               [EXCLUDE_FIXTURE_SETUP <regex>]
               [EXCLUDE_FIXTURE_CLEANUP <regex>]
               [PARALLEL_LEVEL <level>]
               [RESOURCE_SPEC_FILE <file>]
               [TEST_LOAD <threshold>]
               [SCHEDULE_RANDOM <ON|OFF>]
               [STOP_ON_FAILURE]
               [STOP_TIME <time-of-day>]
               [RETURN_VALUE <result-var>]
               [CAPTURE_CMAKE_ERROR <result-var>]
               [REPEAT <mode>:<n>]
               [OUTPUT_JUNIT <file>]
               [PRESET <preset>]
               [PRESETS_FILE <path>]
               [DEFECT_COUNT <defect-count-var>]
               [QUIET]
               )

Runs your tests with a dynamic analysis tool and stores the results in MemCheck.xml so ctest_submit() can send them off ๐Ÿ“ค

Most of these options are exactly the same as the ones for the ctest_test() command โ€” so if you already know that one, you're chilling ๐Ÿ’œ

The option that's unique to this command:

  • DEFECT_COUNT <defect-count-var> โ€” Added in version 3.8. Stores the number of defects found in <defect-count-var>. The receipts of how broken things were ๐Ÿ’€

ctest_read_custom_files</p>

Reads the CTestCustom files ๐ŸŒธ

ctest_read_custom_files(<directory>...)

Reads all the CTestCustom.ctest or CTestCustom.cmake files from the directory (or directories) you give it.

Little default behavior note: if you invoke ctest(1) without a script, it'll read custom files from the binary directory ๐ŸŽ€


ctest_run_script</p>

Runs a ctest -S script, basically โœจ

ctest_run_script([NEW_PROCESS]
                 <script-file>...
                 [RETURN_VALUE <result-var>]
                 )

Runs a script โ€” or several scripts โ€” pretty much like it was run from ctest -S. The options:

  • NEW_PROCESS โ€” Runs each script in its own separate process. One script, one process, everyone in their own lane ๐Ÿฆ‹
  • RETURN_VALUE <result-var> โ€” Stores 0 in <result-var> for success and non-zero on failure. Simple as that โญ

ctest_sleep</p>

Sleeps for some amount of time. Self-care, but for your script ๐Ÿ˜ด๐Ÿ’–

ctest_sleep(<seconds>) โ€” Sleeps for <seconds> seconds.

ctest_sleep(<seconds>)

ctest_sleep(<time1> <duration> <time2>) โ€” Sleeps for <time1> + <duration> - <time2> seconds, if that sum is greater than zero. If it's not greater than zero, no nap for you ๐Ÿ™ˆ

ctest_sleep(<time1> <duration> <time2>)

ctest_start</p>

Performs the CTest Start Step as a Dashboard Client ๐Ÿ‘‘

ctest_start(<model> [<source> [<binary>]] [GROUP <group>] [QUIET])

ctest_start([<model> [<source> [<binary>]]] [GROUP <group>] APPEND [QUIET])

Starts the testing for a given model. Call this command after the binary directory has been initialized, okay? Order matters ๐Ÿ’…

The parameters go like this:

  • <model> โ€” Sets the dashboard model. It must be one of Experimental, Continuous, or Nightly โ€” those three, no substitutions. This parameter is required unless APPEND is specified ๐ŸŽ€
  • <source> โ€” Sets the source directory. If you don't specify it, the value of CTEST_SOURCE_DIRECTORY is used instead ๐Ÿซถ
  • <binary> โ€” sets the binary directory bestie ๐Ÿ’– if you don't pass it, we just use whatever CTEST_BINARY_DIRECTORY is set to instead.
  • GROUP <group> โ€” if you say GROUP, your submissions go to that specific group on the CDash server. No GROUP? Then the name of the model gets used by default, no biggie.

Changed in version 3.16: this replaces the deprecated TRACK option. And likeโ€ฆ the name changed but the behavior is literally identical, so don't stress โœจ

  • APPEND โ€” if APPEND is used, the existing TAG gets reused instead of making a brand new one from the current timestamp. And the cute part? When you use APPEND you can straight up omit the <model> and GROUP <group> parameters, because they're gonna be read from the generated TAG file ๐ŸŽ€ like so:
ctest_start(Experimental GROUP GroupExperimental)

Then later, in a whole other ctest -S script:

ctest_start(APPEND)

When that second script runs ctest_start(APPEND), it reads the Experimental model and the GroupExperimental group from the TAG file that the first ctest_start() command generated. ๐Ÿ‘‘ But heads up โ€” if you call ctest_start(APPEND) and specify a different model or group than the first ctest_start() command had, you get a warning issued, and the new model and group are the ones that get used. ๐Ÿ’…

  • QUIET โ€” Added in version 3.3. If QUIET is used, CTest suppresses any non-error messages it would've otherwise printed to the console. Silence is golden, periodt ๐Ÿคซโœจ

The parameters for ctest_start() can be issued in any order, with exactly one exception: <model>, <source>, and <binary> have to appear in that order relative to each other. So all of these are valid and equivalent, fr fr:

ctest_start(Experimental path/to/source path/to/binary GROUP SomeGroup QUIET APPEND)

ctest_start(GROUP SomeGroup Experimental QUIET path/to/source APPEND path/to/binary)

ctest_start(APPEND QUIET Experimental path/to/source GROUP SomeGroup path/to/binary)

But ngl, for the sake of readability, the recommendation is you order your parameters the way they're listed at the top of the page. Your future self will thank you ๐Ÿซถ

If the CTEST_CHECKOUT_COMMAND variable is set, its content is treated as command-line. The command is invoked with the current working directory set to the parent of the source directory โ€” even if the source directory already exists ๐Ÿ™ˆ This can be used to create the source tree from a version control repository. Iconic little trick, honestly.

๐Ÿ’พ ctest_submit

Perform the CTest Submit Step as a Dashboard Client ๐Ÿฆ‹

ctest_submit([PARTS <part>...] [FILES <file>...]
             [SUBMIT_URL <url>]
             [BUILD_ID <result-var>]
             [HTTPHEADER <header>]
             [RETRY_COUNT <count>]
             [RETRY_DELAY <delay>]
             [RETURN_VALUE <result-var>]
             [CAPTURE_CMAKE_ERROR <result-var>]
             [QUIET]
             )

Submit results to a dashboard server. By default, all available parts get submitted โ€” the whole package, no crumbs left behind ๐Ÿ’–

The options are:

  • PARTS <part>... โ€” specify a subset of parts to submit. Takes precedence over the CTEST_SUBMIT_PARTS variable if both are set. The valid part names are:
    • Start โ€” nothing.
    • Update โ€” ctest_update() results, in Update.xml.
    • Configure โ€” ctest_configure() results, in Configure.xml.
    • Build โ€” ctest_build() results, in Build.xml.
    • Test โ€” ctest_test() results, in Test.xml.
    • Coverage โ€” ctest_coverage() results, in Coverage.xml.
    • MemCheck โ€” ctest_memcheck() results, in DynamicAnalysis.xml and DynamicAnalysis-Test.xml.
    • Notes โ€” files listed by CTEST_NOTES_FILES, in Notes.xml.
    • ExtraFiles โ€” files listed by CTEST_EXTRA_SUBMIT_FILES.
    • Upload โ€” files prepared for upload by ctest_upload(), in Upload.xml.
    • Submit โ€” nothing.
    • Done โ€” build is complete, in Done.xml.
  • FILES <file>... โ€” specify an explicit list of specific files to be submitted. Every single individual file has to exist at the time of the call โญ no ghost files allowed.
  • SUBMIT_URL <url> โ€” Added in version 3.14. The http or https URL of the dashboard server to send the submission to. If it's not given, the CTEST_SUBMIT_URL variable is used instead.
  • BUILD_ID <result-var> โ€” Added in version 3.15. Stores in the <result-var> variable the ID that CDash assigned to this build ๐ŸŽ€
  • HTTPHEADER <HTTP-header> โ€” Added in version 3.9. Specify an HTTP header to be included in the request to CDash during submission. For example, CDash can be configured to only accept submissions from authenticated clients. In that case you should provide a bearer token in your header, like:
ctest_submit(HTTPHEADER "Authorization: Bearer <auth-token>")

This suboption can be repeated several times for multiple headers. Stack 'em, bestie ๐Ÿ’…

  • RETRY_COUNT <count> โ€” specify how many times to retry a timed-out submission.
  • RETRY_DELAY <delay> โ€” specify how long (in seconds) to wait after a timed-out submission before attempting to re-submit.
  • RETURN_VALUE <result-var> โ€” store in the <result-var> variable 0 for success and non-zero on failure.
  • CAPTURE_CMAKE_ERROR <result-var> โ€” Added in version 3.13. Store in the <result-var> variable -1 if there are any errors running the command, and prevent ctest(1) from returning non-zero if an error occurs. Lowkey a lifesaver ๐Ÿ˜ญ
  • QUIET โ€” Added in version 3.3. Suppress all non-error messages that would've otherwise been printed to the console.

๐Ÿ“ค Submit to CDash Upload API

Added in version 3.2.

ctest_submit(CDASH_UPLOAD <file> [CDASH_UPLOAD_TYPE <type>]
             [SUBMIT_URL <url>]
             [BUILD_ID <result-var>]
             [HTTPHEADER <header>]
             [RETRY_COUNT <count>]
             [RETRY_DELAY <delay>]
             [RETURN_VALUE <result-var>]
             [QUIET])

This second signature is the one you use to upload files to CDash via the CDash file upload API ๐ŸŒˆ The API first sends a request to upload to CDash along with a content hash of the file. If CDash doesn't already have that file, then it gets uploaded. Along with the file, a CDash type string is specified to tell CDash which handler to use when processing the data ๐Ÿ‘‘

This signature interprets options in the same way as the first one.

Added in version 3.8: added the RETRY_COUNT, RETRY_DELAY, QUIET options.

Added in version 3.9: added the HTTPHEADER option.

Added in version 3.13: added the RETURN_VALUE option.

Added in version 3.14: added the SUBMIT_URL option.

Added in version 3.15: added the BUILD_ID option.

๐Ÿงช ctest_test

Perform the CTest Test Step as a Dashboard Client ๐ŸŒธ

ctest_test([BUILD <build-dir>] [APPEND]
           [START <start-number>]
           [END <end-number>]
           [STRIDE <stride-number>]
           [EXCLUDE <exclude-regex>]
           [INCLUDE <include-regex>]
           [EXCLUDE_LABEL <label-exclude-regex>]
           [INCLUDE_LABEL <label-include-regex>]
           [EXCLUDE_FROM_FILE <filename>]
           [INCLUDE_FROM_FILE <filename>]
           [EXCLUDE_FIXTURE <regex>]
           [EXCLUDE_FIXTURE_SETUP <regex>]
           [EXCLUDE_FIXTURE_CLEANUP <regex>]
           [PARALLEL_LEVEL [<level>]]
           [RESOURCE_SPEC_FILE <file>]
           [TEST_LOAD <threshold>]
           [SCHEDULE_RANDOM <ON|OFF>]
           [STOP_ON_FAILURE]
           [STOP_TIME <time-of-day>]
           [RETURN_VALUE <result-var>]
           [CAPTURE_CMAKE_ERROR <result-var>]
           [REPEAT <mode>:<n>]
           [OUTPUT_JUNIT <file>]
           [PRESET <preset>]
           [PRESETS_FILE <file>]
           [QUIET]
           )

Run tests in the project build tree and store the results in Test.xml so they can be submitted with the ctest_submit() command ๐Ÿ’œ

The options are:

  • BUILD <build-dir> โ€” specify the top-level build directory. If it's not given, the CTEST_BINARY_DIRECTORY variable is used.
  • APPEND โ€” mark Test.xml for append to results previously submitted to a dashboard server since the last ctest_start() call. Append semantics are defined by the dashboard server in use. Just so we're clear: this does not cause results to be appended to a .xml file produced by a previous call to this command ๐Ÿ™ˆ
  • START <start-number> โ€” specify the beginning of a range of test numbers.
  • END <end-number> โ€” specify the end of a range of test numbers.
  • STRIDE <stride-number> โ€” specify the stride by which to step across a range of test numbers.
  • EXCLUDE <exclude-regex> โ€” specify a regular expression matching test names to exclude.
  • INCLUDE <include-regex> โ€” specify a regular expression matching test names to include. Tests that don't match this expression are excluded.
  • EXCLUDE_LABEL <label-exclude-regex> โ€” specify a regular expression matching test labels to exclude.
  • INCLUDE_LABEL <label-include-regex> โ€” specify a regular expression matching test labels to include. Tests that don't match this expression are excluded.
  • EXCLUDE_FROM_FILE <filename> โ€” Added in version 3.29. Do NOT run tests listed with their exact name in the given file ๐Ÿ’€
  • INCLUDE_FROM_FILE <filename> โ€” Added in version 3.29. Only run the tests listed with their exact name in the given file.
  • EXCLUDE_FIXTURE <regex> โ€” Added in version 3.7. If a test in the set of tests to be executed requires a particular fixture, that fixture's setup and cleanup tests would normally be added to the test set automatically. This option prevents adding setup or cleanup tests for fixtures matching the <regex>. Note that all other fixture behavior is retained, including test dependencies and skipping tests that have fixture setup tests that fail ๐Ÿงš
  • EXCLUDE_FIXTURE_SETUP <regex> โ€” โœจ added in version 3.7. Literally the same energy as EXCLUDE_FIXTURE, except only the matching setup tests get excluded. Just setup ones, bestie ๐Ÿ’…
  • EXCLUDE_FIXTURE_CLEANUP <regex> โ€” same deal, added in version 3.7. Again identical to EXCLUDE_FIXTURE, but this time only the matching cleanup tests get the boot ๐Ÿฅพ
  • PARALLEL_LEVEL [<level>] โ€” run those tests in parallel, capped at the level of parallelism you hand it. Added in version 3.29: the <level> may be omitted, or 0, to let ctest(1) use a default level of parallelism, or unbounded parallelism, respectively โ€” exactly as documented by the ctest --parallel option ๐ŸŒ€

If you do give it, this argument takes precedence over a test preset's jobs field. And heads up: both this argument and that field take precedence over the CTEST_PARALLEL_LEVEL environment variable, and over an explicit ctest --parallel given on the command line. Peep CTEST_PARALLEL_LEVEL for the full precedence order ๐Ÿ‘‘

  • RESOURCE_SPEC_FILE <file> โ€” added in version 3.16. Point it at a resource specification file. See Resource Allocation for the whole lore ๐Ÿงš
  • TEST_LOAD <threshold> โ€” added in version 3.4. While running tests in parallel, it tries not to start tests when they may push the CPU load above your given threshold. If you don't specify it, the CTEST_TEST_LOAD variable gets checked first, and then the --test-load command-line argument to ctest(1). Also see the TestLoad setting in the CTest Test Step ๐Ÿ”ฅ
  • REPEAT <mode>:<n> โ€” added in version 3.17. Run tests over and over based on the given <mode>, up to <n> times. The modes are:
    • UNTIL_FAIL โ€” require each test to run <n> times without failing in order to pass. Genuinely so useful for sniffing out sporadic failures in test cases ๐Ÿ•ต๏ธโ€โ™€๏ธ
    • UNTIL_PASS โ€” let each test run up to <n> times in order to pass. It repeats tests if they fail for any reason. Perfect for tolerating sporadic failures in test cases ๐Ÿซถ
    • AFTER_TIMEOUT โ€” let each test run up to <n> times in order to pass, but it only repeats tests if they timeout. Iconic for tolerating sporadic timeouts on busy machines ๐Ÿ˜ญ
  • SCHEDULE_RANDOM <ON|OFF> โ€” launch tests in a random order. Lowkey useful for detecting implicit test dependencies ๐Ÿ‘€
  • STOP_ON_FAILURE โ€” added in version 3.18. Stop the execution of the tests once one has failed. Periodt ๐Ÿ’€
  • STOP_TIME <time-of-day> โ€” specify a time of day at which the tests should all stop running โฐ
  • RETURN_VALUE <result-var> โ€” store 0 in the <result-var> variable if all tests passed. Store non-zero if anything went wrong ๐Ÿ“
  • CAPTURE_CMAKE_ERROR <result-var> โ€” added in version 3.7. Store -1 in the <result-var> variable if there are any errors running the command, and stop ctest(1) from returning non-zero if an error occurs ๐Ÿ™ˆ
  • OUTPUT_JUNIT <file> โ€” added in version 3.21. Write test results to <file> in JUnit XML format. If <file> is a relative path, it gets placed in the build directory. If <file> already exists, it will be overwritten. Note that the resulting JUnit XML file is not uploaded to CDash, because it would be redundant with CTest's Test.xml file ๐Ÿ’…
  • COVERAGE_TOOL <tool> โ€” added in version 4.4. Specify the <tool> used for collecting coverage while the tests run. See the CTest CoverageTool setting for details ๐ŸŒธ
  • PRESET <preset> โ€” added in version 4.4. Specify a preset to use when running tests. Any value set in the CTest script takes priority over a corresponding setting from the preset โ€” like, the INCLUDE argument will override the filter.include.name setting from the chosen preset ๐Ÿ‘‘

This option also applies to the ctest_memcheck() command. See also the CTEST_TEST_PRESET and CTEST_PRESET variables.

In general, only preset fields which have equivalent options to ctest_test are supported. The exception is the environment, which can hold a value for the CTEST_NO_TESTS_ACTION environment variable and will be respected, despite ctest_test not having an equivalent option to the noTestsAction presets field ๐Ÿซถ

  • PRESETS_FILE <file> โ€” added in version 4.4. Specify a presets file to use instead of the default CMakePresets.json in the source directory. A relative path is interpreted relative to the source directory. Has no effect unless a preset is selected via the PRESET argument or the CTEST_TEST_PRESET or CTEST_PRESET variables ๐Ÿงš

This option also applies to the ctest_memcheck() command. See also the CTEST_PRESETS_FILE variable.

  • QUIET โ€” added in version 3.3. Suppress any CTest-specific non-error messages that would have otherwise been printed to the console. Output from the underlying test command is not affected. Summary info detailing the percentage of passing tests is also unaffected by the QUIET option ๐Ÿคซ

Also go look at the CTEST_CUSTOM_MAXIMUM_PASSED_TEST_OUTPUT_SIZE, CTEST_CUSTOM_MAXIMUM_FAILED_TEST_OUTPUT_SIZE and CTEST_CUSTOM_TEST_OUTPUT_TRUNCATION variables, along with their corresponding ctest(1) command line options --test-output-size-passed, --test-output-size-failed, and --test-output-truncation โญ


โœจ Additional Test Measurements โœจ

CTest can parse the output of your tests for extra measurements to report to CDash ๐Ÿ‘‘

When run as a Dashboard Client, CTest will include these custom measurements in the Test.xml file that gets uploaded to CDash.

Check the CDash test measurement documentation for more info on the types of test measurements that CDash recognizes: https://github.com/Kitware/CDash/blob/master/docs/test_measurements.md

Added in version 3.22: CTest can parse custom measurements from tags named <CTestMeasurement> or <CTestMeasurementFile>. The older names <DartMeasurement> and <DartMeasurementFile> are still supported ๐Ÿ’–

The following example demonstrates how to output a variety of custom test measurements:

  std::cout <<
    "<CTestMeasurement type=\"numeric/double\" name=\"score\">28.3</CTestMeasurement>"
    << std::endl;

  std::cout <<
    "<CTestMeasurement type=\"text/string\" name=\"color\">red</CTestMeasurement>"
    << std::endl;

  std::cout <<
    "<CTestMeasurement type=\"text/link\" name=\"CMake URL\">https://cmake.org</CTestMeasurement>"
    << std::endl;

  std::cout <<
    "<CTestMeasurement type=\"text/preformatted\" name=\"Console Output\">" <<
    "line 1.\n" <<
    "  \033[31;1m line 2. Bold red, and indented!\033[0;0ml\n" <<
    "line 3. Not bold or indented...\n" <<
    "</CTestMeasurement>" << std::endl;

๐Ÿฆ‹ Image Measurements ๐Ÿฆ‹

The following example demonstrates how to upload test images to CDash:

  std::cout <<
    "<CTestMeasurementFile type=\"image/jpg\" name=\"TestImage\">" <<
    "/dir/to/test_img.jpg</CTestMeasurementFile>" << std::endl;

  std::cout <<
    "<CTestMeasurementFile type=\"image/gif\" name=\"ValidImage\">" <<
    "/dir/to/valid_img.gif</CTestMeasurementFile>" << std::endl;

  std::cout <<
    "<CTestMeasurementFile type=\"image/png\" name=\"AlgoResult\">" <<
    "/dir/to/img.png</CTestMeasurementFile>"
    << std::endl;

Images will be displayed together in an interactive comparison mode on CDash if they're provided with two or more of the following names ๐ŸŽ€

  • TestImage
  • ValidImage
  • BaselineImage
  • DifferenceImage2

By convention, TestImage is the image generated by your test, and ValidImage (or BaselineImage) is the basis of comparison used to determine if the test passed or failed ๐Ÿ’…

If another image name is used, it'll be displayed by CDash as a static image, separate from the interactive comparison UI.

๐ŸŒˆ Attached Files ๐ŸŒˆ

Added in version 3.21.

The following example demonstrates how to upload non-image files to CDash:

  std::cout <<
    "<CTestMeasurementFile type=\"file\" name=\"TestInputData1\">" <<
    "/dir/to/data1.csv</CTestMeasurementFile>\n"                   <<
    "<CTestMeasurementFile type=\"file\" name=\"TestInputData2\">" <<
    "/dir/to/data2.csv</CTestMeasurementFile>"                     << std::endl;

If the name of the file to upload is known at configure time, you can use the ATTACHED_FILES or ATTACHED_FILES_ON_FAIL test properties instead ๐Ÿซถ

๐Ÿ’œ Custom Details ๐Ÿ’œ

Added in version 3.21.

The following example demonstrates how to specify a custom value for the Test Details field displayed on CDash:

  std::cout <<
    "<CTestDetails>My Custom Details Value</CTestDetails>" << std::endl;

๐ŸŒธ Additional Labels ๐ŸŒธ

Added in version 3.22.

The following example demonstrates how to add additional labels to a test at runtime:

  std::cout <<
    "<CTestLabel>Custom Label 1</CTestLabel>\n" <<
    "<CTestLabel>Custom Label 2</CTestLabel>"   << std::endl;

Use the LABELS test property instead for labels that can be determined at configure time ๐Ÿ‘‘


๐Ÿ’– ctest_update ๐Ÿ’–

Perform the CTest Update Step as a Dashboard Client. Slay โœจ

ctest_update([SOURCE <source-dir>]
             [RETURN_VALUE <result-var>]
             [VERSION_ONLY]
             [VERSION_OVERRIDE <version>]
             [CAPTURE_CMAKE_ERROR <result-var>]
             [QUIET])

ok so this one pulls the latest from version control into your source tree, and it records all the receipts in Update.xml so you can ship them off with the ctest_submit() command. cute, right? ๐Ÿ’…

here's the tea on the options:

  • SOURCE <source-dir> โ€” tells it where the source dir is at. skip it and it'll just use the CTEST_SOURCE_DIRECTORY variable instead. bestie's got a default. โœจ
  • RETURN_VALUE <result-var> โ€” drops the number of files updated into <result-var>, or -1 if stuff went sideways ๐Ÿ˜ญ
  • VERSION_ONLY โ€” added in version 4.4. just records whatever revision you're currently checked out at, without yanking the source tree to a different version. it's literally the same as setting the CTEST_UPDATE_VERSION_ONLY variable. no cap.
  • VERSION_OVERRIDE <version> โ€” added in version 4.4. reports <version> as the current revision without querying or touching the source tree at all. equivalent to setting CTEST_UPDATE_VERSION_OVERRIDE, and it supersedes VERSION_ONLY. it ate that. ๐Ÿ‘‘
  • CAPTURE_CMAKE_ERROR <result-var> โ€” added in version 3.13. sticks -1 in <result-var> if there were any errors running the command, and stops ctest(1) from returning non-zero when an error happens.
  • QUIET โ€” added in version 3.3. mutes all the CTest-specific non-error messages that would've hit your console otherwise. but heads up: CTest will still report the new revision of the repo and any conflicting files it found ๐Ÿซถ

by default the update follows whatever version control branch is currently checked out in the source dir. VERSION_ONLY and VERSION_OVERRIDE flip that โ€” they just record revision info without changing the source tree at all. ๐ŸŒˆ and if you wanna know about the other variables that change how ctest_update() behaves, go peek at the CTest Update Step docs, iykyk.

๐ŸŒธ ctest_upload ๐ŸŒธ

uploads your files to a dashboard server, as a Dashboard Client. it's giving delivery service but make it CI. ๐ŸŽ€

ctest_upload(FILES <file>... [QUIET] [CAPTURE_CMAKE_ERROR <result-var>])

the options, bestie:

  • FILES <file>... โ€” the list of files you want sent along with the build results to the dashboard server ๐Ÿ’–
  • CAPTURE_CMAKE_ERROR <result-var> โ€” added in version 3.7. puts -1 in <result-var> if anything errored while running the command, and keeps ctest(1) from returning non-zero when an error occurs.
  • QUIET โ€” added in version 3.3. suppresses any CTest-specific non-error output that would've been printed to the console otherwise. shhh ๐Ÿคซ

๐Ÿ’€ DEPRECATED COMMANDS ๐Ÿ’€

ok so these ones are fossilized. they're only still here so old code doesn't break, which is honestly the vibe of every legacy codebase ever. each command's docs tell you which CMake version dropped it, but bottom line: do not use these in new code. periodt. ๐Ÿšซ

๐ŸงŸ build_name ๐ŸงŸ

Disallowed since version 3.0. see CMake Policy CMP0036. ๐Ÿ’€

use ${CMAKE_SYSTEM} and ${CMAKE_CXX_COMPILER} instead. slay.

build_name(variable)

sets the specified variable to a string representing the platform and compiler settings. those values live in the CMAKE_SYSTEM and CMAKE_CXX_COMPILER variables now. so likeโ€ฆ just use those? ๐Ÿ˜ญ

๐ŸงŸ exec_program ๐ŸงŸ

Changed in version 3.28: this command is only available if policy CMP0153 is not set to NEW. port your projects to the execute_process() command, bestie, fr fr.

Deprecated since version 3.0: use the execute_process() command instead.

runs an executable program while CMake is processing a file or script:

exec_program(
  <executable>
  [<working-dir>]
  [ARGS <arguments-to-executable>...]
  [OUTPUT_VARIABLE <var>]
  [RETURN_VALUE <var>]
)

<executable> gets run in the optionally-specified <working-dir> directory. the executable can include arguments if it's double quoted, but ngl it's better to use the optional ARGS argument for that โ€” because then CMake can escape spaces in the executable path. โœจ an optional OUTPUT_VARIABLE names a variable to stash the output in, and to grab the return value of the execution, hand it a RETURN_VALUE. heads up though: if OUTPUT_VARIABLE is specified, no output goes to the stdout/stderr of the console running CMake. it's all captured, zero leakage. ๐Ÿซถ

๐ŸŒธ Examples ๐ŸŒธ

here's the legacy exec_program() usage from back in the day, for the historians:

exec_program(
  some_command
  ${dir}
  ARGS arg_1 arg_2 args "\"<quoted-arg>\""
  OUTPUT_VARIABLE output
  RETURN_VALUE result
)

and here's the direct equivalent replacement using execute_process() in your shiny new code โ€” same energy, better command:

execute_process(
  COMMAND some_command arg_1 arg_2 args "<quoted-arg>"
  WORKING_DIRECTORY ${dir}
  RESULT_VARIABLE result
  OUTPUT_VARIABLE output
  ERROR_VARIABLE output
  OUTPUT_STRIP_TRAILING_WHITESPACE
)

๐ŸงŸ export_library_dependencies ๐ŸงŸ

Disallowed since version 3.0. see CMake Policy CMP0033. ๐Ÿ’€

use the install(EXPORT) or export() command instead.

this one generates an old-style library dependencies file. projects that need CMake 2.6 or later should not use it โ€” instead use the install(EXPORT) command to help export targets from an installation tree, and the export() command to export targets from a build tree. it's giving โœจ upgraded โœจ.

also lowkey important: the old-style library dependencies file does not take into account per-configuration names of libraries, or the LINK_INTERFACE_LIBRARIES target property. it simply doesn't know about them. ๐Ÿ™ˆ

export_library_dependencies(<file> [APPEND])

creates a file named <file> that can be included into a CMake listfile with the INCLUDE command. the file contains a bunch of SET commands that set all the variables needed for library dependency info. this should be the last command in the top level CMakeLists.txt of the project. and if you pass APPEND, the SET commands get appended to the given file instead of replacing it. ๐ŸŽ€

๐ŸงŸ install_files ๐ŸงŸ

Deprecated since version 3.0: use the install(FILES) command instead.

superseded by the install() command, kept around for compatibility with older CMake code. the FILES form is directly replaced by the FILES form of install(), and the regexp form can be expressed more clearly using the GLOB form of the file() command. glow-up complete. ๐Ÿ’…

install_files(<dir> extension file file ...)

creates rules to install the listed files with the given extension into the given directory. only files existing in the current source tree or its corresponding location in the binary tree may be listed. if a file you specify already has an extension, that extension gets removed first โ€” which is genuinely useful for providing lists of source files like foo.cxx when what you actually want installed is the corresponding foo.h. a typical extension is .h. โœจ

install_files(<dir> regexp)

any files in the current source directory that match the regular expression get installed. regex girlies rise. ๐Ÿฆ‹

install_files(<dir> FILES file file ...)

any files listed after the FILES keyword get installed explicitly from the names given. full paths are allowed in this form. ๐Ÿซถ

and the directory <dir> is relative to the installation prefix, which lives in the CMAKE_INSTALL_PREFIX variable.

๐ŸงŸ install_programs ๐ŸงŸ

Deprecated since version 3.0: use the install(PROGRAMS) command instead.

superseded by install(), provided for compatibility with older CMake code. the FILES form is directly replaced by the PROGRAMS form of install(), and again, the regexp form can be expressed more clearly using the GLOB form of the file() command. ๐ŸŒˆ

install_programs(<dir> file1 file2 [file3 ...])
install_programs(<dir> FILES file1 [file2 ...])

creates rules to install the listed programs into the given directory. use the FILES argument to guarantee that the file-list version of the command gets used even when there's only one argument. one is still a list, bestie. ๐Ÿ’…

install_programs(<dir> regexp)

in this second form, any program in the current source directory matching the regular expression gets installed. ๐ŸŽ€

this command is meant for programs that are not built by cmake โ€” like shell scripts. see the TARGETS form of the install() command for creating installation rules for targets built by cmake. iykyk. ๐Ÿ‘‘

and <dir> is relative to the installation prefix, which is stored in the CMAKE_INSTALL_PREFIX variable.

๐ŸงŸ install_targets ๐ŸงŸ

Deprecated since version 3.0: use the install(TARGETS) command instead.

superseded by the install() command, hanging around for compatibility with older CMake code. ๐Ÿซถ

install_targets(<dir> [RUNTIME_DIRECTORY dir] target target)

creates rules to install the listed targets into the given directory. <dir> is relative to the installation prefix, which is stored in the CMAKE_INSTALL_PREFIX variable. and if RUNTIME_DIRECTORY is specified, then on systems with special runtime files (Windows DLLs, hi ๐Ÿ‘‹), the files get copied to that directory. โœจ

๐ŸงŸ load_command ๐ŸงŸ

Disallowed since version 3.0. see CMake Policy CMP0031. ๐Ÿ’€

loads a command into a running CMake. yes, into a running CMake. main character energy for real.

load_command(COMMAND_NAME <loc1> [loc2 ...])

those locations get searched for a library named cmCOMMAND_NAME. if it's found, it gets loaded as a module and the command is added to the set of available CMake commands. usually, try_compile() is used before this command to compile the module. and if the command loads successfully, a variable named CMAKE_LOADED_COMMAND_<COMMAND_NAME> gets set to the full path of the module that was loaded. if it doesn't load? the variable justโ€ฆ doesn't get set. ๐Ÿ˜ญ no drama, no variable.

๐ŸงŸ make_directory ๐ŸงŸ

Deprecated since version 3.0: use the file(MAKE_DIRECTORY) command instead. ๐ŸŽ€

make_directory(directory)

that's it, that's the whole thing. go use file(MAKE_DIRECTORY) bestie, it's literally right there. ๐Ÿ’–

  • Full paths are strongly encouraged โ€” like, give the whole path, bestie, don't be mysterious about it.
  • Parent directories that don't exist yet get created too โ€” the whole chain, no cap. Which is exactly why the man page says use with care ๐Ÿ’… (it will happily build you a whole directory tree if you point it somewhere cursed).
๐Ÿ™ˆ Lowkey, that whole "use with care" is doing a lot of work in one sentence. Justโ€ฆ be normal about paths.

๐Ÿ—‘๏ธ output_required_files

Disallowed since version 3.0. See CMake Policy CMP0032.

Approximate C preprocessor dependency scanning. Yeah. Approximate. โœจ

This command exists only because ancient CMake versions provided it โ€” it's a fossil, a little museum piece ๐Ÿฆ‹. CMake handles preprocessor dependency scanning automatically these days, using a more advanced scanner, so this whole thing is genuinely not your problem anymore.

output_required_files(srcfile outputfile)

It outputs a list of all the source files that are required by the specified srcfile. That list gets written into outputfile. It's similar to writing out the dependencies for srcfile, except that it jumps from .h files into .cxx, .c and .cpp files if possible. Sneaky little hops ๐Ÿ”ฅ


๐Ÿ—‘๏ธ qt_wrap_cpp

Deprecated since version 3.14. Okay so storytime ๐Ÿ“– โ€” this command was originally added to support Qt 3 before the add_custom_command() command was sufficiently mature. It was giving "we made this ourselves" energy and honestly, respect.

The FindQt4 module provides the qt4_wrap_cpp() macro, which should be used instead for Qt 4 projects. For projects using Qt 5 or later, use the equivalent macro provided by Qt itself (e.g. Qt 5 provides qt5_wrap_cpp() at https://doc.qt.io/archives/qt-5.15/qtcore-cmake-qt5-wrap-cpp.html).

Manually create Qt Wrappers. Like, by hand. 2010s manual labour vibes โœจ

qt_wrap_cpp(resultingLibraryName DestName SourceLists ...)

It produces moc files for all the .h files listed in the SourceLists. Those moc files get added to the library using the DestName source list. Slay, but also please don't.

๐Ÿ’ก Consider updating the project to use the AUTOMOC target property instead โ€” that's the way more automated route for invoking the moc tool, and honestly it's the main character move ๐Ÿ‘‘


๐Ÿ—‘๏ธ qt_wrap_ui

Deprecated since version 3.14. Same origin story as above, bestie: originally added to support Qt 3 before the add_custom_command() command was sufficiently mature. The FindQt4 module provides the qt4_wrap_ui() macro, which should be used instead for Qt 4 projects. For projects using Qt 5 or later, use the equivalent macro provided by Qt itself (e.g. Qt 5 provides qt5_wrap_ui()).

Manually create Qt user interfaces Wrappers. Your lil .ui files, hand-wrapped ๐ŸŽ€

qt_wrap_ui(resultingLibraryName HeadersDestName
           SourcesDestName SourceLists ...)

It produces .h and .cxx files for all the .ui files listed in the SourceLists. The .h files will be added to the library using the HeadersDestName source list. The .cxx files will be added to the library using the SourcesDestName source list. Two source lists, two jobs, both done ๐Ÿ’…

๐Ÿ’ก Consider updating the project to use the AUTOUIC target property instead for a more automated way of invoking the uic tool. Please. For me. ๐Ÿซถ


๐Ÿ—‘๏ธ remove

Deprecated since version 3.0. Use the list(REMOVE_ITEM) command instead. Like, genuinely, that's the whole vibe check.

remove(VAR VALUE VALUE ...)

It removes VALUE from the variable VAR. This is typically used to remove entries from a vector (e.g. a semicolon separated list). VALUE is expanded. That's it, that's the command ๐ŸŒธ


๐Ÿ—‘๏ธ subdir_depends

Disallowed since version 3.0. See CMake Policy CMP0029.

Does nothing.

Okay but the way I need to emphasize this: it does nothing. We love an icon who shows up and contributes zero ๐Ÿ’€

subdir_depends(subdir dep1 dep2 ...)

Does not do anything. This command used to help projects order parallel builds correctly. That functionality is now automatic, so the command is just vibing in the corner, unemployed but glamorous โœจ


๐Ÿ—‘๏ธ subdirs

Deprecated since version 3.0. Use the add_subdirectory() command instead.

Add a list of subdirectories to the build. Group project energy ๐ŸŒˆ

subdirs(dir1 dir2 ...[EXCLUDE_FROM_ALL exclude_dir1 exclude_dir2 ...]
        [PREORDER])

Add a list of subdirectories to the build. The add_subdirectory() command should be used instead of subdirs, although subdirs will still work. This will cause any CMakeLists.txt files in the sub directories to be processed by CMake.

Any directories after the PREORDER flag are traversed first by makefile builds โ€” and heads up, the PREORDER flag has no effect on IDE projects ๐Ÿ™ˆ

Any directories after the EXCLUDE_FROM_ALL marker will not be included in the top level makefile or project file. This is useful for having CMake create makefiles or projects for a set of examples in a project. Picture it: you'd want CMake to generate makefiles or project files for all the examples at the same time, but you would not want them to show up in the top level project or be built each time make is run from the top. Obsessed with that kind of control honestly ๐Ÿ‘‘


๐Ÿ—‘๏ธ use_mangled_mesa

Disallowed since version 3.0. See CMake Policy CMP0030.

Copy mesa headers for use in combination with system GL.

use_mangled_mesa(PATH_TO_MESA OUTPUT_DIRECTORY)

The path to mesa includes should contain gl_mangle.h. The mesa headers are copied to the specified output directory. This allows mangled mesa headers to override other GL headers by being added to the include directory path earlier. It's giving "I was here first, so I win" ๐Ÿฆ‹

โš ๏ธ Disallowed, bestie โ€” the policy tag is CMP0030, and no amount of wishful thinking changes that.

๐Ÿ—‘๏ธ utility_source

Disallowed since version 3.0. See CMake Policy CMP0034.

Specify the source tree of a third-party utility.

utility_source(cache_entry executable_name
               path_to_source [file1 file2 ...])

When a third-party utility's source is included in the distribution, this command specifies its location and name. The cache entry will not be set unless the path_to_source and all listed files exist. It is assumed that the source tree of the utility will have been built before it is needed.

Now the cross-compiling part, because it's lowkey important ๐Ÿ˜ญ โ€” when cross compiling, CMake will print a warning if a utility_source() command is executed, because in many cases it is used to build an executable which is executed later on. That doesn't work when cross compiling, since the executable can run only on their target platform. So in this case the cache entry has to be adjusted manually so it points to an executable which is runnable on the build host. Manual adjustment, no shortcuts, fr fr.


๐Ÿ—‘๏ธ variable_requires

Disallowed since version 3.0. See CMake Policy CMP0035.

Use the if() command instead. That's the whole replacement, and honestly if() ate ๐Ÿ’…

Assert satisfaction of an option's required variables.

variable_requires(TEST_VARIABLE RESULT_VARIABLE
                  REQUIRED_VARIABLE1
                  REQUIRED_VARIABLE2 ...)

The first argument (TEST_VARIABLE) is the name of the variable to be tested โ€” if that variable is false, nothing else is done. Chill. If TEST_VARIABLE is true, then the next argument (RESULT_VARIABLE) is a variable that is set to true if all the required variables are set. The rest of the arguments are variables that must be true or not set to NOTFOUND to avoid an error. If any are not true, an error is reported. So yeah โ€” everyone has to show up or the whole thing falls apart ๐Ÿซถ


๐Ÿ—‘๏ธ write_file

Deprecated since version 3.0. Use the file(WRITE) command instead.

write_file(filename "message to write"... [APPEND])

The first argument is the file name, the rest of the arguments are messages to write. If the argument APPEND is specified, then the message will be appended. Simple. Elegant. Deprecated ๐ŸŒธ

๐Ÿ“ NOTE 1: file(WRITE) and file(APPEND) do exactly the same as this one but add some more functionality. So like, same energy, more features.
๐Ÿ’€ NOTE 2: When using write_file the produced file cannot be used as an input to CMake (CONFIGURE_FILE, source file ...) because it will lead to an infinite loop. An actual infinite loop, bestie, not a metaphor. Use configure_file() if you want to generate input files to CMake.

2000-2026 Kitware, Inc. and Contributors.


โœจ and that's the whole deprecated-commands saga, bestie โœจ this page was CMAKE-COMMANDS(7), version 4.4.5, dated Oct 28, 2026, brought to you by Kitware, Inc. and Contributors (2000-2026) โ€” go forth and use the non-deprecated commands, mwah ๐Ÿ’–๐Ÿฆ‹๐Ÿ‘‘

๐Ÿ“ฆ where this page comes from