diff options
77 files changed, 1966 insertions, 1238 deletions
diff --git a/Help/command/cmake_path.rst b/Help/command/cmake_path.rst index 2d37ae0..5d2d7eb 100644 --- a/Help/command/cmake_path.rst +++ b/Help/command/cmake_path.rst @@ -3,109 +3,32 @@ cmake_path .. versionadded:: 3.20 -Filesystem path manipulation command. - -This command is dedicated to the manipulation of objects of type path which -represent paths on a filesystem. Only syntactic aspects of paths are handled: -the pathname may represent a non-existing path or even one that is not allowed -to exist on the current file system or OS. - -For operations involving the filesystem, have a look at the :command:`file` +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 :command:`file` command. -The path name has the following syntax: - -1. ``root-name`` (optional): identifies the root on a filesystem with multiple - roots (such as ``"C:"`` or ``"//myserver"``). - -2. ``root-directory`` (optional): a directory separator that, if present, marks - this path as absolute. If it is missing (and the first element other than - the ``root-name`` is a ``item-name``), then the path is relative. - -Zero or more of the following: - -3. ``item-name``: 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 ``item-names`` are recognized: - - * ``dot``: the item name consisting of a single dot character ``.`` is a - directory name that refers to the current directory. - - * ``dot-dot``: the item name consisting of two dot characters ``..`` is a - directory name that refers to the parent directory. - -4. ``directory-separator``: the forward slash character ``/``. If this - character is repeated, it is treated as a single directory separator: - ``/usr///////lib`` is the same as ``/usr/lib``. - -.. _FILENAME_DEF: - -A path has a filename if it does not ends with a ``directory-separator``. The -filename is the last ``item-name`` of the path. - -.. _EXTENSION_DEF: - -A :ref:`filename <FILENAME_DEF>` can have an extension. By default, the -extension is defined as the sub-string beginning at the leftmost period -(including the period) and until the end of the pathname. When the option -``LAST_ONLY`` is specified, the extension is the sub-string beginning at the -rightmost period. - -The following exceptions apply: - - * If the first character in the :ref:`filename <FILENAME_DEF>` is a period, - that period is ignored (a filename like ``".profile"`` is not treated as an - extension). - - * If the pathname is either ``.`` or ``..``. - .. note:: - ``cmake_path`` command handles paths in the format of the build system, not - the target system. So this is not generally applicable to the target system - in cross-compiling environment. - -For all commands, ``<path-var>`` placeholder expect a variable name. An error -will be raised if the variable does not exist, except for `SET`_ and `APPEND`_ -sub-commands. ``<input>`` placeholder expect a string literal. -``[<input>...]`` placeholder expect zero or more arguments. ``<out-var>`` -placeholder expect a variable name. - -.. note:: - - ``cmake_path`` command does not support list of paths. The ``<path-var>`` - placeholder must store only one path name. - -To initialize a path variable, three possibilities can be used: - -1. :command:`set` command. -2. :ref:`cmake_path(SET) <SET>` command. Mainly used to build a - path variable from a native path. -3. :ref:`cmake_path(APPEND) <APPEND>` command. Can be used to build a path from - already available path fragments. - - .. code-block:: cmake - - # To build the path "${CMAKE_CURRENT_SOURCE_DIR}/data" - - 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 store the result in-place or in -the variable specified by ``OUTPUT_VARIABLE`` option. All other sub-commands -store the result in the required ``<out-var>`` variable. - -Sub-commands supporting ``NORMALIZE`` option will :ref:`normalize <NORMAL_PATH>` -the path. + 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 ^^^^^^^^ .. parsed-literal:: + `Conventions`_ + + `Path Structure And Terminology`_ + + `Normalization`_ + `Decomposition`_ cmake_path(`GET`_ <path-var> :ref:`ROOT_NAME <GET_ROOT_NAME>` <out-var>) cmake_path(`GET`_ <path-var> :ref:`ROOT_DIRECTORY <GET_ROOT_DIRECTORY>` <out-var>) @@ -116,32 +39,6 @@ Synopsis cmake_path(`GET`_ <path-var> :ref:`RELATIVE_PATH <GET_RELATIVE_PATH>` <out-var>) cmake_path(`GET`_ <path-var> :ref:`PARENT_PATH <GET_PARENT_PATH>` <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>]) - - `Conversion`_ - cmake_path(`NATIVE_PATH`_ <path-var> [NORMALIZE] <out-var>) - cmake_path(`CONVERT`_ <input> `TO_CMAKE_PATH_LIST`_ <out-var>) - cmake_path(`CONVERT`_ <input> `TO_NATIVE_PATH_LIST`_ <out-var>) - - `Comparison`_ - cmake_path(`COMPARE`_ <input1> <OP> <input2> <out-var>) - `Query`_ cmake_path(`HAS_ROOT_NAME`_ <path-var> <out-var>) cmake_path(`HAS_ROOT_DIRECTORY`_ <path-var> <out-var>) @@ -154,664 +51,755 @@ Synopsis 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>) + cmake_path(`COMPARE`_ <input1> <OP> <input2> <out-var>) - `Hashing`_ - cmake_path(`HASH`_ <path-var> [NORMALIZE] <out-var>) - -Decomposition -^^^^^^^^^^^^^ - -.. _GET: -.. _GET_ROOT_NAME: - -.. code-block:: cmake - - cmake_path(GET <path-var> ROOT_NAME <out-var>) - -Returns the root name of the path. If the path does not include a root name, -returns an empty path. - -.. note:: - - Only ``Windows`` system has the concept of ``root-name``, so on all other - systems, it is always an empty path. - -For example: - - .. code-block:: cmake - - set (path "c:/a") - cmake_path (GET path ROOT_NAME output) - message ("Root name is \"${output}\"") + `Modification`_ + cmake_path(:ref:`SET <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>]) - Will display:: + `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>]) - Root name is "c:" + `Native Conversion`_ + cmake_path(`NATIVE_PATH`_ <path-var> [NORMALIZE] <out-var>) + cmake_path(`CONVERT`_ <input> `TO_CMAKE_PATH_LIST`_ <out-var>) + cmake_path(`CONVERT`_ <input> `TO_NATIVE_PATH_LIST`_ <out-var>) -.. _GET_ROOT_DIRECTORY: + `Hashing`_ + cmake_path(`HASH`_ <path-var> <out-var>) -.. code-block:: cmake +Conventions +^^^^^^^^^^^ - cmake_path(GET <path-var> ROOT_DIRECTORY <out-var>) +The following conventions are used in this command's documentation: -Returns the root directory of the path. If the path does not include a root -directory, returns an empty path. +``<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. -For example: +``<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. - .. code-block:: cmake +``<input>...`` + Zero or more string literal arguments. - set (path "c:/a") - cmake_path (GET path ROOT_DIRECTORY output) - message ("Root directory is \"${output}\"") +``<out-var>`` + The name of a variable into which the result of a command will be written. - Will display:: - Root directory is "/" +Path Structure And Terminology +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. _GET_ROOT_PATH: +A path has the following structure (all components are optional, with some +constraints): -.. code-block:: cmake +:: - cmake_path(GET <path-var> ROOT_PATH <out-var>) + root-name root-directory-separator (item-name directory-separator)* filename -Returns the root path of the path. If the path does not include a root path, -returns an empty path. +``root-name`` + Identifies the root on a filesystem with multiple roots (such as ``"C:"`` + or ``"//myserver"``). It is optional. -Effectively, returns the following: ``root-name root-directory``. +``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. -For example: +``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: - .. code-block:: cmake + * The item name consisting of a single dot character ``.`` is a + directory name that refers to the current directory. - set (path "c:/a") - cmake_path (GET path ROOT_PATH output) - message ("Root path is \"${output}\"") + * The item name consisting of two dot characters ``..`` is a + directory name that refers to the parent directory. - Will display:: + 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. - Root path is "c:/" +``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``. -.. _GET_FILENAME: +.. _FILENAME_DEF: +.. _EXTENSION_DEF: +.. _STEM_DEF: -.. code-block:: cmake +``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. - cmake_path(GET <path-var> FILENAME <out-var>) + 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. -Returns the :ref:`filename <FILENAME_DEF>` component of the path. If the path -ends with a ``directory-separator``, there is no filename, so returns an empty -path. + The following exceptions apply to the above interpretation: -For example: + * 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). - .. code-block:: cmake + * If the ``filename`` is either ``.`` or ``..``, it has no extension. - set (path "/a") - cmake_path (GET path FILENAME output) - message ("First filename is \"${output}\"") + The *stem* is the part of the ``filename`` before the extension. - set (path "/a/") - cmake_path (GET path FILENAME output) - message ("Second filename is \"${output}\"") +Some commands refer to a ``root-path``. This is the concatenation of +``root-name`` and ``root-directory``, either or both of which can be empty. - Will display:: - First filename is "a" - Second filename is "" +Creating A Path Variable +^^^^^^^^^^^^^^^^^^^^^^^^ -.. _GET_EXTENSION: +While a path can be created with care using an ordinary :command:`set` +command, it is recommended to use :ref:`cmake_path(SET) <cmake_path-SET>` +instead, as it automatically converts the path to the required form where +required. The :ref:`cmake_path(APPEND) <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: .. code-block:: cmake - cmake_path(GET <path-var> EXTENSION [LAST_ONLY] <out-var>) - -Returns the :ref:`extension <EXTENSION_DEF>` of the filename component. - -If the :ref:`filename <FILENAME_DEF>` component of the path contains a period -(``.``), and is not one of the special filesystem elements ``dot`` or -``dot-dot``, then the :ref:`extension <EXTENSION_DEF>` is returned. + set(path1 "${CMAKE_CURRENT_SOURCE_DIR}/data") -For example: + cmake_path(SET path2 "${CMAKE_CURRENT_SOURCE_DIR}/data") - .. code-block:: cmake + cmake_path(APPEND path3 "${CMAKE_CURRENT_SOURCE_DIR}" "data") - set (path "name.ext1.ext2") - cmake_path (GET path EXTENSION result) - message ("Full extension is \"${result}\"") - cmake_path (GET path EXTENSION LAST_ONLY result) - message ("Last extension is \"${result}\"") +`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. - Will display:: +.. _Normalization: - Full extension is ".ext1.ext2" - Last extension is ".ext2" +Normalization +^^^^^^^^^^^^^ -The following exceptions apply: +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 ``.``). - * If the first character in the filename is a period, that period is ignored - (a filename like ``".profile"`` is not treated as an extension). - * If the pathname is either ``.`` or ``..``, or if - :ref:`filename <FILENAME_DEF>` component does not contain the ``.`` - character, then an empty path is returned. +Decomposition +^^^^^^^^^^^^^ +.. _GET: +.. _GET_ROOT_NAME: +.. _GET_ROOT_DIRECTORY: +.. _GET_ROOT_PATH: +.. _GET_FILENAME: +.. _GET_EXTENSION: .. _GET_STEM: -.. code-block:: cmake +The following forms of the ``GET`` subcommand each retrieve a different +component or group of components from a path. +`Path Structure And Terminology`_ defines 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>) -Returns the :ref:`filename <FILENAME_DEF>` component of the path stripped of -its :ref:`extension <EXTENSION_DEF>`. +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 Example: - .. code-block:: cmake +Root examples +""""""""""""" - set (path "name.ext1.ext2") - cmake_path (GET path STEM result) - message ("Filename without the extension is \"${result}\"") - cmake_path (GET path STEM LAST_ONLY result) - message ("Filename whiteout the last extension is \"${result}\"") +.. code-block:: cmake - Will display:: + set(path "c:/a") - Filename without the extension is "name" - Filename without the last extension is "name.ext1" + cmake_path(GET path ROOT_NAME rootName) + cmake_path(GET path ROOT_DIRECTORY rootDir) + cmake_path(GET path ROOT_PATH rootPath) -The following exceptions apply: + message("Root name is \"${rootName}\"") + message("Root directory is \"${rootDir}\"") + message("Root path is \"${rootPath}\"") - * If the first character in the filename is a period, that period is ignored - (a filename like ``".profile"`` is not treated as an extension). +:: - * If the filename is one of the special filesystem components ``dot`` or - ``dot-dot``, or if it has no periods, the function returns the entire - :ref:`filename <FILENAME_DEF>` component. + Root name is "c:" + Root directory is "/" + Root path is "c:/" -.. _GET_RELATIVE_PATH: +Filename examples +""""""""""""""""" .. code-block:: cmake - cmake_path(GET <path-var> RELATIVE_PATH <out-var>) - -Returns path relative to ``root-path``, that is, a pathname composed of -every component of ``<path-var>`` after ``root-path``. If ``<path-var>`` is -an empty path, returns an empty path. + set(path "/a/b") + cmake_path(GET path FILENAME filename) + message("First filename is \"${filename}\"") -For Example: + # Trailing slash means filename is empty + set(path "/a/b/") + cmake_path(GET path FILENAME filename) + message("Second filename is \"${filename}\"") - .. code-block:: cmake +:: - set (path "/a/b") - cmake_path (GET path RELATIVE_PATH result) - message ("Relative path is \"${result}\"") + First filename is "b" + Second filename is "" - set (path "/") - cmake_path (GET path RELATIVE_PATH result) - message ("Relative path is \"${result}\"") +Extension and stem examples +""""""""""""""""""""""""""" - Will display:: +.. code-block:: cmake - Relative path is "a/b" - Relative path is "" + set(path "name.ext1.ext2") -.. _GET_PARENT_PATH: + cmake_path(GET path EXTENSION fullExt) + cmake_path(GET path STEM fullStem) + message("Full extension is \"${fullExt}\"") + message("Full stem is \"${fullStem}\"") -.. code-block:: cmake + # 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}\"") - cmake_path(GET <path-var> PARENT_PATH <out-var>) + # 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 dotMorePath EXTENSION someMoreExt) + cmake_path(GET dotMorePath 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}\"") -Returns the path to the parent directory. +:: -If `HAS_RELATIVE_PATH`_ sub-command returns false, the result is a copy of -``<path-var>``. Otherwise, the result is ``<path-var>`` with one fewer element. + 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" -For Example: - .. code-block:: cmake +Relative paths +"""""""""""""" - set (path "c:/a/b") - cmake_path (GET path PARENT_PATH result) - message ("Parent path is \"${result}\"") +Two other forms of the ``GET`` subcommand interpret a path and return +another path derived from it. - set (path "c:/") - cmake_path (GET path PARENT_PATH result) - message ("Parent path is \"${result}\"") +.. _GET_RELATIVE_PATH: - Will display:: +:: - Parent path is "c:/a" - Relative path is "c:/" + cmake_path(GET <path-var> RELATIVE_PATH <out-var>) -Modification -^^^^^^^^^^^^ +Returns the path with any ``root-name`` and ``root-directory-separator`` +removed. This leaves just the part of the path relative to the root +directory (or put another way, every component of ``<path-var>`` after +``root-path``). If ``<path-var>`` is an empty path, it returns an empty +path. -.. _cmake_path-SET: -.. _SET: +For example: .. code-block:: cmake - cmake_path(SET <path-var> [NORMALIZE] <input>) - -Assign the ``<input>`` path to ``<path-var>``. Moreover, if ``<input>`` is a -native path, it is converted into cmake-style path with forward-slashes -(``/``). On Windows, the long filename marker is taken into account. + set(path "/a/b") + cmake_path(GET path RELATIVE_PATH result) + message("Relative path is \"${result}\"") -When ``NORMALIZE`` option is specified, the path is :ref:`normalized -<NORMAL_PATH>` before the conversion. + set(path "/") + cmake_path(GET path RELATIVE_PATH result) + message("Relative path is \"${result}\"") -For Example: +Output:: - .. code-block:: cmake + Relative path is "a/b" + Relative path is "" - set (native_path "c:\\a\\b/..\\c") - cmake_path (SET path "${native_path}") - message ("CMake path is \"${path}\"") +.. _GET_PARENT_PATH: - cmake_path (SET path NORMALIZE "${native_path}") - message ("Normalized CMake path is \"${path}\"") +The other form returns the parent of the path:: - Will display:: + cmake_path(GET <path-var> PARENT_PATH <out-var>) - CMake path is "c:/a/b/../c" - Normalized CMake path is "c:/a/c" +If the `HAS_RELATIVE_PATH`_ sub-command returns false, the result is a +copy of ``<path-var>``. Otherwise, the result is ``<path-var>`` with +one fewer element. -.. _APPEND: +For example: .. code-block:: cmake - cmake_path(APPEND <path-var> [<input>...] [OUTPUT_VARIABLE <out-var>]) + set(path "c:/a/b") + cmake_path(GET path PARENT_PATH result) + message("Parent path is \"${result}\"") -Append all the ``<input>`` arguments to the ``<path-var>`` using ``/`` as -``directory-separator``. - -For each ``<input>`` argument, the following algorithm (pseudo-code) applies: + set(path "c:/") + cmake_path(GET path PARENT_PATH result) + message("Parent path is \"${result}\"") - .. code-block:: cmake +Output:: - # <path> is the contents of <path-var> + Parent path is "c:/a" + Relative path is "c:/" - IF (<input>.is_absolute() OR - (<input>.has_root_name() AND - NOT <input>.root_name() STREQUAL <path>.root_name())) - replaces <path> with <input> - RETURN() - ENDIF() +Query +^^^^^ - 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())) - appends directory-separator to <path> - ENDIF() +Most of the ``GET`` subcommands also have corresponding ``HAS_...`` +subcommands which can be used to discover whether a particular path +component is present. `Path Structure And Terminology`_ defines the +meaning of each path component. - appends <input> omitting any root-name to <path> +.. _HAS_ROOT_NAME: +.. _HAS_ROOT_DIRECTORY: +.. _HAS_ROOT_PATH: +.. _HAS_FILENAME: +.. _HAS_EXTENSION: +.. _HAS_STEM: -.. _APPEND_STRING: +:: -.. code-block:: cmake + 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(APPEND_STRING <path-var> [<input>...] [OUTPUT_VARIABLE <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. +In the case of ``HAS_ROOT_PATH``, a true result will only be returned if +at least one of ``root-name`` or ``root-directory`` is non-empty. -Append all the ``<input>`` arguments to the ``<path-var>`` without -``directory-separator``. +.. _HAS_RELATIVE_PATH: -.. _REMOVE_FILENAME: +:: -.. code-block:: cmake + cmake_path(HAS_RELATIVE_PATH <path-var> <out-var>) - cmake_path(REMOVE_FILENAME <path-var> [OUTPUT_VARIABLE <out-var>]) +A relative path in this context means everything after the ``root-path``, +if present. This command sets ``<out-var>`` to true if there is at least +one ``item-name`` or ``filename`` in the path. -Removes the :ref:`filename <FILENAME_DEF>` component (as returned by -:ref:`GET ... FILENAME <GET_FILENAME>`) from ``<path-var>``. +.. _HAS_PARENT_PATH: -After this function returns, if change is done in-place, `HAS_FILENAME`_ -returns false for ``<path-var>``. +:: -For Example: + cmake_path(HAS_PARENT_PATH <path-var> <out-var>) - .. code-block:: cmake +This command sets ``<out-var>`` to true if ``<path-var>`` has parent path. +Note that 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 +:ref:`filename <FILENAME_DEF>`. - set (path "/a/b") - cmake_path (REMOVE_FILENAME path) - message ("First path is \"${path}\"") +.. _IS_ABSOLUTE: - cmake_path (REMOVE_FILENAME path) - message ("Second path is \"${result}\"") +:: - Will display:: + cmake_path(IS_ABSOLUTE <path-var> <out-var>) - First path is "/a/" - Second path is "/a/" +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. -.. _REPLACE_FILENAME: +.. _IS_RELATIVE: -.. code-block:: cmake +:: - cmake_path(REPLACE_FILENAME <path-var> <input> [OUTPUT_VARIABLE <out-var>]) + cmake_path(IS_RELATIVE <path-var> <out-var>) -Replaces the :ref:`filename <FILENAME_DEF>` component from ``<path-var>`` with -``<input>``. +This will store the opposite of ``IS_ABSOLUTE`` in ``<out-var>``. -If ``<path-var>`` has no filename component (`HAS_FILENAME`_ returns false), -the path is unchanged. +.. _IS_PREFIX: -Equivalent to the following: +:: - .. code-block:: cmake + cmake_path(IS_PREFIX <path-var> <input> [NORMALIZE] <out-var>) - cmake_path(HAS_FILENAME path has_filename) - if (has_filename) - cmake_path(REMOVE_FILENAME path) - cmake_path(APPEND path "replacement"); - endif() +Checks if ``<path-var>`` is the prefix of ``<input>``. -.. _REMOVE_EXTENSION: +When the ``NORMALIZE`` option is specified, ``<path-var>`` and ``<input>`` +are :ref:`normalized <Normalization>` before the check. .. code-block:: cmake - cmake_path(REMOVE_EXTENSION <path-var> [LAST_ONLY] - [OUTPUT_VARIABLE <out-var>]) + set(path "/a/b/c/d") + cmake_path(IS_PREFIX path "/a/b" result) # result = true + cmake_path(IS_PREFIX path "/x/y/z" result) # result = false -Removes the :ref:`extension <EXTENSION_DEF>`, if any, from ``<path-var>``. + set(path "/a/b") + cmake_path(IS_PREFIX path "/a/c/../b" NORMALIZE result) # result = true -.. _REPLACE_EXTENSION: +.. _COMPARE: -.. code-block:: cmake +:: - cmake_path(REPLACE_EXTENSION <path-var> [LAST_ONLY] <input> - [OUTPUT_VARIABLE <out-var>]) + cmake_path(COMPARE <input1> EQUAL <input2> <out-var>) + cmake_path(COMPARE <input1> NOT_EQUAL <input2> <out-var>) -Replaces the :ref:`extension <EXTENSION_DEF>` with ``<input>``. +Compares the lexical representations of two paths provided as string literals. +No normalization is performed on either path. Equality is determined +according to the following pseudo-code logic: - 1. If ``<path-var>`` has an :ref:`extension <EXTENSION_DEF>` - (`HAS_EXTENSION`_ is true), it is removed. - 2. A ``dot`` character is appended to ``<path-var>``, if ``<input>`` is not - empty or does not begin with a ``dot`` character. - 3. ``<input>`` is appended as if `APPEND_STRING`_ was used. +:: + if(NOT <input1>.root_name() STREQUAL <input2>.root_name()) + return FALSE -Equivalent to the following: + if(<input1>.has_root_directory() XOR <input2>.has_root_directory()) + return FALSE - .. code-block:: cmake + 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. - cmake_path(REMOVE_EXTENSION path) - if (NOT "input" MATCHES "^\\.") - cmake_path(APPEND_STRING path ".") - endif() - cmake_path(APPEND_STRING path "input"); +.. note:: + Unlike most other ``cmake_path()`` subcommands, the ``COMPARE`` subcommand + takes literal strings as input, not the names of variables. -Generation -^^^^^^^^^^ -.. _NORMAL_PATH: +Modification +^^^^^^^^^^^^ -.. code-block:: cmake +.. _cmake_path-SET: - cmake_path(NORMAL_PATH <path-var> [OUTPUT_VARIABLE <out-var>]) +:: -Normalize ``<path-var>``. + cmake_path(SET <path-var> [NORMALIZE] <input>) -A path can be normalized by following this algorithm: +Assign the ``<input>`` path to ``<path-var>``. If ``<input>`` is a native +path, it is converted into a cmake-style path with forward-slashes +(``/``). On Windows, the long filename marker is taken into account. - 1. If the path is empty, stop (normal form of an empty path is an empty - path). - 2. Replace each ``directory-separator`` (which may consist of multiple - separators) with a single ``/``. - 3. Replace each ``directory-separator`` character in the ``root-name`` with - ``/``. - 4. Remove each ``dot`` and any immediately following ``directory-separator``. - 5. Remove each non-dot-dot filename immediately followed by a - ``directory-separator`` and a ``dot-dot``, along with any immediately - following ``directory-separator``. - 6. If there is ``root-directory``, remove all ``dot-dots`` and any - ``directory-separators`` immediately following them. - 7. If the last filename is ``dot-dot``, remove any trailing - ``directory-separator``. - 8. If the path is empty, add a ``dot`` (normal form of ``./`` is ``.``). +When the ``NORMALIZE`` option is specified, the path is :ref:`normalized +<Normalization>` before the conversion. -.. _cmake_path-RELATIVE_PATH: -.. _RELATIVE_PATH: +For example: .. code-block:: cmake - cmake_path(RELATIVE_PATH <path-var> [BASE_DIRECTORY <input>] - [OUTPUT_VARIABLE <out-var>]) - -Returns ``<path-var>`` made relative to ``BASE_DIRECTORY`` argument. If -``BASE_DIRECTORY`` is not specified, the default base directory will be -:variable:`CMAKE_CURRENT_SOURCE_DIR`. - -For reference, the algorithm used to compute the relative path is described -`here <https://en.cppreference.com/w/cpp/filesystem/path/lexically_normal>`_. - -.. _ABSOLUTE_PATH: + set(native_path "c:\\a\\b/..\\c") + cmake_path(SET path "${native_path}") + message("CMake path is \"${path}\"") -.. code-block:: cmake + cmake_path(SET path NORMALIZE "${native_path}") + message("Normalized CMake path is \"${path}\"") - cmake_path(ABSOLUTE_PATH <path-var> [BASE_DIRECTORY <input>] [NORMALIZE] - [OUTPUT_VARIABLE <out-var>]) +Output:: -If ``<path-var>`` is a relative path (`IS_RELATIVE`_ is true), it is evaluated -relative to the given base directory specified by ``BASE_DIRECTORY`` option. + CMake path is "c:/a/b/../c" + Normalized CMake path is "c:/a/c" -If ``BASE_DIRECTORY`` is not specifired, the default base directory will be -:variable:`CMAKE_CURRENT_SOURCE_DIR`. +.. _APPEND: -When ``NORMALIZE`` option is specified, the path is :ref:`normalized -<NORMAL_PATH>` after the path computation. +:: -Because ``cmake_path`` does not access to the filesystem, symbolic links are -not resolved. To compute a real path, use :command:`file(REAL_PATH)` -command. + cmake_path(APPEND <path-var> [<input>...] [OUTPUT_VARIABLE <out-var>]) -Conversion -^^^^^^^^^^ +Append all the ``<input>`` arguments to the ``<path-var>`` using ``/`` as +the ``directory-separator``. Depending on the ``<input>``, the previous +contents of ``<path-var>`` may be discarded. For each ``<input>`` argument, +the following algorithm (pseudo-code) applies: -.. _cmake_path-NATIVE_PATH: -.. _NATIVE_PATH: +:: -.. code-block:: cmake + # <path> is the contents of <path-var> - cmake_path(NATIVE_PATH <path-var> [NORMALIZE] <out-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() -Converts a cmake-style ``<path-var>`` into a native -path with platform-specific slashes (``\`` on Windows and ``/`` elsewhere). + 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() -When ``NORMALIZE`` option is specified, the path is :ref:`normalized -<NORMAL_PATH>` before the conversion. + append <input> omitting any root-name to <path> -.. _CONVERT: -.. _cmake_path-TO_CMAKE_PATH_LIST: -.. _TO_CMAKE_PATH_LIST: +.. _APPEND_STRING: -.. code-block:: cmake +:: - cmake_path(CONVERT <input> TO_CMAKE_PATH_LIST <out-var> [NORMALIZE]) + cmake_path(APPEND_STRING <path-var> [<input>...] [OUTPUT_VARIABLE <out-var>]) -Converts a native ``<input>`` path into cmake-style path with forward-slashes -(``/``). On Windows, 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 cmake-style list separated by ``;`` characters. The -result of the conversion is stored in the ``<out-var>`` variable. +Append all the ``<input>`` arguments to the ``<path-var>`` without adding any +``directory-separator``. -When ``NORMALIZE`` option is specified, the path is :ref:`normalized -<NORMAL_PATH>` before the conversion. +.. _REMOVE_FILENAME: -.. _cmake_path-TO_NATIVE_PATH_LIST: -.. _TO_NATIVE_PATH_LIST: +:: -.. code-block:: cmake + cmake_path(REMOVE_FILENAME <path-var> [OUTPUT_VARIABLE <out-var>]) - cmake_path(CONVERT <input> TO_NATIVE_PATH_LIST <out-var> [NORMALIZE]) +Removes the :ref:`filename <FILENAME_DEF>` component (as returned by +:ref:`GET ... FILENAME <GET_FILENAME>`) from ``<path-var>``. After removal, +any trailing ``directory-separator`` is left alone, if present. -Converts a cmake-style ``<input>`` path into a native path with -platform-specific slashes (``\`` on Windows and ``/`` elsewhere). The input can -be a single path or a cmake-style list. A list will be converted into a native -search path. The result of the conversion is stored in the ``<out-var>`` -variable. +If ``OUTPUT_VARIABLE`` is not given, then after this function returns, +`HAS_FILENAME`_ returns false for ``<path-var>``. -When ``NORMALIZE`` option is specified, the path is :ref:`normalized -<NORMAL_PATH>` before the conversion. +For example: -For Example: +.. code-block:: cmake - .. code-block:: cmake + set(path "/a/b") + cmake_path(REMOVE_FILENAME path) + message("First path is \"${path}\"") - 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}\"") + # filename is now already empty, the following removes nothing + cmake_path(REMOVE_FILENAME path) + message("Second path is \"${result}\"") - Will display, on Windows:: +Output:: - Native path list is "\a\b\c;\x\y\z" + First path is "/a/" + Second path is "/a/" - And on the all other systems:: +.. _REPLACE_FILENAME: - Native path list is "/a/b/c:/x/y/z" +:: -Comparison -^^^^^^^^^^ + cmake_path(REPLACE_FILENAME <path-var> <input> [OUTPUT_VARIABLE <out-var>]) -.. _COMPARE: +Replaces the :ref:`filename <FILENAME_DEF>` 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: .. code-block:: cmake - cmake_path(COMPARE <input1> EQUAL <input2> <out-var>) - cmake_path(COMPARE <input1> NOT_EQUAL <input2> <out-var>) + cmake_path(HAS_FILENAME path has_filename) + if(has_filename) + cmake_path(REMOVE_FILENAME path) + cmake_path(APPEND path input); + endif() -Compares the lexical representations of the path and another path. +.. _REMOVE_EXTENSION: -For testing equality, the following algorithm (pseudo-code) apply: +:: - .. code-block:: cmake + cmake_path(REMOVE_EXTENSION <path-var> [LAST_ONLY] + [OUTPUT_VARIABLE <out-var>]) - IF (NOT <input1>.root_name() STREQUAL <input2>.root_name()) - returns FALSE - ELSEIF (<input1>.has_root_directory() XOR <input2>.has_root_directory()) - returns FALSE - ENDIF() +Removes the :ref:`extension <EXTENSION_DEF>`, if any, from ``<path-var>``. - returns TRUE or FALSE if the relative portion of <input1> is - lexicographically equal or not to the relative portion of <input2>. - Comparison is performed path component-wise +.. _REPLACE_EXTENSION: -Query -^^^^^ +:: -.. _HAS_ROOT_NAME: + cmake_path(REPLACE_EXTENSION <path-var> [LAST_ONLY] <input> + [OUTPUT_VARIABLE <out-var>]) + +Replaces the :ref:`extension <EXTENSION_DEF>` with ``<input>``. Its effect +is equivalent to the following: .. code-block:: cmake - cmake_path(HAS_ROOT_NAME <path-var> <out-var>) + cmake_path(REMOVE_EXTENSION path) + if(NOT "input" MATCHES "^\\.") + cmake_path(APPEND_STRING path ".") + endif() + cmake_path(APPEND_STRING path "input") -Checks if ``<path-var>`` has ``root-name``. -.. _HAS_ROOT_DIRECTORY: +Generation +^^^^^^^^^^ -.. code-block:: cmake +.. _NORMAL_PATH: - cmake_path(HAS_ROOT_DIRECTORY <path-var> <out-var>) +:: -Checks if ``<path-var>`` has ``root-directory``. + cmake_path(NORMAL_PATH <path-var> [OUTPUT_VARIABLE <out-var>]) -.. _HAS_ROOT_PATH: +Normalize ``<path-var>`` according the steps described in :ref:`Normalization`. -.. code-block:: cmake +.. _cmake_path-RELATIVE_PATH: +.. _RELATIVE_PATH: - cmake_path(HAS_ROOT_PATH <path-var> <out-var>) +:: -Checks if ``<path-var>`` has root path. + cmake_path(RELATIVE_PATH <path-var> [BASE_DIRECTORY <input>] + [OUTPUT_VARIABLE <out-var>]) -Effectively, checks if ``<path-var>`` has ``root-name`` and ``root-directory``. +Modifies ``<path-var>`` to make it relative to the ``BASE_DIRECTORY`` argument. +If ``BASE_DIRECTORY`` is not specified, the default base directory will be +:variable:`CMAKE_CURRENT_SOURCE_DIR`. -.. _HAS_FILENAME: +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/w/cpp/filesystem/path/lexically_normal>`_. -.. code-block:: cmake +.. _ABSOLUTE_PATH: - cmake_path(HAS_FILENAME <path-var> <out-var>) +:: -Checks if ``<path-var>`` has a :ref:`filename <FILENAME_DEF>`. + cmake_path(ABSOLUTE_PATH <path-var> [BASE_DIRECTORY <input>] [NORMALIZE] + [OUTPUT_VARIABLE <out-var>]) -.. _HAS_EXTENSION: +If ``<path-var>`` is a relative path (`IS_RELATIVE`_ is true), it is evaluated +relative to the given base directory specified by ``BASE_DIRECTORY`` option. +If ``BASE_DIRECTORY`` is not specified, the default base directory will be +:variable:`CMAKE_CURRENT_SOURCE_DIR`. -.. code-block:: cmake +When the ``NORMALIZE`` option is specified, the path is :ref:`normalized +<Normalization>` after the path computation. - cmake_path(HAS_EXTENSION <path-var> <out-var>) +Because ``cmake_path()`` does not access the filesystem, symbolic links are +not resolved. To compute a real path with symbolic links resolved, use the +:command:`file(REAL_PATH)` command instead. -Checks if ``<path-var>`` has an :ref:`extension <EXTENSION_DEF>`. If the first -character in the filename is a period, it is not treated as an extension (for -example ".profile"). +Native Conversion +^^^^^^^^^^^^^^^^^ -.. _HAS_STEM: +For commands in this section, *native* refers to the host platform, not the +target platform when cross-compiling. -.. code-block:: cmake +.. _cmake_path-NATIVE_PATH: +.. _NATIVE_PATH: - cmake_path(HAS_STEM <path-var> <out-var>) +:: -Checks if ``<path-var>`` has stem (:ref:`GET ... STEM <GET_STEM>` returns a non -empty path). + cmake_path(NATIVE_PATH <path-var> [NORMALIZE] <out-var>) -.. _HAS_RELATIVE_PATH: +Converts a cmake-style ``<path-var>`` into a native path with +platform-specific slashes (``\`` on Windows hosts and ``/`` elsewhere). -.. code-block:: cmake - - cmake_path(HAS_RELATIVE_PATH <path-var> <out-var>) +When the ``NORMALIZE`` option is specified, the path is :ref:`normalized +<Normalization>` before the conversion. -Checks if ``<path-var>`` has relative path (`GET_RELATIVE_PATH`_ returns a -non-empty path). +.. _CONVERT: +.. _cmake_path-TO_CMAKE_PATH_LIST: +.. _TO_CMAKE_PATH_LIST: -.. _HAS_PARENT_PATH: +:: -.. code-block:: cmake + cmake_path(CONVERT <input> TO_CMAKE_PATH_LIST <out-var> [NORMALIZE]) - cmake_path(HAS_PARENT_PATH <path-var> <out-var>) +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 cmake-style list +separated by ``;`` characters (on non-Windows platforms, this essentially +means ``:`` separators are replaced with ``;``). The result of the +conversion is stored in the ``<out-var>`` variable. -Checks if ``<path-var>`` has parent path. The result is true except if the path -is only composed of a :ref:`filename <FILENAME_DEF>`. +When the ``NORMALIZE`` option is specified, the path is :ref:`normalized +<Normalization>` before the conversion. -.. _IS_ABSOLUTE: +.. note:: + Unlike most other ``cmake_path()`` subcommands, the ``CONVERT`` subcommand + takes a literal string as input, not the name of a variable. -.. code-block:: cmake +.. _cmake_path-TO_NATIVE_PATH_LIST: +.. _TO_NATIVE_PATH_LIST: - cmake_path(IS_ABSOLUTE <path-var> <out-var>) +:: -Checks if ``<path-var>`` is absolute. + cmake_path(CONVERT <input> TO_NATIVE_PATH_LIST <out-var> [NORMALIZE]) -An absolute path is a path that unambiguously identifies the location of a file -without reference to an additional starting location. +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 cmake-style list. A list will be +converted into a native search path (``;``-separated on Windows, +``:``-separated on other platforms). The result of the conversion is +stored in the ``<out-var>`` variable. -.. _IS_RELATIVE: +When the ``NORMALIZE`` option is specified, the path is :ref:`normalized +<Normalization>` before the conversion. -.. code-block:: cmake +.. note:: + Unlike most other ``cmake_path()`` subcommands, the ``CONVERT`` subcommand + takes a literal string as input, not the name of a variable. - cmake_path(IS_RELATIVE <path-var> <out-var>) +For example: -Checks if path is relative (i.e. not :ref:`absolute <IS_ABSOLUTE>`). +.. code-block:: cmake -.. _IS_PREFIX: + 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}\"") -.. code-block:: cmake +Output on Windows:: - cmake_path(IS_PREFIX <path-var> <input> [NORMALIZE] <out-var>) + Native path list is "\a\b\c;\x\y\z" -Checks if ``<path-var>`` is the prefix of ``<input>``. +Output on all other platforms:: -When ``NORMALIZE`` option is specified, the paths are :ref:`normalized -<NORMAL_PATH>` before the check. + Native path list is "/a/b/c:/x/y/z" Hashing ^^^^^^^ .. _HASH: -.. code-block:: cmake - - cmake_path(HASH <path-var> [NORMALIZE] <out-var>) +:: -Compute hash value of ``<path-var>`` such that if for two paths (``p1`` and -``p2``) are equal (:ref:`COMPARE ... EQUAL <COMPARE>`) then hash value of p1 is -equal to hash value of p2. + cmake_path(HASH <path-var> <out-var>) -When ``NORMALIZE`` option is specified, the paths are :ref:`normalized -<NORMAL_PATH>` before the check. +Compute a hash value of ``<path-var>`` such that for two paths ``p1`` and +``p2`` that compare equal (:ref:`COMPARE ... EQUAL <COMPARE>`), the hash +value of ``p1`` is equal to the hash value of ``p2``. The path is always +:ref:`normalized <Normalization>` before the hash is computed. diff --git a/Help/command/if.rst b/Help/command/if.rst index 72d328d..c51d2bc 100644 --- a/Help/command/if.rst +++ b/Help/command/if.rst @@ -47,7 +47,8 @@ as ``EXISTS``, ``COMMAND``, and ``DEFINED``. Then binary tests such as and ``MATCHES``. Then the boolean operators in the order ``NOT``, ``AND``, and finally ``OR``. -Possible conditions are: +Basic Expressions +""""""""""""""""" ``if(<constant>)`` True if the constant is ``1``, ``ON``, ``YES``, ``TRUE``, ``Y``, @@ -62,6 +63,9 @@ Possible conditions are: True if given a variable that is defined to a value that is not a false constant. False otherwise. (Note macro arguments are not variables.) +Logic Operators +""""""""""""""" + ``if(NOT <condition>)`` True if the condition is not true. @@ -71,6 +75,15 @@ Possible conditions are: ``if(<cond1> OR <cond2>)`` True if either condition would be considered true individually. +``if((condition) AND (condition OR (condition)))`` + The conditions inside the parenthesis are evaluated first and then + the remaining condition is evaluated as in the other examples. + Where there are nested parenthesis the innermost are 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. @@ -89,6 +102,21 @@ Possible conditions are: True if the given name is an existing test name created by the :command:`add_test` command. +``if(DEFINED <name>|CACHE{<name>}|ENV{<name>})`` + True if a variable, cache variable or environment variable + with given ``<name>`` is defined. The value of the variable + does not matter. Note that macro arguments are not variables. + + .. versionadded:: 3.14 + Added support for ``CACHE{<name>}`` variables. + +``if(<variable|string> IN_LIST <variable>)`` + .. versionadded:: 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. Behavior is well-defined only for full paths. Resolves symbolic links, i.e. if the named file or @@ -114,6 +142,9 @@ Possible conditions are: ``if(IS_ABSOLUTE path)`` True if the given path is an absolute path. +Comparisons +""""""""""" + ``if(<variable|string> MATCHES regex)`` True if the given string or variable's value matches the given regular condition. See :ref:`Regex Specification` for regex format. @@ -165,6 +196,9 @@ Possible conditions are: 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 +""""""""""""""""""" + ``if(<variable|string> VERSION_LESS <variable|string>)`` Component-wise integer version number comparison (version format is ``major[.minor[.patch[.tweak]]]``, omitted components are treated as zero). @@ -197,24 +231,6 @@ Possible conditions are: Any non-integer version component or non-integer trailing part of a version component effectively truncates the string at that point. -``if(<variable|string> IN_LIST <variable>)`` - .. versionadded:: 3.3 - True if the given element is contained in the named list variable. - -``if(DEFINED <name>|CACHE{<name>}|ENV{<name>})`` - True if a variable, cache variable or environment variable - with given ``<name>`` is defined. The value of the variable - does not matter. Note that macro arguments are not variables. - - .. versionadded:: 3.14 - Added support for ``CACHE{<name>}`` variables. - -``if((condition) AND (condition OR (condition)))`` - The conditions inside the parenthesis are evaluated first and then - the remaining condition is evaluated as in the previous examples. - Where there are nested parenthesis the innermost are evaluated as part - of evaluating the condition that contains them. - Variable Expansion ^^^^^^^^^^^^^^^^^^ diff --git a/Help/command/install.rst b/Help/command/install.rst index bd8da39..35207f4 100644 --- a/Help/command/install.rst +++ b/Help/command/install.rst @@ -473,6 +473,11 @@ this advice while installing headers to a project-specific subdirectory: use "generator expressions" with the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` manual for available expressions. +.. versionadded:: 3.20 + An install rename given as a ``RENAME`` argument may + use "generator expressions" with the syntax ``$<...>``. See the + :manual:`cmake-generator-expressions(7)` manual for available expressions. + Installing Directories ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/Help/generator/Ninja Multi-Config.rst b/Help/generator/Ninja Multi-Config.rst index d1df42b..8901192 100644 --- a/Help/generator/Ninja Multi-Config.rst +++ b/Help/generator/Ninja Multi-Config.rst @@ -130,3 +130,34 @@ output config using the ``Release`` command config. The ``Release`` build of the ``generator`` target is called with ``Debug.txt Debug Release`` as arguments. The command depends on the ``Release`` builds of ``tgt1`` and ``tgt4``, and the ``Debug`` builds of ``tgt2`` and ``tgt3``. + +``PRE_BUILD``, ``PRE_LINK``, and ``POST_BUILD`` custom commands for targets +only get run in their "native" configuration (the ``Release`` configuration in +the ``build-Release.ninja`` file) unless they have no ``BYPRODUCTS`` or their +``BYPRODUCTS`` are unique per config. Consider the following example: + +.. code-block:: cmake + + add_executable(exe main.c) + add_custom_command( + TARGET exe + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo "Running no-byproduct command" + ) + add_custom_command( + TARGET exe + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo "Running separate-byproduct command for $<CONFIG>" + BYPRODUCTS $<CONFIG>.txt + ) + add_custom_command( + TARGET exe + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo "Running common-byproduct command for $<CONFIG>" + BYPRODUCTS exe.txt + ) + +In this example, if you build ``exe:Debug`` in ``build-Release.ninja``, the +first and second custom commands get run, since their byproducts are unique +per-config, but the last custom command does not. However, if you build +``exe:Release`` in ``build-Release.ninja``, all three custom commands get run. diff --git a/Help/guide/tutorial/index.rst b/Help/guide/tutorial/index.rst index 00fa39a..94753d5 100644 --- a/Help/guide/tutorial/index.rst +++ b/Help/guide/tutorial/index.rst @@ -414,27 +414,23 @@ tutorial assume that they are not common. If the platform has ``log`` and ``exp`` then we will use them to compute the square root in the ``mysqrt`` function. We first test for the availability of -these functions using the :module:`CheckSymbolExists` module in the top-level -``CMakeLists.txt``. On some platforms, we will need to link to the m library. -If ``log`` and ``exp`` are not initially found, require the m library and try -again. - -We're going to use the new defines in ``TutorialConfig.h.in``, so be sure to -set them before that file is configured. +these functions using the :module:`CheckSymbolExists` module in +``MathFunctions/CMakeLists.txt``. On some platforms, we will need to link to +the m library. If ``log`` and ``exp`` are not initially found, require the m +library and try again. .. literalinclude:: Step6/MathFunctions/CMakeLists.txt :language: cmake :start-after: # does this system provide the log and exp functions? :end-before: # add compile definitions -Now let's add these defines to ``TutorialConfig.h.in`` so that we can use them -from ``mysqrt.cxx``: - -.. code-block:: console +If available, use :command:`target_compile_definitions` to specify +``HAVE_LOG`` and ``HAVE_EXP`` as ``PRIVATE`` compile definitions. - // does the platform provide exp and log functions? - #cmakedefine HAVE_LOG - #cmakedefine HAVE_EXP +.. literalinclude:: Step6/MathFunctions/CMakeLists.txt + :language: cmake + :start-after: # add compile definitions + :end-before: # install rules If ``log`` and ``exp`` are available on the system, then we will use them to compute the square root in the ``mysqrt`` function. Add the following code to @@ -456,51 +452,8 @@ Run the :manual:`cmake <cmake(1)>` executable or the :manual:`cmake-gui <cmake-gui(1)>` to configure the project and then build it with your chosen build tool and run the Tutorial executable. -You will notice that we're not using ``log`` and ``exp``, even if we think they -should be available. We should realize quickly that we have forgotten to -include ``TutorialConfig.h`` in ``mysqrt.cxx``. - -We will also need to update ``MathFunctions/CMakeLists.txt`` so ``mysqrt.cxx`` -knows where this file is located: - -.. code-block:: cmake - - target_include_directories(MathFunctions - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} - PRIVATE ${CMAKE_BINARY_DIR} - ) - -After making this update, go ahead and build the project again and run the -built Tutorial executable. If ``log`` and ``exp`` are still not being used, -open the generated ``TutorialConfig.h`` file from the build directory. Maybe -they aren't available on the current system? - Which function gives better results now, sqrt or mysqrt? -Specify Compile Definition --------------------------- - -Is there a better place for us to save the ``HAVE_LOG`` and ``HAVE_EXP`` values -other than in ``TutorialConfig.h``? Let's try to use -:command:`target_compile_definitions`. - -First, remove the defines from ``TutorialConfig.h.in``. We no longer need to -include ``TutorialConfig.h`` from ``mysqrt.cxx`` or the extra include in -``MathFunctions/CMakeLists.txt``. - -Next, we can move the check for ``HAVE_LOG`` and ``HAVE_EXP`` to -``MathFunctions/CMakeLists.txt`` and then specify those values as ``PRIVATE`` -compile definitions. - -.. literalinclude:: Step6/MathFunctions/CMakeLists.txt - :language: cmake - :start-after: # does this system provide the log and exp functions? - :end-before: # install rules - -After making these updates, go ahead and build the project again. Run the -built Tutorial executable and verify that the results are same as earlier in -this step. - Adding a Custom Command and Generated File (Step 6) =================================================== diff --git a/Help/manual/cmake-developer.7.rst b/Help/manual/cmake-developer.7.rst index 85ed935..af9a8ab 100644 --- a/Help/manual/cmake-developer.7.rst +++ b/Help/manual/cmake-developer.7.rst @@ -23,15 +23,14 @@ in turn link to developer guides for CMake itself. Find Modules ============ -A "find module" is a ``Find<PackageName>.cmake`` file to be loaded -by the :command:`find_package` command when invoked for ``<PackageName>``. +A "find module" is a ``Find<PackageName>.cmake`` file to be loaded by the +:command:`find_package` command when invoked for ``<PackageName>``. -The primary task of a find module is to determine whether a package -exists on the system, set the ``<PackageName>_FOUND`` variable to reflect -this and provide any variables, macros and imported targets required to -use the package. A find module is useful in cases where an upstream -library does not provide a -:ref:`config file package <Config File Packages>`. +The primary task of a find module is to determine whether a package is +available, set the ``<PackageName>_FOUND`` variable to reflect this and +provide any variables, macros and imported targets required to use the +package. A find module is useful in cases where an upstream library does +not provide a :ref:`config file package <Config File Packages>`. The traditional approach is to use variables for everything, including libraries and executables: see the `Standard Variable Names`_ section @@ -91,55 +90,92 @@ Standard Variable Names For a ``FindXxx.cmake`` module that takes the approach of setting variables (either instead of or in addition to creating imported targets), the following variable names should be used to keep things -consistent between find modules. Note that all variables start with -``Xxx_`` to make sure they do not interfere with other find modules; the -same consideration applies to macros, functions and imported targets. +consistent between Find modules. Note that all variables start with +``Xxx_``, which (unless otherwise noted) must match exactly the name +of the ``FindXxx.cmake`` file, including upper/lowercase. +This prefix on the variable names ensures that they do not conflict with +variables of other Find modules. The same pattern should also be followed +for any macros, functions and imported targets defined by the Find module. ``Xxx_INCLUDE_DIRS`` The final set of include directories listed in one variable for use by - client code. This should not be a cache entry. + client code. This should not be a cache entry (note that this also means + this variable should not be used as the result variable of a + :command:`find_path` command - see ``Xxx_INCLUDE_DIR`` below for that). ``Xxx_LIBRARIES`` - The libraries to link against to use Xxx. These should include full - paths. This should not be a cache entry. + The libraries to use with the module. These may be CMake targets, full + absolute paths to a library binary or the name of a library that the + linker must find in its search path. This should not be a cache entry + (note that this also means this variable should not be used as the + result variable of a :command:`find_library` command - see + ``Xxx_LIBRARY`` below for that). ``Xxx_DEFINITIONS`` - Definitions to use when compiling code that uses Xxx. This really - shouldn't include options such as ``-DHAS_JPEG`` that a client + The compile definitions to use when compiling code that uses the module. + This really shouldn't include options such as ``-DHAS_JPEG`` that a client source-code file uses to decide whether to ``#include <jpeg.h>`` ``Xxx_EXECUTABLE`` - Where to find the Xxx tool. - -``Xxx_Yyy_EXECUTABLE`` - Where to find the Yyy tool that comes with Xxx. + The full absolute path to an executable. In this case, ``Xxx`` might not + be the name of the module, it might be the name of the tool (usually + converted to all uppercase), assuming that tool has such a well-known name + that it is unlikely that another tool with the same name exists. It would + be appropriate to use this as the result variable of a + :command:`find_program` command. + +``Xxx_YYY_EXECUTABLE`` + Similar to ``Xxx_EXECUTABLE`` except here the ``Xxx`` is always the module + name and ``YYY`` is the tool name (again, usually fully uppercase). + Prefer this form if the tool name is not very widely known or has the + potential to clash with another tool. For greater consistency, also + prefer this form if the module provides more than one executable. ``Xxx_LIBRARY_DIRS`` Optionally, the final set of library directories listed in one - variable for use by client code. This should not be a cache entry. + variable for use by client code. This should not be a cache entry. ``Xxx_ROOT_DIR`` - Where to find the base directory of Xxx. - -``Xxx_VERSION_Yy`` - Expect Version Yy if true. Make sure at most one of these is ever true. - -``Xxx_WRAP_Yy`` - If False, do not try to use the relevant CMake wrapping command. + Where to find the base directory of the module. + +``Xxx_VERSION_VV`` + Variables of this form specify whether the ``Xxx`` module being provided + is version ``VV`` of the module. There should not be more than one + variable of this form set to true for a given module. For example, a + module ``Barry`` might have evolved over many years and gone through a + number of different major versions. Version 3 of the ``Barry`` module + might set the variable ``Barry_VERSION_3`` to true, whereas an older + version of the module might set ``Barry_VERSION_2`` to true instead. + It would be an error for both ``Barry_VERSION_3`` and ``Barry_VERSION_2`` + to both be set to true. + +``Xxx_WRAP_YY`` + When a variable of this form is set to false, it indicates that the + relevant wrapping command should not be used. The wrapping command + depends on the module, it may be implied by the module name or it might + be specified by the ``YY`` part of the variable. ``Xxx_Yy_FOUND`` - If False, optional Yy part of Xxx system is not available. + For variables of this form, ``Yy`` is the name of a component for the + module. It should match exactly one of the valid component names that + may be passed to the :command:`find_package` command for the module. + If a variable of this form is set to false, it means that the ``Yy`` + component of module ``Xxx`` was not found or is not available. + Variables of this form would typically be used for optional components + so that the caller can check whether an optional component is available. ``Xxx_FOUND`` - Set to false, or undefined, if we haven't found, or don't want to use - Xxx. + When the :command:`find_package` command returns to the caller, this + variable will be set to true if the module was deemed to have been found + successfully. ``Xxx_NOT_FOUND_MESSAGE`` Should be set by config-files in the case that it has set ``Xxx_FOUND`` to FALSE. The contained message will be printed by the :command:`find_package` command and by - ``find_package_handle_standard_args()`` to inform the user about the - problem. + :command:`find_package_handle_standard_args` to inform the user about the + problem. Use this instead of calling :command:`message` directly to + report a reason for failing to find the module or package. ``Xxx_RUNTIME_LIBRARY_DIRS`` Optionally, the runtime library search path for use when running an @@ -160,23 +196,36 @@ same consideration applies to macros, functions and imported targets. ``Xxx_VERSION_PATCH`` The patch version of the package found, if any. -The following names should not usually be used in CMakeLists.txt files, but -are typically cache variables for users to edit and control the -behaviour of find modules (like entering the path to a library manually) +The following names should not usually be used in ``CMakeLists.txt`` files. +They are intended for use by Find modules to specify and cache the locations +of specific files or directories. Users are typically able to set and edit +these variables to control the behavior of Find modules (like entering the +path to a library manually): ``Xxx_LIBRARY`` - The path of the Xxx library (as used with :command:`find_library`, for - example). + The path of the library. Use this form only when the module provides a + single library. It is appropriate to use this as the result variable + in a :command:`find_library` command. ``Xxx_Yy_LIBRARY`` - The path of the Yy library that is part of the Xxx system. It may or - may not be required to use Xxx. + The path of library ``Yy`` provided by the module ``Xxx``. Use this form + when the module provides more than one library or where other modules may + also provide a library of the same name. It is also appropriate to use + this form as the result variable in a :command:`find_library` command. ``Xxx_INCLUDE_DIR`` - Where to find headers for using the Xxx library. + When the module provides only a single library, this variable can be used + to specify where to find headers for using the library (or more accurately, + the path that consumers of the library should add to their header search + path). It would be appropriate to use this as the result variable in a + :command:`find_path` command. ``Xxx_Yy_INCLUDE_DIR`` - Where to find headers for using the Yy library of the Xxx system. + If the module provides more than one library or where other modules may + also provide a library of the same name, this form is recommended for + specifying where to find headers for using library ``Yy`` provided by + the module. Again, it would be appropriate to use this as the result + variable in a :command:`find_path` command. To prevent users being overwhelmed with settings to configure, try to keep as many options as possible out of the cache, leaving at least one @@ -185,7 +234,8 @@ not-found library (e.g. ``Xxx_ROOT_DIR``). For the same reason, mark most cache options as advanced. For packages which provide both debug and release binaries, it is common to create cache variables with a ``_LIBRARY_<CONFIG>`` suffix, such as ``Foo_LIBRARY_RELEASE`` and -``Foo_LIBRARY_DEBUG``. +``Foo_LIBRARY_DEBUG``. The :module:`SelectLibraryConfigurations` module +can be helpful for such cases. While these are the standard variable names, you should provide backwards compatibility for any old names that were actually in use. diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst index 6876e1c..89739b7 100644 --- a/Help/manual/cmake-file-api.7.rst +++ b/Help/manual/cmake-file-api.7.rst @@ -1154,3 +1154,160 @@ The members specific to ``cmakeFiles`` objects are: ``isCMake`` Optional member that is present with boolean value ``true`` if the path specifies a file in the CMake installation. + +Object Kind "toolchains" +------------------------ + +The ``toolchains`` object kind lists properties of the toolchains used during +the build. These include the language, compiler path, ID, and version. + +There is only one ``toolchains`` object major version, version 1. + +"toolchains" version 1 +^^^^^^^^^^^^^^^^^^^^^^ + +``toolchains`` object version 1 is a JSON object: + +.. code-block:: json + + { + "kind": "toolchains", + "version": { "major": 1, "minor": 0 }, + "toolchains": [ + { + "language": "C", + "compiler": { + "path": "/usr/bin/cc", + "id": "GNU", + "version": "9.3.0", + "implicit": { + "includeDirectories": [ + "/usr/lib/gcc/x86_64-linux-gnu/9/include", + "/usr/local/include", + "/usr/include/x86_64-linux-gnu", + "/usr/include" + ], + "linkDirectories": [ + "/usr/lib/gcc/x86_64-linux-gnu/9", + "/usr/lib/x86_64-linux-gnu", + "/usr/lib", + "/lib/x86_64-linux-gnu", + "/lib" + ], + "linkFrameworkDirectories": [], + "linkLibraries": [ "gcc", "gcc_s", "c", "gcc", "gcc_s" ] + } + }, + "sourceFileExtensions": [ "c", "m" ] + }, + { + "language": "CXX", + "compiler": { + "path": "/usr/bin/c++", + "id": "GNU", + "version": "9.3.0", + "implicit": { + "includeDirectories": [ + "/usr/include/c++/9", + "/usr/include/x86_64-linux-gnu/c++/9", + "/usr/include/c++/9/backward", + "/usr/lib/gcc/x86_64-linux-gnu/9/include", + "/usr/local/include", + "/usr/include/x86_64-linux-gnu", + "/usr/include" + ], + "linkDirectories": [ + "/usr/lib/gcc/x86_64-linux-gnu/9", + "/usr/lib/x86_64-linux-gnu", + "/usr/lib", + "/lib/x86_64-linux-gnu", + "/lib" + ], + "linkFrameworkDirectories": [], + "linkLibraries": [ + "stdc++", "m", "gcc_s", "gcc", "c", "gcc_s", "gcc" + ] + } + }, + "sourceFileExtensions": [ + "C", "M", "c++", "cc", "cpp", "cxx", "mm", "CPP" + ] + } + ] + } + +The members specific to ``toolchains`` objects are: + +``toolchains`` + A JSON array whose entries are each a JSON object specifying a toolchain + associated with a particular language. The members of each entry are: + + ``language`` + A JSON string specifying the toolchain language, like C or CXX. Language + names are the same as langauge names that can be passed to the + :command:`project` command. Because CMake only supports a single toolchain + per language, this field can be used as a key. + + ``compiler`` + A JSON object containing members: + + ``path`` + Optional member that is present when the + :variable:`CMAKE_<LANG>_COMPILER` variable is defined for the current + language. Its value is a JSON string holding the path to the compiler. + + ``id`` + Optional member that is present when the + :variable:`CMAKE_<LANG>_COMPILER_ID` variable is defined for the current + language. Its value is a JSON string holding the ID (GNU, MSVC, etc.) of + the compiler. + + ``version`` + Optional member that is present when the + :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable is defined for the + current language. Its value is a JSON string holding the version of the + compiler. + + ``target`` + Optional member that is present when the + :variable:`CMAKE_<LANG>_COMPILER_TARGET` variable is defined for the + current language. Its value is a JSON string holding the cross-compiling + target of the compiler. + + ``implicit`` + A JSON object containing members: + + ``includeDirectories`` + Optional member that is present when the + :variable:`CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES` variable is + defined for the current language. Its value is a JSON array of JSON + strings where each string holds a path to an implicit include + directory for the compiler. + + ``linkDirectories`` + Optional member that is present when the + :variable:`CMAKE_<LANG>_IMPLICIT_LINK_DIRECTORIES` variable is + defined for the current language. Its value is a JSON array of JSON + strings where each string holds a path to an implicit link directory + for the compiler. + + ``linkFrameworkDirectories`` + Optional member that is present when the + :variable:`CMAKE_<LANG>_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES` variable + is defined for the current language. Its value is a JSON array of JSON + strings where each string holds a path to an implicit link framework + directory for the compiler. + + ``linkLibraries`` + Optional member that is present when the + :variable:`CMAKE_<LANG>_IMPLICIT_LINK_LIBRARIES` variable is defined + for the current language. Its value is a JSON array of JSON strings + where each string holds a path to an implicit link library for the + compiler. + + ``sourceFileExtensions`` + Optional member that is present when the + :variable:`CMAKE_<LANG>_SOURCE_FILE_EXTENSIONS` variable is defined for + the current language. Its value is a JSON array of JSON strings where each + each string holds a file extension (without the leading dot) for the + language. diff --git a/Help/manual/cmake-presets.7.rst b/Help/manual/cmake-presets.7.rst index 6f137c4..3ab0a62 100644 --- a/Help/manual/cmake-presets.7.rst +++ b/Help/manual/cmake-presets.7.rst @@ -29,337 +29,344 @@ is using Git, ``CMakePresets.json`` may be tracked, and Format ====== - The files are a JSON document with an object as the root: +The files are a JSON document with an object as the root: - .. literalinclude:: presets/example.json - :language: json +.. literalinclude:: presets/example.json + :language: json - The root object recognizes the following fields: +The root object recognizes the following fields: - ``version`` +``version`` - A required integer representing the version of the JSON schema. Currently, - the only supported version is 1. + A required integer representing the version of the JSON schema. Currently, + the only supported version is 1. - ``cmakeMinimumRequired`` +``cmakeMinimumRequired`` - An optional object representing the minimum version of CMake needed to - build this project. This object consists of the following fields: + An optional object representing the minimum version of CMake needed to + build this project. This object consists of the following fields: - ``major`` + ``major`` - An optional integer representing the major version. + An optional integer representing the major version. - ``minor`` + ``minor`` - An optional integer representing the minor version. + An optional integer representing the minor version. - ``patch`` + ``patch`` - An optional integer representing the patch version. + An optional integer representing the patch version. - ``vendor`` +``vendor`` - An optional map containing vendor-specific information. CMake does not - interpret the contents of this field except to verify that it is a map if - it does exist. However, the keys should be a vendor-specific domain name - followed by a ``/``-separated path. For example, the Example IDE 1.0 could - use ``example.com/ExampleIDE/1.0``. The value of each field can be anything - desired by the vendor, though will typically be a map. + An optional map containing vendor-specific information. CMake does not + interpret the contents of this field except to verify that it is a map if + it does exist. However, the keys should be a vendor-specific domain name + followed by a ``/``-separated path. For example, the Example IDE 1.0 could + use ``example.com/ExampleIDE/1.0``. The value of each field can be anything + desired by the vendor, though will typically be a map. - ``configurePresets`` +``configurePresets`` - An optional array of configure preset objects. Each preset may contain the - following fields: + An optional array of `Configure Preset`_ objects. - ``name`` +Configure Preset +^^^^^^^^^^^^^^^^ - A required string representing the machine-friendly name of the preset. - This identifier is used in the ``--preset`` argument. There must not be - two presets in the union of ``CMakePresets.json`` and - ``CMakeUserPresets.json`` in the same directory with the same name. +Each entry of the ``configurePresets`` array is a JSON object +that may contain the following fields: - ``hidden`` +``name`` - An optional boolean specifying whether or not a preset should be hidden. - If a preset is hidden, it cannot be used in the ``--preset=`` argument, - will not show up in the :manual:`CMake GUI <cmake-gui(1)>`, and does not - have to have a valid ``generator`` or ``binaryDir``, even from - inheritance. ``hidden`` presets are intended to be used as a base for - other presets to inherit via the ``inherits`` field. + A required string representing the machine-friendly name of the preset. + This identifier is used in the ``--preset`` argument. There must not be + two presets in the union of ``CMakePresets.json`` and + ``CMakeUserPresets.json`` in the same directory with the same name. - ``inherits`` +``hidden`` - An optional array of strings representing the names of presets to inherit - from. The preset will inherit all of the fields from the ``inherits`` - presets by default (except ``name``, ``hidden``, ``inherits``, - ``description``, and ``displayName``), but can override them as - desired. If multiple ``inherits`` presets provide conflicting values for - the same field, the earlier preset in the ``inherits`` list will be - preferred. Presets in ``CMakePresets.json`` may not inherit from presets - in ``CMakeUserPresets.json``. + An optional boolean specifying whether or not a preset should be hidden. + If a preset is hidden, it cannot be used in the ``--preset=`` argument, + will not show up in the :manual:`CMake GUI <cmake-gui(1)>`, and does not + have to have a valid ``generator`` or ``binaryDir``, even from + inheritance. ``hidden`` presets are intended to be used as a base for + other presets to inherit via the ``inherits`` field. - This field can also be a string, which is equivalent to an array - containing one string. +``inherits`` - ``vendor`` + An optional array of strings representing the names of presets to inherit + from. The preset will inherit all of the fields from the ``inherits`` + presets by default (except ``name``, ``hidden``, ``inherits``, + ``description``, and ``displayName``), but can override them as + desired. If multiple ``inherits`` presets provide conflicting values for + the same field, the earlier preset in the ``inherits`` list will be + preferred. Presets in ``CMakePresets.json`` may not inherit from presets + in ``CMakeUserPresets.json``. - An optional map containing vendor-specific information. CMake does not - interpret the contents of this field except to verify that it is a map - if it does exist. However, it should follow the same conventions as the - root-level ``vendor`` field. If vendors use their own per-preset - ``vendor`` field, they should implement inheritance in a sensible manner - when appropriate. + This field can also be a string, which is equivalent to an array + containing one string. - ``displayName`` +``vendor`` - An optional string with a human-friendly name of the preset. + An optional map containing vendor-specific information. CMake does not + interpret the contents of this field except to verify that it is a map + if it does exist. However, it should follow the same conventions as the + root-level ``vendor`` field. If vendors use their own per-preset + ``vendor`` field, they should implement inheritance in a sensible manner + when appropriate. - ``description`` +``displayName`` - An optional string with a human-friendly description of the preset. + An optional string with a human-friendly name of the preset. - ``generator`` +``description`` - An optional string representing the generator to use for the preset. If - ``generator`` is not specified, it must be inherited from the - ``inherits`` preset (unless this preset is ``hidden``). + An optional string with a human-friendly description of the preset. - Note that for Visual Studio generators, unlike in the command line ``-G`` - argument, you cannot include the platform name in the generator name. Use - the ``architecture`` field instead. +``generator`` - ``architecture`` - ``toolset`` + An optional string representing the generator to use for the preset. If + ``generator`` is not specified, it must be inherited from the + ``inherits`` preset (unless this preset is ``hidden``). - Optional fields representing the platform and toolset, respectively, for - generators that support them. Each may be either a string or an object - with the following fields: + Note that for Visual Studio generators, unlike in the command line ``-G`` + argument, you cannot include the platform name in the generator name. Use + the ``architecture`` field instead. - ``value`` +``architecture``, ``toolset`` - An optional string representing the value. + Optional fields representing the platform and toolset, respectively, for + generators that support them. Each may be either a string or an object + with the following fields: - ``strategy`` + ``value`` - An optional string telling CMake how to handle the ``architecture`` or - ``toolset`` field. Valid values are: + An optional string representing the value. - ``"set"`` + ``strategy`` - Set the respective value. This will result in an error for generators - that do not support the respective field. + An optional string telling CMake how to handle the ``architecture`` or + ``toolset`` field. Valid values are: - ``"external"`` + ``"set"`` - Do not set the value, even if the generator supports it. This is - useful if, for example, a preset uses the Ninja generator, and an IDE - knows how to set up the Visual C++ environment from the - ``architecture`` and ``toolset`` fields. In that case, CMake will - ignore the field, but the IDE can use them to set up the environment - before invoking CMake. + Set the respective value. This will result in an error for generators + that do not support the respective field. - ``binaryDir`` + ``"external"`` - An optional string representing the path to the output binary directory. - This field supports macro expansion. If a relative path is specified, it - is calculated relative to the source directory. If ``binaryDir`` is not - specified, it must be inherited from the ``inherits`` preset (unless this - preset is ``hidden``). + Do not set the value, even if the generator supports it. This is + useful if, for example, a preset uses the Ninja generator, and an IDE + knows how to set up the Visual C++ environment from the + ``architecture`` and ``toolset`` fields. In that case, CMake will + ignore the field, but the IDE can use them to set up the environment + before invoking CMake. - ``cmakeExecutable`` +``binaryDir`` - An optional string representing the path to the CMake executable to use - for this preset. This is reserved for use by IDEs, and is not used by - CMake itself. IDEs that use this field should expand any macros in it. + An optional string representing the path to the output binary directory. + This field supports `macro expansion`_. If a relative path is specified, + it is calculated relative to the source directory. If ``binaryDir`` is not + specified, it must be inherited from the ``inherits`` preset (unless this + preset is ``hidden``). - ``cacheVariables`` +``cmakeExecutable`` - An optional map of cache variables. The key is the variable name (which - may not be an empty string), and the value is either ``null``, a boolean - (which is equivalent to a value of ``"TRUE"`` or ``"FALSE"`` and a type - of ``BOOL``), a string representing the value of the variable (which - supports macro expansion), or an object with the following fields: + An optional string representing the path to the CMake executable to use + for this preset. This is reserved for use by IDEs, and is not used by + CMake itself. IDEs that use this field should expand any macros in it. - ``type`` +``cacheVariables`` - An optional string representing the type of the variable. + An optional map of cache variables. The key is the variable name (which + may not be an empty string), and the value is either ``null``, a boolean + (which is equivalent to a value of ``"TRUE"`` or ``"FALSE"`` and a type + of ``BOOL``), a string representing the value of the variable (which + supports `macro expansion`_), or an object with the following fields: - ``value`` + ``type`` - A required string or boolean representing the value of the variable. - A boolean is equivalent to ``"TRUE"`` or ``"FALSE"``. This field - supports macro expansion. + An optional string representing the type of the variable. - Cache variables are inherited through the ``inherits`` field, and the - preset's variables will be the union of its own ``cacheVariables`` and - the ``cacheVariables`` from all its parents. If multiple presets in this - union define the same variable, the standard rules of ``inherits`` are - applied. Setting a variable to ``null`` causes it to not be set, even if - a value was inherited from another preset. + ``value`` - ``environment`` + A required string or boolean representing the value of the variable. + A boolean is equivalent to ``"TRUE"`` or ``"FALSE"``. This field + supports `macro expansion`_. - An optional map of environment variables. The key is the variable name - (which may not be an empty string), and the value is either ``null`` or - a string representing the value of the variable. Each variable is set - regardless of whether or not a value was given to it by the process's - environment. This field supports macro expansion, and environment - variables in this map may reference each other, and may be listed in any - order, as long as such references do not cause a cycle (for example, - if ``ENV_1`` is ``$env{ENV_2}``, ``ENV_2`` may not be ``$env{ENV_1}``.) + Cache variables are inherited through the ``inherits`` field, and the + preset's variables will be the union of its own ``cacheVariables`` and + the ``cacheVariables`` from all its parents. If multiple presets in this + union define the same variable, the standard rules of ``inherits`` are + applied. Setting a variable to ``null`` causes it to not be set, even if + a value was inherited from another preset. - Environment variables are inherited through the ``inherits`` field, and - the preset's environment will be the union of its own ``environment`` and - the ``environment`` from all its parents. If multiple presets in this - union define the same variable, the standard rules of ``inherits`` are - applied. Setting a variable to ``null`` causes it to not be set, even if - a value was inherited from another preset. +``environment`` - ``warnings`` + An optional map of environment variables. The key is the variable name + (which may not be an empty string), and the value is either ``null`` or + a string representing the value of the variable. Each variable is set + regardless of whether or not a value was given to it by the process's + environment. This field supports `macro expansion`_, and environment + variables in this map may reference each other, and may be listed in any + order, as long as such references do not cause a cycle (for example, + if ``ENV_1`` is ``$env{ENV_2}``, ``ENV_2`` may not be ``$env{ENV_1}``.) - An optional object specifying the warnings to enable. The object may - contain the following fields: + Environment variables are inherited through the ``inherits`` field, and + the preset's environment will be the union of its own ``environment`` and + the ``environment`` from all its parents. If multiple presets in this + union define the same variable, the standard rules of ``inherits`` are + applied. Setting a variable to ``null`` causes it to not be set, even if + a value was inherited from another preset. - ``dev`` +``warnings`` - An optional boolean. Equivalent to passing ``-Wdev`` or ``-Wno-dev`` - on the command line. This may not be set to ``false`` if ``errors.dev`` - is set to ``true``. + An optional object specifying the warnings to enable. The object may + contain the following fields: - ``deprecated`` + ``dev`` - An optional boolean. Equivalent to passing ``-Wdeprecated`` or - ``-Wno-deprecated`` on the command line. This may not be set to - ``false`` if ``errors.deprecated`` is set to ``true``. + An optional boolean. Equivalent to passing ``-Wdev`` or ``-Wno-dev`` + on the command line. This may not be set to ``false`` if ``errors.dev`` + is set to ``true``. - ``uninitialized`` + ``deprecated`` - An optional boolean. Setting this to ``true`` is equivalent to passing - ``--warn-uninitialized`` on the command line. + An optional boolean. Equivalent to passing ``-Wdeprecated`` or + ``-Wno-deprecated`` on the command line. This may not be set to + ``false`` if ``errors.deprecated`` is set to ``true``. - ``unusedCli`` + ``uninitialized`` - An optional boolean. Setting this to ``false`` is equivalent to passing - ``--no-warn-unused-cli`` on the command line. + An optional boolean. Setting this to ``true`` is equivalent to passing + ``--warn-uninitialized`` on the command line. - ``systemVars`` + ``unusedCli`` - An optional boolean. Setting this to ``true`` is equivalent to passing - ``--check-system-vars`` on the command line. + An optional boolean. Setting this to ``false`` is equivalent to passing + ``--no-warn-unused-cli`` on the command line. - ``errors`` + ``systemVars`` - An optional object specifying the errors to enable. The object may - contain the following fields: + An optional boolean. Setting this to ``true`` is equivalent to passing + ``--check-system-vars`` on the command line. - ``dev`` +``errors`` - An optional boolean. Equivalent to passing ``-Werror=dev`` or - ``-Wno-error=dev`` on the command line. This may not be set to ``true`` - if ``warnings.dev`` is set to ``false``. + An optional object specifying the errors to enable. The object may + contain the following fields: - ``deprecated`` + ``dev`` - An optional boolean. Equivalent to passing ``-Werror=deprecated`` or - ``-Wno-error=deprecated`` on the command line. This may not be set to - ``true`` if ``warnings.deprecated`` is set to ``false``. + An optional boolean. Equivalent to passing ``-Werror=dev`` or + ``-Wno-error=dev`` on the command line. This may not be set to ``true`` + if ``warnings.dev`` is set to ``false``. - ``debug`` + ``deprecated`` - An optional object specifying debug options. The object may contain the - following fields: + An optional boolean. Equivalent to passing ``-Werror=deprecated`` or + ``-Wno-error=deprecated`` on the command line. This may not be set to + ``true`` if ``warnings.deprecated`` is set to ``false``. - ``output`` +``debug`` - An optional boolean. Setting this to ``true`` is equivalent to passing - ``--debug-output`` on the command line. + An optional object specifying debug options. The object may contain the + following fields: - ``tryCompile`` + ``output`` - An optional boolean. Setting this to ``true`` is equivalent to passing - ``--debug-trycompile`` on the command line. + An optional boolean. Setting this to ``true`` is equivalent to passing + ``--debug-output`` on the command line. - ``find`` + ``tryCompile`` - An optional boolean. Setting this to ``true`` is equivalent to passing - ``--debug-find`` on the command line. + An optional boolean. Setting this to ``true`` is equivalent to passing + ``--debug-trycompile`` on the command line. - As mentioned above, some fields support macro expansion. Macros are - recognized in the form ``$<macro-namespace>{<macro-name>}``. All macros are - evaluated in the context of the preset being used, even if the macro is in a - field that was inherited from another preset. For example, if the ``Base`` - preset sets variable ``PRESET_NAME`` to ``${presetName}``, and the - ``Derived`` preset inherits from ``Base``, ``PRESET_NAME`` will be set to - ``Derived``. + ``find`` - It is an error to not put a closing brace at the end of a macro name. For - example, ``${sourceDir`` is invalid. A dollar sign (``$``) followed by - anything other than a left curly brace (``{``) with a possible namespace is - interpreted as a literal dollar sign. + An optional boolean. Setting this to ``true`` is equivalent to passing + ``--debug-find`` on the command line. - Recognized macros include: +Macro Expansion +^^^^^^^^^^^^^^^ - ``${sourceDir}`` +As mentioned above, some fields support macro expansion. Macros are +recognized in the form ``$<macro-namespace>{<macro-name>}``. All macros are +evaluated in the context of the preset being used, even if the macro is in a +field that was inherited from another preset. For example, if the ``Base`` +preset sets variable ``PRESET_NAME`` to ``${presetName}``, and the +``Derived`` preset inherits from ``Base``, ``PRESET_NAME`` will be set to +``Derived``. - Path to the project source directory. +It is an error to not put a closing brace at the end of a macro name. For +example, ``${sourceDir`` is invalid. A dollar sign (``$``) followed by +anything other than a left curly brace (``{``) with a possible namespace is +interpreted as a literal dollar sign. - ``${sourceParentDir}`` +Recognized macros include: - Path to the project source directory's parent directory. +``${sourceDir}`` - ``${sourceDirName}`` + Path to the project source directory. - The last filename component of ``${sourceDir}``. For example, if - ``${sourceDir}`` is ``/path/to/source``, this would be ``source``. +``${sourceParentDir}`` - ``${presetName}`` + Path to the project source directory's parent directory. - Name specified in the preset's ``name`` field. +``${sourceDirName}`` - ``${generator}`` + The last filename component of ``${sourceDir}``. For example, if + ``${sourceDir}`` is ``/path/to/source``, this would be ``source``. - Generator specified in the preset's ``generator`` field. +``${presetName}`` - ``${dollar}`` + Name specified in the preset's ``name`` field. - A literal dollar sign (``$``). +``${generator}`` - ``$env{<variable-name>}`` + Generator specified in the preset's ``generator`` field. - Environment variable with name ``<variable-name>``. The variable name may - not be an empty string. If the variable is defined in the ``environment`` - field, that value is used instead of the value from the parent environment. - If the environment variable is not defined, this evaluates as an empty - string. +``${dollar}`` - Note that while Windows environment variable names are case-insensitive, - variable names within a preset are still case-sensitive. This may lead to - unexpected results when using inconsistent casing. For best results, keep - the casing of environment variable names consistent. + A literal dollar sign (``$``). - ``$penv{<variable-name>}`` +``$env{<variable-name>}`` - Similar to ``$env{<variable-name>}``, except that the value only comes from - the parent environment, and never from the ``environment`` field. This - allows you to prepend or append values to existing environment variables. - For example, setting ``PATH`` to ``/path/to/ninja/bin:$penv{PATH}`` will - prepend ``/path/to/ninja/bin`` to the ``PATH`` environment variable. This - is needed because ``$env{<variable-name>}`` does not allow circular - references. + Environment variable with name ``<variable-name>``. The variable name may + not be an empty string. If the variable is defined in the ``environment`` + field, that value is used instead of the value from the parent environment. + If the environment variable is not defined, this evaluates as an empty + string. - ``$vendor{<macro-name>}`` + Note that while Windows environment variable names are case-insensitive, + variable names within a preset are still case-sensitive. This may lead to + unexpected results when using inconsistent casing. For best results, keep + the casing of environment variable names consistent. - An extension point for vendors to insert their own macros. CMake will not - be able to use presets which have a ``$vendor{<macro-name>}`` macro, and - effectively ignores such presets. However, it will still be able to use - other presets from the same file. +``$penv{<variable-name>}`` - CMake does not make any attempt to interpret ``$vendor{<macro-name>}`` - macros. However, to avoid name collisions, IDE vendors should prefix - ``<macro-name>`` with a very short (preferably <= 4 characters) vendor - identifier prefix, followed by a ``.``, followed by the macro name. For - example, the Example IDE could have ``$vendor{xide.ideInstallDir}``. + Similar to ``$env{<variable-name>}``, except that the value only comes from + the parent environment, and never from the ``environment`` field. This + allows you to prepend or append values to existing environment variables. + For example, setting ``PATH`` to ``/path/to/ninja/bin:$penv{PATH}`` will + prepend ``/path/to/ninja/bin`` to the ``PATH`` environment variable. This + is needed because ``$env{<variable-name>}`` does not allow circular + references. + +``$vendor{<macro-name>}`` + + An extension point for vendors to insert their own macros. CMake will not + be able to use presets which have a ``$vendor{<macro-name>}`` macro, and + effectively ignores such presets. However, it will still be able to use + other presets from the same file. + + CMake does not make any attempt to interpret ``$vendor{<macro-name>}`` + macros. However, to avoid name collisions, IDE vendors should prefix + ``<macro-name>`` with a very short (preferably <= 4 characters) vendor + identifier prefix, followed by a ``.``, followed by the macro name. For + example, the Example IDE could have ``$vendor{xide.ideInstallDir}``. Schema ====== diff --git a/Help/release/dev/cuda-nvcc-ccache-symlink.rst b/Help/release/dev/cuda-nvcc-ccache-symlink.rst new file mode 100644 index 0000000..4d38047 --- /dev/null +++ b/Help/release/dev/cuda-nvcc-ccache-symlink.rst @@ -0,0 +1,9 @@ +cuda-nvcc-ccache-symlink +------------------------ + +* ``CUDA`` language support now works when ``nvcc`` is a symbolic link, + for example due to a ``ccache`` or ``colornvcc`` wrapper script. + +* The :module:`FindCUDAToolkit` module gained support for finding CUDA + toolkits when ``nvcc`` is a symbolic link, + for example due to a ``ccache`` or ``colornvcc`` wrapper script. diff --git a/Help/release/dev/external-project-configure-handled-by-build.rst b/Help/release/dev/external-project-configure-handled-by-build.rst new file mode 100644 index 0000000..4a1fac8 --- /dev/null +++ b/Help/release/dev/external-project-configure-handled-by-build.rst @@ -0,0 +1,8 @@ +external-project-configure-handled-by-build +------------------------------------------- + +* The :module:`ExternalProject` function ``ExternalProject_Add`` learned a new + ``CONFIGURE_HANDLED_BY_BUILD`` option to have subsequent runs of the configure + step be triggered by the build step when an external project dependency + rebuilds instead of always rerunning the configure step when an external + project dependency rebuilds. diff --git a/Help/release/dev/install-files-rename-genex.rst b/Help/release/dev/install-files-rename-genex.rst new file mode 100644 index 0000000..f735e24 --- /dev/null +++ b/Help/release/dev/install-files-rename-genex.rst @@ -0,0 +1,5 @@ +install-files-rename-genex +-------------------------- + +* The :command:`install(FILES)` command ``RENAME`` option learned to + support :manual:`generator expressions <cmake-generator-expressions(7)>`. diff --git a/Help/variable/CMAKE_CUDA_ARCHITECTURES.rst b/Help/variable/CMAKE_CUDA_ARCHITECTURES.rst index 7f7e679..d885516 100644 --- a/Help/variable/CMAKE_CUDA_ARCHITECTURES.rst +++ b/Help/variable/CMAKE_CUDA_ARCHITECTURES.rst @@ -18,3 +18,18 @@ and compiler versions. This variable is used to initialize the :prop_tgt:`CUDA_ARCHITECTURES` property on all targets. See the target property for additional information. + +Examples +^^^^^^^^ + +.. code-block:: cmake + + cmake_minimum_required(VERSION) + + if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES) + set(CMAKE_CUDA_ARCHITECTURES 75) + endif() + + project(example LANGUAGES CUDA) + +``CMAKE_CUDA_ARCHITECTURES`` will default to ``75`` unless overridden by the user. diff --git a/Help/variable/CTEST_MEMORYCHECK_SANITIZER_OPTIONS.rst b/Help/variable/CTEST_MEMORYCHECK_SANITIZER_OPTIONS.rst index 6cd51fa..b6fee2e 100644 --- a/Help/variable/CTEST_MEMORYCHECK_SANITIZER_OPTIONS.rst +++ b/Help/variable/CTEST_MEMORYCHECK_SANITIZER_OPTIONS.rst @@ -5,3 +5,8 @@ CTEST_MEMORYCHECK_SANITIZER_OPTIONS Specify the CTest ``MemoryCheckSanitizerOptions`` setting in a :manual:`ctest(1)` dashboard client script. + +CTest prepends correct sanitizer options ``*_OPTIONS`` +environment variable to executed command. CTests adds +its own ``log_path`` to sanitizer options, don't provide your +own ``log_path``. diff --git a/Modules/CMakeDetermineCCompiler.cmake b/Modules/CMakeDetermineCCompiler.cmake index ae3abe9..ab33b40 100644 --- a/Modules/CMakeDetermineCCompiler.cmake +++ b/Modules/CMakeDetermineCCompiler.cmake @@ -161,9 +161,10 @@ if (NOT _CMAKE_TOOLCHAIN_PREFIX) if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang|QCC") get_filename_component(COMPILER_BASENAME "${CMAKE_C_COMPILER}" NAME) - if (COMPILER_BASENAME MATCHES "^(.+-)(clang|g?cc)(-[0-9]+(\\.[0-9]+)*)?(-[^.]+)?(\\.exe)?$") + if (COMPILER_BASENAME MATCHES "^(.+-)?(clang|g?cc)(-cl)?(-[0-9]+(\\.[0-9]+)*)?(-[^.]+)?(\\.exe)?$") set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_MATCH_1}) - set(_CMAKE_COMPILER_SUFFIX ${CMAKE_MATCH_5}) + set(_CMAKE_TOOLCHAIN_SUFFIX ${CMAKE_MATCH_4}) + set(_CMAKE_COMPILER_SUFFIX ${CMAKE_MATCH_6}) elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") if(CMAKE_C_COMPILER_TARGET) set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_C_COMPILER_TARGET}-) diff --git a/Modules/CMakeDetermineCUDACompiler.cmake b/Modules/CMakeDetermineCUDACompiler.cmake index 578729c..c77fc3a 100644 --- a/Modules/CMakeDetermineCUDACompiler.cmake +++ b/Modules/CMakeDetermineCUDACompiler.cmake @@ -172,7 +172,20 @@ if(NOT CMAKE_CUDA_COMPILER_ID_RUN) endif() endif() - get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${_CUDA_NVCC_EXECUTABLE}" DIRECTORY) + # If NVCC is a symlink due to a wrapper script (e.g. ccache or colornvcc), then invoke it to find the + # real non-scattered toolkit. + if(IS_SYMLINK ${_CUDA_NVCC_EXECUTABLE}) + execute_process(COMMAND ${_CUDA_NVCC_EXECUTABLE} "-v" "__cmake_determine_cuda" ERROR_VARIABLE NVCC_ERR) + if(NVCC_ERR MATCHES " _HERE_=([^\r\n]*)") + set(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${CMAKE_MATCH_1}") + else() + message(FATAL_ERROR "Could not execute nvcc with -v.") + endif() + unset(NVCC_ERR) + else() + get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${_CUDA_NVCC_EXECUTABLE}" DIRECTORY) + endif() + set(CMAKE_CUDA_DEVICE_LINKER "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}/nvlink${CMAKE_EXECUTABLE_SUFFIX}") set(CMAKE_CUDA_FATBINARY "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}/fatbinary${CMAKE_EXECUTABLE_SUFFIX}") get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}" DIRECTORY) diff --git a/Modules/CMakeDetermineCXXCompiler.cmake b/Modules/CMakeDetermineCXXCompiler.cmake index 905eb25..7283bc2 100644 --- a/Modules/CMakeDetermineCXXCompiler.cmake +++ b/Modules/CMakeDetermineCXXCompiler.cmake @@ -160,8 +160,9 @@ if (NOT _CMAKE_TOOLCHAIN_PREFIX) if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|QCC") get_filename_component(COMPILER_BASENAME "${CMAKE_CXX_COMPILER}" NAME) - if (COMPILER_BASENAME MATCHES "^(.+-)(clan)?[gc]\\+\\+(-[0-9]+(\\.[0-9]+)*)?(-[^.]+)?(\\.exe)?$") + if (COMPILER_BASENAME MATCHES "^(.+-)?(clang\\+\\+|g\\+\\+|clang-cl)(-[0-9]+(\\.[0-9]+)*)?(-[^.]+)?(\\.exe)?$") set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_MATCH_1}) + set(_CMAKE_TOOLCHAIN_SUFFIX ${CMAKE_MATCH_3}) set(_CMAKE_COMPILER_SUFFIX ${CMAKE_MATCH_5}) elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") if(CMAKE_CXX_COMPILER_TARGET) diff --git a/Modules/CMakeFindBinUtils.cmake b/Modules/CMakeFindBinUtils.cmake index d81fd11..ff178f6 100644 --- a/Modules/CMakeFindBinUtils.cmake +++ b/Modules/CMakeFindBinUtils.cmake @@ -70,17 +70,18 @@ if(("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC" AND OR (CMAKE_GENERATOR MATCHES "Visual Studio" AND NOT CMAKE_VS_PLATFORM_NAME STREQUAL "Tegra-Android")) + set(_CMAKE_LINKER_NAMES "link") + set(_CMAKE_AR_NAMES "lib") + set(_CMAKE_MT_NAMES "mt") if("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL "xClang") - find_program(CMAKE_NM NAMES ${_CMAKE_TOOLCHAIN_PREFIX}nm llvm-nm HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - set(_CMAKE_ADDITIONAL_LINKER_NAMES "lld-link") - set(_CMAKE_ADDITIONAL_AR_NAMES "llvm-lib") + set(_CMAKE_NM_NAMES "llvm-nm" "nm") + list(APPEND _CMAKE_AR_NAMES "lib" "llvm-lib") + list(APPEND _CMAKE_MT_NAMES "mt" "llvm-mt") + list(APPEND _CMAKE_LINKER_NAMES "lld-link") + list(APPEND _CMAKE_TOOL_VARS NM) endif() - find_program(CMAKE_LINKER NAMES ${_CMAKE_ADDITIONAL_LINKER_NAMES} link HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_AR NAMES ${_CMAKE_ADDITIONAL_AR_NAMES} lib HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_MT NAMES mt HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - - list(APPEND _CMAKE_TOOL_VARS LINKER MT) + list(APPEND _CMAKE_TOOL_VARS LINKER MT AR) # in all other cases search for ar, ranlib, etc. else() @@ -92,55 +93,54 @@ else() endif() if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL Clang) - set(_CMAKE_ADDITIONAL_AR_NAMES "llvm-ar") - set(_CMAKE_ADDITIONAL_RANLIB_NAMES "llvm-ranlib") - set(_CMAKE_ADDITIONAL_STRIP_NAMES "llvm-strip") if("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC") - set(_CMAKE_ADDITIONAL_LINKER_NAMES "lld-link") + set(_CMAKE_LINKER_NAMES "lld-link") else() - set(_CMAKE_ADDITIONAL_LINKER_NAMES "ld.lld") + set(_CMAKE_LINKER_NAMES "ld.lld") endif() - set(_CMAKE_ADDITIONAL_NM_NAMES "llvm-nm") - set(_CMAKE_ADDITIONAL_OBJDUMP_NAMES "llvm-objdump") - set(_CMAKE_ADDITIONAL_OBJCOPY_NAMES "llvm-objcopy") - set(_CMAKE_ADDITIONAL_READELF_NAMES "llvm-readelf") - set(_CMAKE_ADDITIONAL_DLLTOOL_NAMES "llvm-dlltool") - set(_CMAKE_ADDITIONAL_ADDR2LINE_NAMES "llvm-addr2line") + list(APPEND _CMAKE_AR_NAMES "llvm-ar") + list(APPEND _CMAKE_NM_NAMES "llvm-nm") + list(APPEND _CMAKE_OBJDUMP_NAMES "llvm-objdump") + list(APPEND _CMAKE_OBJCOPY_NAMES "llvm-objcopy") + list(APPEND _CMAKE_READELF_NAMES "llvm-readelf") + list(APPEND _CMAKE_DLLTOOL_NAMES "llvm-dlltool") + list(APPEND _CMAKE_ADDR2LINE_NAMES "llvm-addr2line") endif() - if(NOT CMAKE_CROSSCOMPILING AND NOT "${_CMAKE_TOOLCHAIN_PREFIX}" STREQUAL "") - list(APPEND _CMAKE_ADDITIONAL_AR_NAMES "ar") - list(APPEND _CMAKE_ADDITIONAL_RANLIB_NAMES "ranlib") - list(APPEND _CMAKE_ADDITIONAL_STRIP_NAMES "strip") - list(APPEND _CMAKE_ADDITIONAL_LINKER_NAMES "ld") - list(APPEND _CMAKE_ADDITIONAL_NM_NAMES "nm") - list(APPEND _CMAKE_ADDITIONAL_OBJDUMP_NAMES "objdump") - list(APPEND _CMAKE_ADDITIONAL_OBJCOPY_NAMES "objcopy") - list(APPEND _CMAKE_ADDITIONAL_READELF_NAMES "readelf") - list(APPEND _CMAKE_ADDITIONAL_DLLTOOL_NAMES "dlltool") - list(APPEND _CMAKE_ADDITIONAL_ADDR2LINE_NAMES "addr2line") - endif() + list(APPEND _CMAKE_AR_NAMES "ar") + list(APPEND _CMAKE_RANLIB_NAMES "ranlib") + list(APPEND _CMAKE_STRIP_NAMES "strip") + list(APPEND _CMAKE_LINKER_NAMES "ld") + list(APPEND _CMAKE_NM_NAMES "nm") + list(APPEND _CMAKE_OBJDUMP_NAMES "objdump") + list(APPEND _CMAKE_OBJCOPY_NAMES "objcopy") + list(APPEND _CMAKE_READELF_NAMES "readelf") + list(APPEND _CMAKE_DLLTOOL_NAMES "dlltool") + list(APPEND _CMAKE_ADDR2LINE_NAMES "addr2line") + + list(APPEND _CMAKE_TOOL_VARS AR RANLIB STRIP LINKER NM OBJDUMP OBJCOPY READELF DLLTOOL ADDR2LINE) +endif() - find_program(CMAKE_AR NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ar${_CMAKE_TOOLCHAIN_SUFFIX} ${_CMAKE_ADDITIONAL_AR_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) +foreach(TOOL IN LISTS _CMAKE_TOOL_VARS) + foreach(NAME IN LISTS _CMAKE_${TOOL}_NAMES) + if(NOT _CMAKE_TOOLCHAIN_PREFIX STREQUAL "") + if(NOT _CMAKE_TOOLCHAIN_SUFFIX STREQUAL "") + list(PREPEND _CMAKE_${TOOL}_NAMES ${NAME}${_CMAKE_TOOLCHAIN_SUFFIX}) + endif() + list(PREPEND _CMAKE_${TOOL}_NAMES ${_CMAKE_TOOLCHAIN_PREFIX}${NAME}) + endif() + if(NOT _CMAKE_TOOLCHAIN_SUFFIX STREQUAL "") + list(PREPEND _CMAKE_${TOOL}_NAMES ${_CMAKE_TOOLCHAIN_PREFIX}${NAME}${_CMAKE_TOOLCHAIN_SUFFIX}) + endif() + endforeach() + find_program(CMAKE_${TOOL} NAMES ${_CMAKE_${TOOL}_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) +endforeach() - find_program(CMAKE_RANLIB NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ranlib ${_CMAKE_ADDITIONAL_RANLIB_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - if(NOT CMAKE_RANLIB) +if(NOT CMAKE_RANLIB) set(CMAKE_RANLIB : CACHE INTERNAL "noop for ranlib") - endif() - - - find_program(CMAKE_STRIP NAMES ${_CMAKE_TOOLCHAIN_PREFIX}strip${_CMAKE_TOOLCHAIN_SUFFIX} ${_CMAKE_ADDITIONAL_STRIP_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_LINKER NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ld ${_CMAKE_ADDITIONAL_LINKER_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_NM NAMES ${_CMAKE_TOOLCHAIN_PREFIX}nm ${_CMAKE_ADDITIONAL_NM_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_OBJDUMP NAMES ${_CMAKE_TOOLCHAIN_PREFIX}objdump ${_CMAKE_ADDITIONAL_OBJDUMP_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_OBJCOPY NAMES ${_CMAKE_TOOLCHAIN_PREFIX}objcopy ${_CMAKE_ADDITIONAL_OBJCOPY_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_READELF NAMES ${_CMAKE_TOOLCHAIN_PREFIX}readelf ${_CMAKE_ADDITIONAL_READELF_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_DLLTOOL NAMES ${_CMAKE_TOOLCHAIN_PREFIX}dlltool ${_CMAKE_ADDITIONAL_DLLTOOL_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_ADDR2LINE NAMES ${_CMAKE_TOOLCHAIN_PREFIX}addr2line ${_CMAKE_ADDITIONAL_ADDR2LINE_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - - list(APPEND _CMAKE_TOOL_VARS AR RANLIB STRIP LINKER NM OBJDUMP OBJCOPY READELF DLLTOOL ADDR2LINE) endif() + if(CMAKE_PLATFORM_HAS_INSTALLNAME) find_program(CMAKE_INSTALL_NAME_TOOL NAMES ${_CMAKE_TOOLCHAIN_PREFIX}install_name_tool HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) @@ -157,7 +157,7 @@ foreach(var IN LISTS _CMAKE_TOOL_VARS) if(_CMAKE_TOOL_CACHED) mark_as_advanced(CMAKE_${var}) endif() - unset(_CMAKE_ADDITIONAL_${var}_NAMES) + unset(_CMAKE_${var}_NAMES) endforeach() unset(_CMAKE_TOOL_VARS) unset(_CMAKE_TOOL_CACHED) diff --git a/Modules/Compiler/Clang-FindBinUtils.cmake b/Modules/Compiler/Clang-FindBinUtils.cmake index b852660..e6c469a 100644 --- a/Modules/Compiler/Clang-FindBinUtils.cmake +++ b/Modules/Compiler/Clang-FindBinUtils.cmake @@ -2,6 +2,12 @@ if(NOT DEFINED _CMAKE_PROCESSING_LANGUAGE OR _CMAKE_PROCESSING_LANGUAGE STREQUAL message(FATAL_ERROR "Internal error: _CMAKE_PROCESSING_LANGUAGE is not set") endif() +# Ubuntu: +# * /usr/bin/llvm-ar-9 +# * /usr/bin/llvm-ranlib-9 +string(REGEX MATCH "^([0-9]+)" __version_x + "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_VERSION}") + # Debian: # * /usr/bin/llvm-ar-4.0 # * /usr/bin/llvm-ranlib-4.0 @@ -19,6 +25,7 @@ set(__clang_hints ${__clang_hint_1} ${__clang_hint_2}) # http://manpages.ubuntu.com/manpages/precise/en/man1/llvm-ar.1.html find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR NAMES "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ar-${__version_x_y}" + "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ar-${__version_x}" "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ar" HINTS ${__clang_hints} DOC "LLVM archiver" @@ -28,6 +35,7 @@ mark_as_advanced(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR) # http://manpages.ubuntu.com/manpages/precise/en/man1/llvm-ranlib.1.html find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_RANLIB NAMES "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ranlib-${__version_x_y}" + "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ranlib-${__version_x}" "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ranlib" HINTS ${__clang_hints} DOC "Generate index for LLVM archive" diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index 28d21c1..3307bc6 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -543,6 +543,18 @@ External Project Definition When ``BUILD_IN_SOURCE`` option is enabled, the ``BUILD_COMMAND`` is used to point to an alternative directory within the source tree. + ``CONFIGURE_HANDLED_BY_BUILD <bool>`` + .. versionadded:: 3.20 + + Enabling this option relaxes the dependencies of the configure step on + other external projects to order-only. This means the configure step will + be executed after its external project dependencies are built but it will + not be marked dirty when one of its external project dependencies is + rebuilt. This option can be enabled when the build step is smart enough + to figure out if the configure step needs to be rerun. CMake and Meson are + examples of build systems whose build step is smart enough to know if the + configure step needs to be rerun. + **Build Step Options:** If the configure step assumed the external project uses CMake as its build system, the build step will also. Otherwise, the build step will assume a @@ -3083,6 +3095,23 @@ function(_ep_add_patch_command name) ) endfunction() +function(_ep_get_file_deps var name) + set(file_deps) + + get_property(deps TARGET ${name} PROPERTY _EP_DEPENDS) + foreach(dep IN LISTS deps) + get_property(dep_type TARGET ${dep} PROPERTY TYPE) + if(dep_type STREQUAL "UTILITY") + get_property(is_ep TARGET ${dep} PROPERTY _EP_IS_EXTERNAL_PROJECT) + if(is_ep) + _ep_get_step_stampfile(${dep} "done" done_stamp_file) + list(APPEND file_deps ${done_stamp_file}) + endif() + endif() + endforeach() + + set("${var}" "${file_deps}" PARENT_SCOPE) +endfunction() function(_ep_extract_configure_command var name) get_property(cmd_set TARGET ${name} PROPERTY _EP_CONFIGURE_COMMAND SET) @@ -3191,19 +3220,13 @@ endfunction() function(_ep_add_configure_command name) ExternalProject_Get_Property(${name} binary_dir tmp_dir) - # Depend on other external projects (file-level). set(file_deps) - get_property(deps TARGET ${name} PROPERTY _EP_DEPENDS) - foreach(dep IN LISTS deps) - get_property(dep_type TARGET ${dep} PROPERTY TYPE) - if(dep_type STREQUAL "UTILITY") - get_property(is_ep TARGET ${dep} PROPERTY _EP_IS_EXTERNAL_PROJECT) - if(is_ep) - _ep_get_step_stampfile(${dep} "done" done_stamp_file) - list(APPEND file_deps ${done_stamp_file}) - endif() - endif() - endforeach() + get_property(configure_handled_by_build TARGET ${name} + PROPERTY _EP_CONFIGURE_HANDLED_BY_BUILD) + if(NOT configure_handled_by_build) + # Depend on other external projects (file-level) + _ep_get_file_deps(file_deps ${name}) + endif() _ep_extract_configure_command(cmd ${name}) @@ -3254,6 +3277,14 @@ endfunction() function(_ep_add_build_command name) ExternalProject_Get_Property(${name} binary_dir) + set(file_deps) + get_property(configure_handled_by_build TARGET ${name} + PROPERTY _EP_CONFIGURE_HANDLED_BY_BUILD) + if(configure_handled_by_build) + # Depend on other external projects (file-level) + _ep_get_file_deps(file_deps ${name}) + endif() + get_property(cmd_set TARGET ${name} PROPERTY _EP_BUILD_COMMAND SET) if(cmd_set) get_property(cmd TARGET ${name} PROPERTY _EP_BUILD_COMMAND) @@ -3296,6 +3327,7 @@ function(_ep_add_build_command name) BYPRODUCTS \${build_byproducts} WORKING_DIRECTORY \${binary_dir} DEPENDEES configure + DEPENDS \${file_deps} ALWAYS \${always} ${log} ${uses_terminal} diff --git a/Modules/FindCUDA.cmake b/Modules/FindCUDA.cmake index 6daf81d..240f0a5 100644 --- a/Modules/FindCUDA.cmake +++ b/Modules/FindCUDA.cmake @@ -834,7 +834,20 @@ if(NOT CUDA_TOOLKIT_ROOT_DIR AND NOT CMAKE_CROSSCOMPILING) ) if (CUDA_TOOLKIT_ROOT_DIR_NVCC) - get_filename_component(CUDA_TOOLKIT_ROOT_DIR_NVCC_PAR "${CUDA_TOOLKIT_ROOT_DIR_NVCC}" DIRECTORY) + # If NVCC is a symlink due to a wrapper script (e.g. ccache or colornvcc), then invoke it to find the + # real non-scattered toolkit. + if(IS_SYMLINK ${CUDA_TOOLKIT_ROOT_DIR_NVCC}) + execute_process(COMMAND ${CUDA_TOOLKIT_ROOT_DIR_NVCC} "-v" "__cmake_determine_cuda" ERROR_VARIABLE NVCC_ERR) + if(NVCC_ERR MATCHES " _HERE_=([^\r\n]*)") + set(CUDA_TOOLKIT_ROOT_DIR_NVCC_PAR "${CMAKE_MATCH_1}") + else() + message(FATAL_ERROR "Could not execute nvcc with -v.") + endif() + unset(NVCC_ERR) + else() + get_filename_component(CUDA_TOOLKIT_ROOT_DIR_NVCC_PAR "${CUDA_TOOLKIT_ROOT_DIR_NVCC}" DIRECTORY) + endif() + get_filename_component(CUDA_TOOLKIT_ROOT_DIR "${CUDA_TOOLKIT_ROOT_DIR_NVCC_PAR}" DIRECTORY CACHE) string(REGEX REPLACE "[/\\\\]?bin[64]*[/\\\\]?$" "" CUDA_TOOLKIT_ROOT_DIR ${CUDA_TOOLKIT_ROOT_DIR}) # We need to force this back into the cache. diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake index 61e264b..0d80c80 100644 --- a/Modules/FindCUDAToolkit.cmake +++ b/Modules/FindCUDAToolkit.cmake @@ -519,7 +519,19 @@ else() endif() if(CUDAToolkit_NVCC_EXECUTABLE) - get_filename_component(CUDAToolkit_BIN_DIR "${CUDAToolkit_NVCC_EXECUTABLE}" DIRECTORY) + # If NVCC is a symlink due to a wrapper script (e.g. ccache or colornvcc), then invoke it to find the + # real non-scattered toolkit. + if(IS_SYMLINK ${CUDAToolkit_NVCC_EXECUTABLE}) + execute_process(COMMAND ${CUDAToolkit_NVCC_EXECUTABLE} "-v" "__cmake_determine_cuda" ERROR_VARIABLE NVCC_ERR) + if(NVCC_ERR MATCHES " _HERE_=([^\r\n]*)") + set(CUDAToolkit_BIN_DIR "${CMAKE_MATCH_1}") + else() + message(FATAL_ERROR "Could not execute nvcc with -v.") + endif() + unset(NVCC_ERR) + else() + get_filename_component(CUDAToolkit_BIN_DIR "${CUDAToolkit_NVCC_EXECUTABLE}" DIRECTORY) + endif() set(CUDAToolkit_BIN_DIR "${CUDAToolkit_BIN_DIR}" CACHE PATH "" FORCE) mark_as_advanced(CUDAToolkit_BIN_DIR) diff --git a/Modules/FindHDF5.cmake b/Modules/FindHDF5.cmake index 05696ae..a69a585 100644 --- a/Modules/FindHDF5.cmake +++ b/Modules/FindHDF5.cmake @@ -116,15 +116,22 @@ also be defined. With all components enabled, the following variables will be d With all components enabled, the following targets will be defined: -:: - - ``hdf5::hdf5`` - ``hdf5::hdf5_hl_cpp`` - ``hdf5::hdf5_fortran`` - ``hdf5::hdf5_hl`` - ``hdf5::hdf5_hl_cpp`` - ``hdf5::hdf5_hl_fortran`` - ``hdf5::h5diff`` +``HDF5::HDF5`` + All detected ``HDF5_LIBRARIES``. +``hdf5::hdf5`` + C library. +``hdf5::hdf5_cpp`` + C++ library. +``hdf5::hdf5_fortran`` + Fortran library. +``hdf5::hdf5_hl`` + High-level C library. +``hdf5::hdf5_hl_cpp`` + High-level C++ library. +``hdf5::hdf5_hl_fortran`` + High-level Fortran library. +``hdf5::h5diff`` + ``h5diff`` executable. Hints ^^^^^ @@ -206,22 +213,6 @@ else() set(HDF5_Fortran_COMPILER_NAMES h5fc h5pfc) endif() -# We may have picked up some duplicates in various lists during the above -# process for the language bindings (both the C and C++ bindings depend on -# libz for example). Remove the duplicates. It appears that the default -# CMake behavior is to remove duplicates from the end of a list. However, -# for link lines, this is incorrect since unresolved symbols are searched -# for down the link line. Therefore, we reverse the list, remove the -# duplicates, and then reverse it again to get the duplicates removed from -# the beginning. -macro(_HDF5_remove_duplicates_from_beginning _list_name) - if(${_list_name}) - list(REVERSE ${_list_name}) - list(REMOVE_DUPLICATES ${_list_name}) - list(REVERSE ${_list_name}) - endif() -endmacro() - # Test first if the current compilers automatically wrap HDF5 function(_HDF5_test_regular_compiler_C success version is_parallel) set(scratch_directory @@ -724,10 +715,8 @@ if(NOT HDF5_FOUND) endif() set(HDF5_${_lang}_FOUND TRUE) - _HDF5_remove_duplicates_from_beginning(HDF5_${_lang}_DEFINITIONS) - _HDF5_remove_duplicates_from_beginning(HDF5_${_lang}_INCLUDE_DIRS) - _HDF5_remove_duplicates_from_beginning(HDF5_${_lang}_LIBRARIES) - _HDF5_remove_duplicates_from_beginning(HDF5_${_lang}_HL_LIBRARIES) + list(REMOVE_DUPLICATES HDF5_${_lang}_DEFINITIONS) + list(REMOVE_DUPLICATES HDF5_${_lang}_INCLUDE_DIRS) else() set(_HDF5_NEED_TO_SEARCH TRUE) endif() @@ -780,10 +769,8 @@ elseif(NOT HDF5_FOUND AND NOT _HDF5_NEED_TO_SEARCH) endif() endif() endforeach() - _HDF5_remove_duplicates_from_beginning(HDF5_DEFINITIONS) - _HDF5_remove_duplicates_from_beginning(HDF5_INCLUDE_DIRS) - _HDF5_remove_duplicates_from_beginning(HDF5_LIBRARIES) - _HDF5_remove_duplicates_from_beginning(HDF5_HL_LIBRARIES) + list(REMOVE_DUPLICATES HDF5_DEFINITIONS) + list(REMOVE_DUPLICATES HDF5_INCLUDE_DIRS) set(HDF5_FOUND TRUE) set(HDF5_REQUIRED_VARS HDF5_LIBRARIES) if(HDF5_FIND_HL) @@ -923,10 +910,8 @@ if( NOT HDF5_FOUND ) set(HDF5_HL_FOUND TRUE) endif() - _HDF5_remove_duplicates_from_beginning(HDF5_DEFINITIONS) - _HDF5_remove_duplicates_from_beginning(HDF5_INCLUDE_DIRS) - _HDF5_remove_duplicates_from_beginning(HDF5_LIBRARIES) - _HDF5_remove_duplicates_from_beginning(HDF5_HL_LIBRARIES) + list(REMOVE_DUPLICATES HDF5_DEFINITIONS) + list(REMOVE_DUPLICATES HDF5_INCLUDE_DIRS) # If the HDF5 include directory was found, open H5pubconf.h to determine if # HDF5 was compiled with parallel IO support @@ -1147,6 +1132,21 @@ if (HDF5_FIND_DEBUG) message(STATUS "HDF5_${_lang}_HL_LIBRARY: ${HDF5_${_lang}_HL_LIBRARY}") message(STATUS "HDF5_${_lang}_HL_LIBRARIES: ${HDF5_${_lang}_HL_LIBRARIES}") endforeach() + message(STATUS "Defined targets (if any):") + foreach(_lang IN ITEMS "" "_cpp" "_fortran") + foreach(_hl IN ITEMS "" "_hl") + foreach(_prefix IN ITEMS "hdf5::" "") + foreach(_suffix IN ITEMS "-static" "-shared" "") + set (_target ${_prefix}hdf5${_hl}${_lang}${_suffix}) + if (TARGET ${_target}) + message(STATUS "... ${_target}") + else() + #message(STATUS "... ${_target} does not exist") + endif() + endforeach() + endforeach() + endforeach() + endforeach() endif() unset(_lang) unset(_HDF5_NEED_TO_SEARCH) diff --git a/Modules/FindJPEG.cmake b/Modules/FindJPEG.cmake index 3f243de..add2486 100644 --- a/Modules/FindJPEG.cmake +++ b/Modules/FindJPEG.cmake @@ -58,7 +58,7 @@ Obsolete variables find_path(JPEG_INCLUDE_DIR jpeglib.h) -set(jpeg_names ${JPEG_NAMES} jpeg jpeg-static libjpeg libjpeg-static) +set(jpeg_names ${JPEG_NAMES} jpeg jpeg-static libjpeg libjpeg-static turbojpeg turbojpeg-static) foreach(name ${jpeg_names}) list(APPEND jpeg_names_debug "${name}d") endforeach() diff --git a/Modules/FindLAPACK.cmake b/Modules/FindLAPACK.cmake index d5af5da..45e4be7 100644 --- a/Modules/FindLAPACK.cmake +++ b/Modules/FindLAPACK.cmake @@ -72,6 +72,12 @@ The following variables may be set to influence this module's behavior: ``BLA_F95`` if ``ON`` tries to find the BLAS95/LAPACK95 interfaces +``BLA_PREFER_PKGCONFIG`` + .. versionadded:: 3.20 + + if set ``pkg-config`` will be used to search for a LAPACK library first + and if one is found that is preferred + Imported targets ^^^^^^^^^^^^^^^^ @@ -121,6 +127,26 @@ endif() include(${CMAKE_CURRENT_LIST_DIR}/CMakePushCheckState.cmake) include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) +function(_add_lapack_target) + if(LAPACK_FOUND AND NOT TARGET LAPACK::LAPACK) + add_library(LAPACK::LAPACK INTERFACE IMPORTED) + set(_lapack_libs "${LAPACK_LIBRARIES}") + if(_lapack_libs AND TARGET BLAS::BLAS) + # remove the ${BLAS_LIBRARIES} from the interface and replace it + # with the BLAS::BLAS target + list(REMOVE_ITEM _lapack_libs "${BLAS_LIBRARIES}") + list(APPEND _lapack_libs BLAS::BLAS) + endif() + + if(_lapack_libs) + set_target_properties(LAPACK::LAPACK PROPERTIES + INTERFACE_LINK_LIBRARIES "${_lapack_libs}" + ) + endif() + unset(_lapack_libs) + endif() +endfunction() + macro(_lapack_find_library_setup) cmake_push_check_state() set(CMAKE_REQUIRED_QUIET ${LAPACK_FIND_QUIETLY}) @@ -265,6 +291,21 @@ if(NOT LAPACK_NOT_FOUND_MESSAGE) _lapack_find_dependency(BLAS) endif() +# Search with pkg-config if specified +if(BLA_PREFER_PKGCONFIG) + find_package(PkgConfig) + pkg_check_modules(PKGC_LAPACK lapack) + if(PKGC_LAPACK_FOUND) + set(LAPACK_FOUND TRUE) + set(LAPACK_LIBRARIES "${PKGC_LAPACK_LINK_LIBRARIES}") + if (BLAS_LIBRARIES) + list(APPEND LAPACK_LIBRARIES "${BLAS_LIBRARIES}") + endif() + _add_lapack_target() + return() + endif() +endif() + # Search for different LAPACK distributions if BLAS is found if(NOT LAPACK_NOT_FOUND_MESSAGE) set(LAPACK_LINKER_FLAGS ${BLAS_LINKER_FLAGS}) @@ -585,21 +626,6 @@ if(LAPACK_LIBRARIES STREQUAL "LAPACK_LIBRARIES-PLACEHOLDER-FOR-EMPTY-LIBRARIES") set(LAPACK_LIBRARIES "") endif() -if(LAPACK_FOUND AND NOT TARGET LAPACK::LAPACK) - add_library(LAPACK::LAPACK INTERFACE IMPORTED) - set(_lapack_libs "${LAPACK_LIBRARIES}") - if(_lapack_libs AND TARGET BLAS::BLAS) - # remove the ${BLAS_LIBRARIES} from the interface and replace it - # with the BLAS::BLAS target - list(REMOVE_ITEM _lapack_libs "${BLAS_LIBRARIES}") - endif() - - if(_lapack_libs) - set_target_properties(LAPACK::LAPACK PROPERTIES - INTERFACE_LINK_LIBRARIES "${_lapack_libs}" - ) - endif() - unset(_lapack_libs) -endif() +_add_lapack_target() _lapack_find_library_teardown() diff --git a/Modules/InstallRequiredSystemLibraries.cmake b/Modules/InstallRequiredSystemLibraries.cmake index c34b819..6ecdb9c 100644 --- a/Modules/InstallRequiredSystemLibraries.cmake +++ b/Modules/InstallRequiredSystemLibraries.cmake @@ -299,6 +299,7 @@ if(MSVC) foreach(crt "${MSVC_CRT_DIR}/msvcp${v}_1.dll" "${MSVC_CRT_DIR}/msvcp${v}_2.dll" + "${MSVC_CRT_DIR}/msvcp${v}_atomic_wait.dll" "${MSVC_CRT_DIR}/msvcp${v}_codecvt_ids.dll" "${MSVC_CRT_DIR}/vcruntime${v}_1.dll" ) @@ -327,6 +328,7 @@ if(MSVC) foreach(crt "${MSVC_CRT_DIR}/msvcp${v}_1d.dll" "${MSVC_CRT_DIR}/msvcp${v}_2d.dll" + "${MSVC_CRT_DIR}/msvcp${v}d_atomic_wait.dll" "${MSVC_CRT_DIR}/msvcp${v}d_codecvt_ids.dll" "${MSVC_CRT_DIR}/vcruntime${v}_1d.dll" ) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index c5b67c0..dca94ee 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -268,6 +268,8 @@ set(SRCS cmFileAPICodemodel.h cmFileAPICMakeFiles.cxx cmFileAPICMakeFiles.h + cmFileAPIToolchains.cxx + cmFileAPIToolchains.h cmFileCopier.cxx cmFileCopier.h cmFileInstaller.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index a5cf411..241c1c3 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,7 +1,7 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 19) -set(CMake_VERSION_PATCH 20210112) +set(CMake_VERSION_PATCH 20210118) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) diff --git a/Source/CTest/cmCTestMemCheckHandler.cxx b/Source/CTest/cmCTestMemCheckHandler.cxx index 8a30dc0..6e8091b 100644 --- a/Source/CTest/cmCTestMemCheckHandler.cxx +++ b/Source/CTest/cmCTestMemCheckHandler.cxx @@ -1364,9 +1364,15 @@ void cmCTestMemCheckHandler::AppendMemTesterOutput(cmCTestTestResult& res, } } if (this->LogWithPID) { - cmSystemTools::RemoveFile(ofile); - cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - "Remove: " << ofile << "\n", this->Quiet); + auto pos = ofile.find_last_of('.'); + if (pos != std::string::npos) { + auto ofileWithoutPid = ofile.substr(0, pos); + cmSystemTools::RenameFile(ofile, ofileWithoutPid); + cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + "Renaming: " << ofile << " to: " << ofileWithoutPid + << "\n", + this->Quiet); + } } } diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt index d8f89d6..0c263bb 100644 --- a/Source/QtDialog/CMakeLists.txt +++ b/Source/QtDialog/CMakeLists.txt @@ -213,6 +213,7 @@ else() qt_wrap_cpp(MOC_BUILT_SRCS ${MOC_SRCS}) qt_add_resources(QRC_BUILT_SRCS ${QRC_SRCS}) endif() +add_library(CMakeGUIQRCLib OBJECT ${QRC_BUILT_SRCS}) if (FALSE) # CMake's bootstrap binary does not support automoc set(CMAKE_AUTOMOC 1) @@ -221,8 +222,7 @@ if (FALSE) # CMake's bootstrap binary does not support automoc else () list(APPEND SRCS ${UI_BUILT_SRCS} - ${MOC_BUILT_SRCS} - ${QRC_BUILT_SRCS}) + ${MOC_BUILT_SRCS}) endif () if(USE_LGPL) @@ -246,6 +246,7 @@ target_link_libraries(CMakeGUIMainLib PUBLIC CMakeGUILib) add_executable(cmake-gui WIN32 MACOSX_BUNDLE CMakeGUIExec.cxx ${MANIFEST_FILE}) target_link_libraries(cmake-gui CMakeGUIMainLib Qt${INSTALLED_QT_VERSION}::Core) +target_sources(CMakeGUIMainLib INTERFACE $<TARGET_OBJECTS:CMakeGUIQRCLib>) if(WIN32) target_sources(CMakeGUIMainLib INTERFACE $<TARGET_OBJECTS:CMakeVersion> CMakeSetup.rc) endif() diff --git a/Source/cmCMakePath.cxx b/Source/cmCMakePath.cxx index b8215df..73321c6 100644 --- a/Source/cmCMakePath.cxx +++ b/Source/cmCMakePath.cxx @@ -88,7 +88,8 @@ bool cmCMakePath::IsPrefix(const cmCMakePath& path) const ++prefix_it; ++path_it; } - return prefix_it == prefix_end; + return (prefix_it == prefix_end) || + (prefix_it->empty() && path_it != path_end); } std::string cmCMakePath::FormatPath(std::string path, format fmt) diff --git a/Source/cmCMakePathCommand.cxx b/Source/cmCMakePathCommand.cxx index e9bf84a..5662a2f 100644 --- a/Source/cmCMakePathCommand.cxx +++ b/Source/cmCMakePathCommand.cxx @@ -929,17 +929,8 @@ bool HandleIsPrefixCommand(std::vector<std::string> const& args, bool HandleHashCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - if (args.size() < 3 || args.size() > 4) { - status.SetError("HASH must be called with two or three arguments."); - return false; - } - - static NormalizeParser const parser; - - const auto arguments = parser.Parse(args); - - if (parser.GetInputs().size() != 1) { - status.SetError("HASH called with unexpected arguments."); + if (args.size() != 3) { + status.SetError("HASH must be called with two arguments."); return false; } @@ -948,15 +939,14 @@ bool HandleHashCommand(std::vector<std::string> const& args, return false; } - const auto& output = parser.GetInputs().front(); + const auto& output = args[2]; if (output.empty()) { status.SetError("Invalid name for output variable."); return false; } - auto hash = hash_value(arguments.Normalize ? cmCMakePath(inputPath).Normal() - : cmCMakePath(inputPath)); + auto hash = hash_value(cmCMakePath(inputPath).Normal()); std::ostringstream out; out << std::setbase(16) << hash; diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx index c2ab2f1..d2a9bec 100644 --- a/Source/cmFileAPI.cxx +++ b/Source/cmFileAPI.cxx @@ -18,6 +18,7 @@ #include "cmFileAPICMakeFiles.h" #include "cmFileAPICache.h" #include "cmFileAPICodemodel.h" +#include "cmFileAPIToolchains.h" #include "cmGlobalGenerator.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -262,6 +263,17 @@ bool cmFileAPI::ReadQuery(std::string const& query, objects.push_back(o); return true; } + if (kindName == ObjectKindName(ObjectKind::Toolchains)) { + Object o; + o.Kind = ObjectKind::Toolchains; + if (verStr == "v1") { + o.Version = 1; + } else { + return false; + } + objects.push_back(o); + return true; + } if (kindName == ObjectKindName(ObjectKind::InternalTest)) { Object o; o.Kind = ObjectKind::InternalTest; @@ -402,6 +414,7 @@ const char* cmFileAPI::ObjectKindName(ObjectKind kind) "codemodel", // "cache", // "cmakeFiles", // + "toolchains", // "__test" // }; return objectKindNames[size_t(kind)]; @@ -435,6 +448,9 @@ Json::Value cmFileAPI::BuildObject(Object const& object) case ObjectKind::CMakeFiles: value = this->BuildCMakeFiles(object); break; + case ObjectKind::Toolchains: + value = this->BuildToolchains(object); + break; case ObjectKind::InternalTest: value = this->BuildInternalTest(object); break; @@ -491,6 +507,8 @@ cmFileAPI::ClientRequest cmFileAPI::BuildClientRequest( r.Kind = ObjectKind::Cache; } else if (kindName == this->ObjectKindName(ObjectKind::CMakeFiles)) { r.Kind = ObjectKind::CMakeFiles; + } else if (kindName == this->ObjectKindName(ObjectKind::Toolchains)) { + r.Kind = ObjectKind::Toolchains; } else if (kindName == this->ObjectKindName(ObjectKind::InternalTest)) { r.Kind = ObjectKind::InternalTest; } else { @@ -518,6 +536,9 @@ cmFileAPI::ClientRequest cmFileAPI::BuildClientRequest( case ObjectKind::CMakeFiles: this->BuildClientRequestCMakeFiles(r, versions); break; + case ObjectKind::Toolchains: + this->BuildClientRequestToolchains(r, versions); + break; case ObjectKind::InternalTest: this->BuildClientRequestInternalTest(r, versions); break; @@ -765,6 +786,40 @@ Json::Value cmFileAPI::BuildCMakeFiles(Object const& object) return cmakeFiles; } +// The "toolchains" object kind. + +static unsigned int const ToolchainsV1Minor = 0; + +void cmFileAPI::BuildClientRequestToolchains( + ClientRequest& r, std::vector<RequestVersion> const& versions) +{ + // Select a known version from those requested. + for (RequestVersion const& v : versions) { + if ((v.Major == 1 && v.Minor <= ToolchainsV1Minor)) { + r.Version = v.Major; + break; + } + } + if (!r.Version) { + r.Error = NoSupportedVersion(versions); + } +} + +Json::Value cmFileAPI::BuildToolchains(Object const& object) +{ + Json::Value toolchains = cmFileAPIToolchainsDump(*this, object.Version); + toolchains["kind"] = this->ObjectKindName(object.Kind); + + Json::Value& version = toolchains["version"]; + if (object.Version == 1) { + version = BuildVersion(1, ToolchainsV1Minor); + } else { + return toolchains; // should be unreachable + } + + return toolchains; +} + // The "__test" object kind is for internal testing of CMake. static unsigned int const InternalTestV1Minor = 3; @@ -828,5 +883,13 @@ Json::Value cmFileAPI::ReportCapabilities() requests.append(std::move(request)); // NOLINT(*) } + { + Json::Value request = Json::objectValue; + request["kind"] = ObjectKindName(ObjectKind::Toolchains); + Json::Value& versions = request["version"] = Json::arrayValue; + versions.append(BuildVersion(1, ToolchainsV1Minor)); + requests.append(std::move(request)); // NOLINT(*) + } + return capabilities; } diff --git a/Source/cmFileAPI.h b/Source/cmFileAPI.h index 086a92a..22302b4 100644 --- a/Source/cmFileAPI.h +++ b/Source/cmFileAPI.h @@ -56,6 +56,7 @@ private: CodeModel, Cache, CMakeFiles, + Toolchains, InternalTest }; @@ -200,6 +201,10 @@ private: ClientRequest& r, std::vector<RequestVersion> const& versions); Json::Value BuildCMakeFiles(Object const& object); + void BuildClientRequestToolchains( + ClientRequest& r, std::vector<RequestVersion> const& versions); + Json::Value BuildToolchains(Object const& object); + void BuildClientRequestInternalTest( ClientRequest& r, std::vector<RequestVersion> const& versions); Json::Value BuildInternalTest(Object const& object); diff --git a/Source/cmFileAPIToolchains.cxx b/Source/cmFileAPIToolchains.cxx new file mode 100644 index 0000000..722c114 --- /dev/null +++ b/Source/cmFileAPIToolchains.cxx @@ -0,0 +1,151 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmFileAPIToolchains.h" + +#include <memory> +#include <string> +#include <vector> + +#include <cm3p/json/value.h> + +#include "cmFileAPI.h" +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmProperty.h" +#include "cmState.h" +#include "cmStringAlgorithms.h" +#include "cmake.h" + +namespace { + +struct ToolchainVariable +{ + std::string ObjectKey; + std::string VariableSuffix; + bool IsList; +}; + +class Toolchains +{ + cmFileAPI& FileAPI; + unsigned long Version; + + static const std::vector<ToolchainVariable> CompilerVariables; + static const std::vector<ToolchainVariable> CompilerImplicitVariables; + static const ToolchainVariable SourceFileExtensionsVariable; + + Json::Value DumpToolchains(); + Json::Value DumpToolchain(std::string const& lang); + Json::Value DumpToolchainVariables( + cmMakefile const* mf, std::string const& lang, + std::vector<ToolchainVariable> const& variables); + void DumpToolchainVariable(cmMakefile const* mf, Json::Value& object, + std::string const& lang, + ToolchainVariable const& variable); + +public: + Toolchains(cmFileAPI& fileAPI, unsigned long version); + Json::Value Dump(); +}; + +const std::vector<ToolchainVariable> Toolchains::CompilerVariables{ + { "path", "COMPILER", false }, + { "id", "COMPILER_ID", false }, + { "version", "COMPILER_VERSION", false }, + { "target", "COMPILER_TARGET", false }, +}; + +const std::vector<ToolchainVariable> Toolchains::CompilerImplicitVariables{ + { "includeDirectories", "IMPLICIT_INCLUDE_DIRECTORIES", true }, + { "linkDirectories", "IMPLICIT_LINK_DIRECTORIES", true }, + { "linkFrameworkDirectories", "IMPLICIT_LINK_FRAMEWORK_DIRECTORIES", true }, + { "linkLibraries", "IMPLICIT_LINK_LIBRARIES", true }, +}; + +const ToolchainVariable Toolchains::SourceFileExtensionsVariable{ + "sourceFileExtensions", "SOURCE_FILE_EXTENSIONS", true +}; + +Toolchains::Toolchains(cmFileAPI& fileAPI, unsigned long version) + : FileAPI(fileAPI) + , Version(version) +{ + static_cast<void>(this->Version); +} + +Json::Value Toolchains::Dump() +{ + Json::Value toolchains = Json::objectValue; + toolchains["toolchains"] = this->DumpToolchains(); + return toolchains; +} + +Json::Value Toolchains::DumpToolchains() +{ + Json::Value toolchains = Json::arrayValue; + + for (std::string const& lang : + this->FileAPI.GetCMakeInstance()->GetState()->GetEnabledLanguages()) { + toolchains.append(this->DumpToolchain(lang)); + } + + return toolchains; +} + +Json::Value Toolchains::DumpToolchain(std::string const& lang) +{ + const auto& mf = + this->FileAPI.GetCMakeInstance()->GetGlobalGenerator()->GetMakefiles()[0]; + Json::Value toolchain = Json::objectValue; + toolchain["language"] = lang; + toolchain["compiler"] = + this->DumpToolchainVariables(mf.get(), lang, CompilerVariables); + toolchain["compiler"]["implicit"] = + this->DumpToolchainVariables(mf.get(), lang, CompilerImplicitVariables); + this->DumpToolchainVariable(mf.get(), toolchain, lang, + SourceFileExtensionsVariable); + return toolchain; +} + +Json::Value Toolchains::DumpToolchainVariables( + cmMakefile const* mf, std::string const& lang, + std::vector<ToolchainVariable> const& variables) +{ + Json::Value object = Json::objectValue; + for (const auto& variable : variables) { + this->DumpToolchainVariable(mf, object, lang, variable); + } + return object; +} + +void Toolchains::DumpToolchainVariable(cmMakefile const* mf, + Json::Value& object, + std::string const& lang, + ToolchainVariable const& variable) +{ + std::string const variableName = + cmStrCat("CMAKE_", lang, "_", variable.VariableSuffix); + + if (variable.IsList) { + std::vector<std::string> values; + if (mf->GetDefExpandList(variableName, values)) { + Json::Value jsonArray = Json::arrayValue; + for (std::string const& value : values) { + jsonArray.append(value); + } + object[variable.ObjectKey] = jsonArray; + } + } else { + cmProp def = mf->GetDefinition(variableName); + if (def) { + object[variable.ObjectKey] = *def; + } + } +} +} + +Json::Value cmFileAPIToolchainsDump(cmFileAPI& fileAPI, unsigned long version) +{ + Toolchains toolchains(fileAPI, version); + return toolchains.Dump(); +} diff --git a/Source/cmFileAPIToolchains.h b/Source/cmFileAPIToolchains.h new file mode 100644 index 0000000..c188807 --- /dev/null +++ b/Source/cmFileAPIToolchains.h @@ -0,0 +1,12 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <cm3p/json/value.h> + +class cmFileAPI; + +extern Json::Value cmFileAPIToolchainsDump(cmFileAPI& fileAPI, + unsigned long version); diff --git a/Source/cmGlobalNMakeMakefileGenerator.cxx b/Source/cmGlobalNMakeMakefileGenerator.cxx index c4bec23..36f583f 100644 --- a/Source/cmGlobalNMakeMakefileGenerator.cxx +++ b/Source/cmGlobalNMakeMakefileGenerator.cxx @@ -21,6 +21,8 @@ cmGlobalNMakeMakefileGenerator::cmGlobalNMakeMakefileGenerator(cmake* cm) this->PassMakeflags = true; this->UnixCD = false; this->MakeSilentFlag = "/nologo"; + // nmake breaks on '!' in long-line dependencies + this->ToolSupportsLongLineDependencies = false; } void cmGlobalNMakeMakefileGenerator::EnableLanguage( diff --git a/Source/cmInstallFilesGenerator.cxx b/Source/cmInstallFilesGenerator.cxx index c45000d..0a353e4 100644 --- a/Source/cmInstallFilesGenerator.cxx +++ b/Source/cmInstallFilesGenerator.cxx @@ -25,10 +25,14 @@ cmInstallFilesGenerator::cmInstallFilesGenerator( , Programs(programs) , Optional(optional) { - // We need per-config actions if the destination has generator expressions. + // We need per-config actions if the destination and rename have generator + // expressions. if (cmGeneratorExpression::Find(this->Destination) != std::string::npos) { this->ActionsPerConfig = true; } + if (cmGeneratorExpression::Find(this->Rename) != std::string::npos) { + this->ActionsPerConfig = true; + } // We need per-config actions if any directories have generator expressions. if (!this->ActionsPerConfig) { @@ -56,6 +60,12 @@ std::string cmInstallFilesGenerator::GetDestination( this->LocalGenerator, config); } +std::string cmInstallFilesGenerator::GetRename(std::string const& config) const +{ + return cmGeneratorExpression::Evaluate(this->Rename, this->LocalGenerator, + config); +} + void cmInstallFilesGenerator::AddFilesInstallRule( std::ostream& os, std::string const& config, Indent indent, std::vector<std::string> const& files) @@ -66,7 +76,7 @@ void cmInstallFilesGenerator::AddFilesInstallRule( os, this->GetDestination(config), (this->Programs ? cmInstallType_PROGRAMS : cmInstallType_FILES), files, this->Optional, this->FilePermissions.c_str(), no_dir_permissions, - this->Rename.c_str(), nullptr, indent); + this->GetRename(config).c_str(), nullptr, indent); } void cmInstallFilesGenerator::GenerateScriptActions(std::ostream& os, diff --git a/Source/cmInstallFilesGenerator.h b/Source/cmInstallFilesGenerator.h index b5a1ef4..66145f4 100644 --- a/Source/cmInstallFilesGenerator.h +++ b/Source/cmInstallFilesGenerator.h @@ -31,6 +31,7 @@ public: bool Compute(cmLocalGenerator* lg) override; std::string GetDestination(std::string const& config) const; + std::string GetRename(std::string const& config) const; protected: void GenerateScriptActions(std::ostream& os, Indent indent) override; diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx index 53c871b..fdddb45 100644 --- a/Source/cmListCommand.cxx +++ b/Source/cmListCommand.cxx @@ -422,9 +422,10 @@ bool HandleJoinCommand(std::vector<std::string> const& args, bool HandleRemoveItemCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - if (args.size() < 3) { - status.SetError("sub-command REMOVE_ITEM requires two or more arguments."); - return false; + assert(args.size() >= 2); + + if (args.size() == 2) { + return true; } const std::string& listName = args[1]; diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 5a747e5..7229101 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -693,13 +693,11 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement( } } -namespace { -bool HasUniqueByproducts(cmLocalGenerator& lg, - std::vector<std::string> const& byproducts, - cmListFileBacktrace const& bt) +bool cmLocalNinjaGenerator::HasUniqueByproducts( + std::vector<std::string> const& byproducts, cmListFileBacktrace const& bt) { std::vector<std::string> configs = - lg.GetMakefile()->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + this->GetMakefile()->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); cmGeneratorExpression ge(bt); for (std::string const& p : byproducts) { if (cmGeneratorExpression::Find(p) == std::string::npos) { @@ -709,7 +707,7 @@ bool HasUniqueByproducts(cmLocalGenerator& lg, std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(p); for (std::string const& config : configs) { for (std::string const& b : - lg.ExpandCustomCommandOutputPaths(*cge, config)) { + this->ExpandCustomCommandOutputPaths(*cge, config)) { if (!seen.insert(b).second) { return false; } @@ -719,6 +717,7 @@ bool HasUniqueByproducts(cmLocalGenerator& lg, return true; } +namespace { bool HasUniqueOutputs(std::vector<cmCustomCommandGenerator> const& ccgs) { std::set<std::string> allOutputs; @@ -746,7 +745,7 @@ std::string cmLocalNinjaGenerator::CreateUtilityOutput( // In Ninja Multi-Config, we can only produce cross-config utility // commands if all byproducts are per-config. if (!this->GetGlobalGenerator()->IsMultiConfig() || - !HasUniqueByproducts(*this, byproducts, bt)) { + !this->HasUniqueByproducts(byproducts, bt)) { return this->cmLocalGenerator::CreateUtilityOutput(targetName, byproducts, bt); } diff --git a/Source/cmLocalNinjaGenerator.h b/Source/cmLocalNinjaGenerator.h index 87d5e53..5b850f3 100644 --- a/Source/cmLocalNinjaGenerator.h +++ b/Source/cmLocalNinjaGenerator.h @@ -86,6 +86,9 @@ public: cmNinjaDeps& ninjaDeps, const std::string& config); + bool HasUniqueByproducts(std::vector<std::string> const& byproducts, + cmListFileBacktrace const& bt); + protected: std::string ConvertToIncludeReference( std::string const& path, diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index e4c8e4b..cea20ad 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -303,7 +303,7 @@ void cmMakefile::PrintCommandTrace( args.reserve(lff.Arguments().size()); for (cmListFileArgument const& arg : lff.Arguments()) { - if (expand) { + if (expand && arg.Delim != cmListFileArgument::Bracket) { temp = arg.Value; this->ExpandVariablesInString(temp); args.push_back(temp); diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 620b8ff..49e5e4c 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -12,6 +12,7 @@ #include <utility> #include <cm/memory> +#include <cm/optional> #include <cm/vector> #include "cmComputeLinkInformation.h" @@ -1238,14 +1239,17 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( std::vector<std::string> preLinkCmdLines; std::vector<std::string> postBuildCmdLines; - if (config == fileConfig) { - std::vector<std::string>* cmdLineLists[3] = { &preLinkCmdLines, - &preLinkCmdLines, - &postBuildCmdLines }; - - for (unsigned i = 0; i != 3; ++i) { - for (cmCustomCommand const& cc : *cmdLists[i]) { - cmCustomCommandGenerator ccg(cc, config, this->GetLocalGenerator()); + std::vector<std::string>* cmdLineLists[3] = { &preLinkCmdLines, + &preLinkCmdLines, + &postBuildCmdLines }; + + for (unsigned i = 0; i != 3; ++i) { + for (cmCustomCommand const& cc : *cmdLists[i]) { + if (config == fileConfig || + this->GetLocalGenerator()->HasUniqueByproducts(cc.GetByproducts(), + cc.GetBacktrace())) { + cmCustomCommandGenerator ccg(cc, fileConfig, this->GetLocalGenerator(), + true, config); localGen.AppendCustomCommandLines(ccg, *cmdLineLists[i]); std::vector<std::string> const& ccByproducts = ccg.GetByproducts(); std::transform(ccByproducts.begin(), ccByproducts.end(), diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 74fca5c..5948911 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -1279,21 +1279,6 @@ if(BUILD_TESTING) set_property(TEST CMakeTestAllGenerators PROPERTY RUN_SERIAL 1) endif() - if(NOT DEFINED CTEST_RUN_CMakeTestMultipleConfigures) - set(CTEST_RUN_CMakeTestMultipleConfigures ON) - endif() - - if(CTEST_RUN_CMakeTestMultipleConfigures) - add_test(CMakeTestMultipleConfigures ${CMAKE_CMAKE_COMMAND} - -D dir=${CMake_BINARY_DIR}/Tests/CMakeTestMultipleConfigures - -D gen=${CMAKE_GENERATOR} - -D CMake_SOURCE_DIR=${CMake_SOURCE_DIR} - -P ${CMake_SOURCE_DIR}/Tests/CMakeTestMultipleConfigures/RunCMake.cmake - ) - list(APPEND TEST_BUILD_DIRS - "${CMake_BINARY_DIR}/Tests/CMakeTestMultipleConfigures") - endif() - if(NOT CMake_TEST_EXTERNAL_CMAKE) add_test(LoadedCommandOneConfig ${CMAKE_CTEST_COMMAND} --build-and-test diff --git a/Tests/CMakeTestMultipleConfigures/RunCMake.cmake b/Tests/CMakeTestMultipleConfigures/RunCMake.cmake deleted file mode 100644 index a79bfcb..0000000 --- a/Tests/CMakeTestMultipleConfigures/RunCMake.cmake +++ /dev/null @@ -1,165 +0,0 @@ -if(NOT DEFINED CMake_SOURCE_DIR) - message(FATAL_ERROR "CMake_SOURCE_DIR not defined") -endif() - -if(NOT DEFINED dir) - message(FATAL_ERROR "dir not defined") -endif() - -if(NOT DEFINED gen) - message(FATAL_ERROR "gen not defined") -endif() - -# Call cmake once to get a baseline/reference output build tree: "Build". -# Then call cmake N more times, each time making a copy of the entire -# build tree after cmake is done configuring/generating. At the end, -# analyze the diffs in the generated build trees. Expect no diffs. -# -message(STATUS "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)") - -set(N 7) - -# First setup source and binary trees: -# -execute_process(COMMAND ${CMAKE_COMMAND} -E rm -rf - ${dir}/Source -) - -execute_process(COMMAND ${CMAKE_COMMAND} -E rm -rf - ${dir}/Build -) - -execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory - ${CMake_SOURCE_DIR}/Tests/CTestTest/SmallAndFast - ${dir}/Source -) - -execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory - ${dir}/Build -) - -# Patch SmallAndFast to build a .cxx executable too: -# -execute_process(COMMAND ${CMAKE_COMMAND} -E copy - ${dir}/Source/echoargs.c - ${dir}/Source/echoargs.cxx -) -file(APPEND "${dir}/Source/CMakeLists.txt" "\nadd_executable(echoargsCXX echoargs.cxx)\n") - -# Loop N times, saving a copy of the configured/generated build tree each time: -# -foreach(i RANGE 1 ${N}) - # Equivalent sequence of shell commands: - # - message(STATUS "${i}: cd Build && cmake -G \"${gen}\" ../Source && cd .. && cp -r Build b${i}") - - # Run cmake: - # - execute_process(COMMAND ${CMAKE_COMMAND} -G ${gen} ../Source - RESULT_VARIABLE result - OUTPUT_VARIABLE stdout - ERROR_VARIABLE stderr - WORKING_DIRECTORY ${dir}/Build - ) - - message(STATUS "result='${result}'") - message(STATUS "stdout='${stdout}'") - message(STATUS "stderr='${stderr}'") - message(STATUS "") - - # Save this iteration of the Build directory: - # - execute_process(COMMAND ${CMAKE_COMMAND} -E rm -rf - ${dir}/b${i} - ) - execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory - ${dir}/Build - ${dir}/b${i} - RESULT_VARIABLE result - OUTPUT_VARIABLE stdout - ERROR_VARIABLE stderr - ) - - message(STATUS "result='${result}'") - message(STATUS "stdout='${stdout}'") - message(STATUS "stderr='${stderr}'") - message(STATUS "") -endforeach() - - -# Function to analyze diffs between two directories. -# Set DIFF_EXECUTABLE before calling if 'diff' is available. -# -function(analyze_directory_diffs d1 d2 diff_count_var) - set(diffs 0) - - message(STATUS "Analyzing directory diffs between:") - message(STATUS " d1='${d1}'") - message(STATUS " d2='${d2}'") - - if(NOT "${d1}" STREQUAL "" AND NOT "${d2}" STREQUAL "") - message(STATUS "info: analyzing directories") - - file(GLOB_RECURSE files1 RELATIVE "${d1}" "${d1}/*") - file(GLOB_RECURSE files2 RELATIVE "${d2}" "${d2}/*") - - if("${files1}" STREQUAL "${files2}") - message(STATUS "info: file lists the same") - #message(STATUS " files='${files1}'") - - foreach(f ${files1}) - execute_process(COMMAND ${CMAKE_COMMAND} -E compare_files - ${d1}/${f} - ${d2}/${f} - RESULT_VARIABLE result - OUTPUT_VARIABLE stdout - ERROR_VARIABLE stderr - ) - if(result STREQUAL 0) - #message(STATUS "info: file '${f}' the same") - else() - math(EXPR diffs "${diffs} + 1") - message(STATUS "warning: file '${f}' differs from d1 to d2") - file(READ "${d1}/${f}" f1contents) - message(STATUS "contents of file '${d1}/${f}' -[===[${f1contents}]===]") - file(READ "${d2}/${f}" f2contents) - message(STATUS "contents of file '${d2}/${f}' -[===[${f2contents}]===]") - if(DIFF_EXECUTABLE) - message(STATUS "diff of files '${d1}/${f}' '${d2}/${f}'") - message(STATUS "[====[") - execute_process(COMMAND ${DIFF_EXECUTABLE} "${d1}/${f}" "${d2}/${f}") - message(STATUS "]====]") - endif() - endif() - endforeach() - else() - math(EXPR diffs "${diffs} + 1") - message(STATUS "warning: file *lists* differ - some files exist in d1/not-d2 or not-d1/d2...") - message(STATUS " files1='${files1}'") - message(STATUS " files2='${files2}'") - endif() - endif() - - set(${diff_count_var} ${diffs} PARENT_SCOPE) -endfunction() - - -# Analyze diffs between b1:b2, b2:b3, b3:b4, b4:b5 ... bN-1:bN. -# Expect no diffs. -# -find_program(DIFF_EXECUTABLE diff) -set(total_diffs 0) - -foreach(i RANGE 2 ${N}) - math(EXPR prev "${i} - 1") - set(count 0) - analyze_directory_diffs(${dir}/b${prev} ${dir}/b${i} count) - message(STATUS "diff count='${count}'") - message(STATUS "") - math(EXPR total_diffs "${total_diffs} + ${count}") -endforeach() - -message(STATUS "CMAKE_COMMAND='${CMAKE_COMMAND}'") -message(STATUS "total_diffs='${total_diffs}'") diff --git a/Tests/CMakeTests/ListTest.cmake.in b/Tests/CMakeTests/ListTest.cmake.in index 785f41d..76737e5 100644 --- a/Tests/CMakeTests/ListTest.cmake.in +++ b/Tests/CMakeTests/ListTest.cmake.in @@ -142,9 +142,8 @@ set(Find-List-Only-STDERR "three") set(Insert-List-Only-STDERR "at least three") set(Length-List-Only-STDERR "two") set(Remove_At-List-Only-STDERR "at least two") -set(Remove_Item-List-Only-STDERR "two or more") -foreach(cmd IN ITEMS Find Get Insert Length Remove_At Remove_Item) +foreach(cmd IN ITEMS Find Get Insert Length Remove_At) string(TOUPPER ${cmd} cmd_upper) set(${cmd}-List-Only-RESULT 1) set(${cmd}-List-Only-STDERR ".*CMake Error at List-${cmd}-List-Only.cmake:1 \\(list\\):.*list sub-command ${cmd_upper} requires ${${cmd}-List-Only-STDERR} arguments.*") diff --git a/Tests/Fuzzing/README.rst b/Tests/Fuzzing/README.rst new file mode 100644 index 0000000..a869f9c --- /dev/null +++ b/Tests/Fuzzing/README.rst @@ -0,0 +1,8 @@ +The fuzzers in this directory are run continuously through OSS-fuzz. +All fuzzers are implemented by way of the `libFuzzer engine`_. + +The link to the OSS-fuzz integration can be found here: (pending) +All email addresses in the `project.yaml` file on OSS-fuzz will have access +to detailed bug reports and will be notified via email if/when bugs are found. + +.. _`libFuzzer Engine`: https://llvm.org/docs/LibFuzzer.html diff --git a/Tests/Fuzzing/xml_parser_fuzzer.cc b/Tests/Fuzzing/xml_parser_fuzzer.cc new file mode 100644 index 0000000..1faa918 --- /dev/null +++ b/Tests/Fuzzing/xml_parser_fuzzer.cc @@ -0,0 +1,27 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include "cmXMLParser.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + char test_file[] = "libfuzzer.xml"; + + FILE* fp = fopen(test_file, "wb"); + if (!fp) + return 0; + fwrite(data, size, 1, fp); + fclose(fp); + + cmXMLParser parser; + if (!parser.ParseFile(test_file)) { + return 1; + } + + remove(test_file); + return 0; +} diff --git a/Tests/IncludeDirectories/CMakeLists.txt b/Tests/IncludeDirectories/CMakeLists.txt index d980a52..d4c19c7 100644 --- a/Tests/IncludeDirectories/CMakeLists.txt +++ b/Tests/IncludeDirectories/CMakeLists.txt @@ -67,13 +67,7 @@ else() endif() # Test escaping of special characters in include directory paths. -set(special_chars "~@&{}()'") -if(NOT (CMAKE_GENERATOR STREQUAL "NMake Makefiles" AND - "x${CMAKE_C_COMPILER_ID}" STREQUAL "xMSVC" AND - "${CMAKE_C_COMPILER_VERSION}" VERSION_LESS 13.0)) - # NMake from VS 6 mistakes '!' in a path after a line continuation for a directive. - string(APPEND special_chars "!") -endif() +set(special_chars "~@&{}()!'") if(NOT CMAKE_GENERATOR MATCHES "(Unix|MinGW|MSYS) Makefiles") # when compiler is used for dependencies, special characters for make are not escaped string(APPEND special_chars "%") diff --git a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt index a8b6584..c76c92d 100644 --- a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt +++ b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt @@ -1 +1 @@ -^{"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":2}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"version":{.*}}$ +^{"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":2}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]},{"kind":"toolchains","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"version":{.*}}$ diff --git a/Tests/RunCMake/CommandLine/trace-expand.cmake b/Tests/RunCMake/CommandLine/trace-expand.cmake index e69de29..24da02a 100644 --- a/Tests/RunCMake/CommandLine/trace-expand.cmake +++ b/Tests/RunCMake/CommandLine/trace-expand.cmake @@ -0,0 +1 @@ +set(a [[\B]]) diff --git a/Tests/RunCMake/ExternalProject/CONFIGURE_HANDLED_BY_BUILD-rebuild-check.cmake b/Tests/RunCMake/ExternalProject/CONFIGURE_HANDLED_BY_BUILD-rebuild-check.cmake new file mode 100644 index 0000000..887da0f --- /dev/null +++ b/Tests/RunCMake/ExternalProject/CONFIGURE_HANDLED_BY_BUILD-rebuild-check.cmake @@ -0,0 +1,19 @@ +file(TIMESTAMP "${STAMP_DIR}/proj1-configure" PROJ1_CONFIGURE_TIMESTAMP_AFTER "%s") +# When BUILD_ALWAYS is set, the build stamp is never created. +file(TIMESTAMP "${STAMP_DIR}/proj2-configure" PROJ2_CONFIGURE_TIMESTAMP_AFTER "%s") +file(TIMESTAMP "${STAMP_DIR}/proj2-build" PROJ2_BUILD_TIMESTAMP_AFTER "%s") + +if(NOT PROJ1_CONFIGURE_TIMESTAMP_BEFORE EQUAL PROJ1_CONFIGURE_TIMESTAMP_AFTER) + set(RunCMake_TEST_FAILED "Unexpected rebuild of proj1 configure step (${PROJ1_CONFIGURE_TIMESTAMP_BEFORE} != ${PROJ1_CONFIGURE_TIMESTAMP_AFTER})") + return() +endif() + +if(NOT PROJ2_CONFIGURE_TIMESTAMP_BEFORE EQUAL PROJ2_CONFIGURE_TIMESTAMP_AFTER) + set(RunCMake_TEST_FAILED "Unexpected rebuild of proj2 configure step (${PROJ2_CONFIGURE_TIMESTAMP_BEFORE} != ${PROJ2_CONFIGURE_TIMESTAMP_AFTER})") + return() +endif() + +if(PROJ2_BUILD_TIMESTAMP_BEFORE EQUAL PROJ2_BUILD_TIMESTAMP_AFTER) + set(RunCMake_TEST_FAILED "proj2 build step did not rebuild (${PROJ2_BUILD_TIMESTAMP_BEFORE} != ${PROJ2_BUILD_TIMESTAMP_AFTER})") + return() +endif() diff --git a/Tests/RunCMake/ExternalProject/CONFIGURE_HANDLED_BY_BUILD.cmake b/Tests/RunCMake/ExternalProject/CONFIGURE_HANDLED_BY_BUILD.cmake new file mode 100644 index 0000000..c86a60e --- /dev/null +++ b/Tests/RunCMake/ExternalProject/CONFIGURE_HANDLED_BY_BUILD.cmake @@ -0,0 +1,28 @@ +include(ExternalProject) + +# Given this setup, on the first build, both configure steps and both build +# steps will run. On a noop rebuild, only the build steps will run. Without +# CONFIGURE_HANDLED_BY_BUILD, the configure step of proj2 would also run on a +# noop rebuild. + +ExternalProject_Add(proj1 + DOWNLOAD_COMMAND "" + SOURCE_DIR "" + CONFIGURE_COMMAND ${CMAKE_COMMAND} -E echo "Doing something" + # file(TIMESTAMP) gives back the timestamp in seconds so we sleep a second to + # make sure we get a different timestamp on the stamp file + BUILD_COMMAND ${CMAKE_COMMAND} -E sleep 1 + INSTALL_COMMAND "" + BUILD_ALWAYS ON + STAMP_DIR "stamp" +) +ExternalProject_Add(proj2 + DOWNLOAD_COMMAND "" + SOURCE_DIR "" + CONFIGURE_COMMAND ${CMAKE_COMMAND} -E echo "Doing something" + BUILD_COMMAND ${CMAKE_COMMAND} -E sleep 1 + INSTALL_COMMAND "" + CONFIGURE_HANDLED_BY_BUILD ON + DEPENDS proj1 + STAMP_DIR "stamp" +) diff --git a/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake b/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake index 598671f..22b8d24 100644 --- a/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake +++ b/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake @@ -151,3 +151,33 @@ endif() if(doSubstitutionTest) __ep_test_with_build(Substitutions) endif() + +function(__ep_test_CONFIGURE_HANDLED_BY_BUILD) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CONFIGURE_HANDLED_BY_BUILD-build) + run_cmake(CONFIGURE_HANDLED_BY_BUILD) + + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(BUILD_CONFIG --config Debug) + set(STAMP_DIR "${RunCMake_TEST_BINARY_DIR}/stamp/Debug") + else() + set(BUILD_CONFIG "") + set(STAMP_DIR "${RunCMake_TEST_BINARY_DIR}/stamp") + endif() + + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(CONFIGURE_HANDLED_BY_BUILD-build ${CMAKE_COMMAND} --build . ${BUILD_CONFIG}) + + # Calculate timestamps before rebuilding so we can compare before and after in + # CONFIGURE_HANDLED_BY_BUILD-rebuild-check.cmake + + file(TIMESTAMP "${STAMP_DIR}/proj1-configure" PROJ1_CONFIGURE_TIMESTAMP_BEFORE "%s") + # When BUILD_ALWAYS is set, the build stamp is never created. + file(TIMESTAMP "${STAMP_DIR}/proj2-configure" PROJ2_CONFIGURE_TIMESTAMP_BEFORE "%s") + file(TIMESTAMP "${STAMP_DIR}/proj2-build" PROJ2_BUILD_TIMESTAMP_BEFORE "%s") + + run_cmake_command(CONFIGURE_HANDLED_BY_BUILD-rebuild ${CMAKE_COMMAND} --build . ${BUILD_CONFIG}) +endfunction() + +if(NOT RunCMake_GENERATOR MATCHES "Visual Studio 9 ") + __ep_test_CONFIGURE_HANDLED_BY_BUILD() +endif() diff --git a/Tests/RunCMake/FileAPI/RunCMakeTest.cmake b/Tests/RunCMake/FileAPI/RunCMakeTest.cmake index 2bb2765..ae3d179 100644 --- a/Tests/RunCMake/FileAPI/RunCMakeTest.cmake +++ b/Tests/RunCMake/FileAPI/RunCMakeTest.cmake @@ -24,6 +24,7 @@ function(check_python case) file(GLOB index ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/index-*.json) execute_process( COMMAND ${PYTHON_EXECUTABLE} "${RunCMake_SOURCE_DIR}/${case}-check.py" "${index}" "${CMAKE_CXX_COMPILER_ID}" + "${RunCMake_TEST_BINARY_DIR}" RESULT_VARIABLE result OUTPUT_VARIABLE output ERROR_VARIABLE output @@ -62,3 +63,4 @@ endfunction() run_object(codemodel-v2) run_object(cache-v2) run_object(cmakeFiles-v1) +run_object(toolchains-v1) diff --git a/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateful-check.cmake b/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateful-check.cmake new file mode 100644 index 0000000..ce38461 --- /dev/null +++ b/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateful-check.cmake @@ -0,0 +1,11 @@ +set(expect + query + query/client-foo + query/client-foo/query.json + reply + reply/index-[0-9.T-]+.json + reply/toolchains-v1-[0-9a-f]+.json + ) +check_api("^${expect}$") + +check_python(toolchains-v1) diff --git a/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateful-prep.cmake b/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateful-prep.cmake new file mode 100644 index 0000000..ca62edf --- /dev/null +++ b/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateful-prep.cmake @@ -0,0 +1,4 @@ +file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/query.json" [[ +{ "requests": [ { "kind": "toolchains", "version" : 1 } ] } +]]) diff --git a/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateless-check.cmake b/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateless-check.cmake new file mode 100644 index 0000000..4676dd8 --- /dev/null +++ b/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateless-check.cmake @@ -0,0 +1,11 @@ +set(expect + query + query/client-foo + query/client-foo/toolchains-v1 + reply + reply/index-[0-9.T-]+.json + reply/toolchains-v1-[0-9a-f]+.json + ) +check_api("^${expect}$") + +check_python(toolchains-v1) diff --git a/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateless-prep.cmake b/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateless-prep.cmake new file mode 100644 index 0000000..7edff93 --- /dev/null +++ b/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateless-prep.cmake @@ -0,0 +1,2 @@ +file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/toolchains-v1" "") diff --git a/Tests/RunCMake/FileAPI/toolchains-v1-SharedStateless-check.cmake b/Tests/RunCMake/FileAPI/toolchains-v1-SharedStateless-check.cmake new file mode 100644 index 0000000..8e83758 --- /dev/null +++ b/Tests/RunCMake/FileAPI/toolchains-v1-SharedStateless-check.cmake @@ -0,0 +1,10 @@ +set(expect + query + query/toolchains-v1 + reply + reply/index-[0-9.T-]+.json + reply/toolchains-v1-[0-9a-f]+.json + ) +check_api("^${expect}$") + +check_python(toolchains-v1) diff --git a/Tests/RunCMake/FileAPI/toolchains-v1-SharedStateless-prep.cmake b/Tests/RunCMake/FileAPI/toolchains-v1-SharedStateless-prep.cmake new file mode 100644 index 0000000..2db73a1 --- /dev/null +++ b/Tests/RunCMake/FileAPI/toolchains-v1-SharedStateless-prep.cmake @@ -0,0 +1,2 @@ +file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/toolchains-v1" "") diff --git a/Tests/RunCMake/FileAPI/toolchains-v1-check.py b/Tests/RunCMake/FileAPI/toolchains-v1-check.py new file mode 100644 index 0000000..a0e50c2 --- /dev/null +++ b/Tests/RunCMake/FileAPI/toolchains-v1-check.py @@ -0,0 +1,86 @@ +from check_index import * +import os + +class ExpectedVar(object): + def __init__(self, name): + self.name = name + +class ExpectedList(object): + def __init__(self, name): + self.name = name + +EXPECTED_TOOLCHAIN = { + "language": "CXX", + "compiler": { + "path": ExpectedVar("CMAKE_CXX_COMPILER"), + "id": ExpectedVar("CMAKE_CXX_COMPILER_ID"), + "version": ExpectedVar("CMAKE_CXX_COMPILER_VERSION"), + "target": ExpectedVar("CMAKE_CXX_COMPILER_TARGET"), + "implicit": { + "includeDirectories": \ + ExpectedList("CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES"), + "linkDirectories": \ + ExpectedList("CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES"), + "linkFrameworkDirectories": \ + ExpectedList( + "CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES"), + "linkLibraries": \ + ExpectedList("CMAKE_CXX_IMPLICIT_LINK_LIBRARIES"), + } + }, + "sourceFileExtensions": \ + ExpectedList("CMAKE_CXX_SOURCE_FILE_EXTENSIONS"), +} + +def check_objects(o): + assert is_list(o) + assert len(o) == 1 + check_index_object(o[0], "toolchains", 1, 0, check_object_toolchains) + +def check_object_toolchains(o): + assert sorted(o.keys()) == ["kind", "toolchains", "version"] + # The "kind" and "version" members are handled by check_index_object. + toolchains = o["toolchains"] + assert is_list(toolchains) + + # Other platform-specific toolchains may exist (like RC on Windows). + has_cxx_toolchain = False + for toolchain in toolchains: + assert is_dict(toolchain) + assert "language" in toolchain + if toolchain["language"] == "CXX": + check_object_toolchain(toolchain, EXPECTED_TOOLCHAIN) + has_cxx_toolchain = True + + assert has_cxx_toolchain + +def check_object_toolchain(o, expected): + expected_keys = [ + key for (key, value) in expected.items() + if is_string(value) or is_dict(value) + or (type(value) in (ExpectedVar, ExpectedList) + and variables[value.name]["defined"])] + assert sorted(o.keys()) == sorted(expected_keys) + + for key in expected_keys: + value = expected[key] + if is_string(value): + assert o[key] == value + elif is_dict(value): + check_object_toolchain(o[key], value) + elif type(value) == ExpectedVar: + assert o[key] == variables[value.name]["value"] + elif type(value) == ExpectedList: + expected_items = filter( + None, variables[value.name]["value"].split(";")) + check_list_match(lambda a, b: a == b, o[key], expected_items) + else: + assert False + +with open(os.path.join(sys.argv[3], "toolchain_variables.json")) as f: + variables = json.load(f) + +assert is_dict(variables) +assert is_dict(index) +assert sorted(index.keys()) == ["cmake", "objects", "reply"] +check_objects(index["objects"]) diff --git a/Tests/RunCMake/FileAPI/toolchains-v1.cmake b/Tests/RunCMake/FileAPI/toolchains-v1.cmake new file mode 100644 index 0000000..367aade --- /dev/null +++ b/Tests/RunCMake/FileAPI/toolchains-v1.cmake @@ -0,0 +1,22 @@ +enable_language(CXX) + +set(variable_suffixes + COMPILER COMPILER_ID COMPILER_VERSION COMPILER_TARGET + IMPLICIT_INCLUDE_DIRECTORIES IMPLICIT_LINK_DIRECTORIES + IMPLICIT_LINK_FRAMEWORK_DIRECTORIES IMPLICIT_LINK_LIBRARIES + SOURCE_FILE_EXTENSIONS) +set(language CXX) +set(json "{}") + +foreach(variable_suffix ${variable_suffixes}) + set(variable "CMAKE_${language}_${variable_suffix}") + string(JSON json SET "${json}" "${variable}" "{}") + if(DEFINED "${variable}") + string(JSON json SET "${json}" "${variable}" "defined" "true") + string(JSON json SET "${json}" "${variable}" "value" "\"${${variable}}\"") + else() + string(JSON json SET "${json}" "${variable}" "defined" "false") + endif() +endforeach() + +file(WRITE ${CMAKE_BINARY_DIR}/toolchain_variables.json "${json}") diff --git a/Tests/RunCMake/NinjaMultiConfig/PostBuild-debug-in-release-graph-build-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/PostBuild-debug-in-release-graph-build-stdout.txt new file mode 100644 index 0000000..fad923a --- /dev/null +++ b/Tests/RunCMake/NinjaMultiConfig/PostBuild-debug-in-release-graph-build-stdout.txt @@ -0,0 +1,2 @@ +Running post-build command with Debug +Generating Debug\.txt diff --git a/Tests/RunCMake/NinjaMultiConfig/PostBuild-release-build-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/PostBuild-release-build-stdout.txt new file mode 100644 index 0000000..485a52c --- /dev/null +++ b/Tests/RunCMake/NinjaMultiConfig/PostBuild-release-build-stdout.txt @@ -0,0 +1,3 @@ +Running post-build command with Release +Generating out\.txt with Release +Generating Release\.txt diff --git a/Tests/RunCMake/NinjaMultiConfig/PostBuild.cmake b/Tests/RunCMake/NinjaMultiConfig/PostBuild.cmake new file mode 100644 index 0000000..5fcff74 --- /dev/null +++ b/Tests/RunCMake/NinjaMultiConfig/PostBuild.cmake @@ -0,0 +1,6 @@ +enable_language(C) + +add_executable(Exe main.c) +add_custom_command(TARGET Exe POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo "Running post-build command with $<CONFIG>") +add_custom_command(TARGET Exe POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo "Generating out.txt with $<CONFIG>" BYPRODUCTS out.txt) +add_custom_command(TARGET Exe POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo "Generating $<CONFIG>.txt" BYPRODUCTS $<CONFIG>.txt) diff --git a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake index dc2db57..480d628 100644 --- a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake +++ b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake @@ -187,6 +187,12 @@ run_ninja(SimpleCrossConfigs clean-all-in-release-graph build-Release.ninja clea run_cmake_build(SimpleCrossConfigs all-all-in-release-graph Release all:all) run_cmake_build(SimpleCrossConfigs all-relwithdebinfo-in-release-graph Release all:RelWithDebInfo) +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/PostBuild-build) +set(RunCMake_TEST_OPTIONS "-DCMAKE_CROSS_CONFIGS=all") +run_cmake_configure(PostBuild) +run_cmake_build(PostBuild release Release Exe) +run_cmake_build(PostBuild debug-in-release-graph Release Exe:Debug) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Framework-build) set(RunCMake_TEST_OPTIONS "-DCMAKE_CROSS_CONFIGS=all") run_cmake_configure(Framework) diff --git a/Tests/RunCMake/cmake_path/HASH.cmake b/Tests/RunCMake/cmake_path/HASH.cmake index dfcf2b2..eb04f7f 100644 --- a/Tests/RunCMake/cmake_path/HASH.cmake +++ b/Tests/RunCMake/cmake_path/HASH.cmake @@ -14,11 +14,6 @@ set (path1 "a///b/c/../d") cmake_path(HASH path1 hash1) set (path2 "a/b////d") cmake_path(HASH path2 hash2) -if (hash1 STREQUAL hash2) - list (APPEND errors "'hash values equal for '${path1}' and '${path2}'") -endif() -cmake_path(HASH path1 hash1 NORMALIZE) -cmake_path(HASH path2 NORMALIZE hash2) if (NOT hash1 STREQUAL hash2) list (APPEND errors "'hash values not equal for '${path1}' and '${path2}'") endif() diff --git a/Tests/RunCMake/cmake_path/IS_PREFIX.cmake b/Tests/RunCMake/cmake_path/IS_PREFIX.cmake index 53da93b..9160dab 100644 --- a/Tests/RunCMake/cmake_path/IS_PREFIX.cmake +++ b/Tests/RunCMake/cmake_path/IS_PREFIX.cmake @@ -18,5 +18,10 @@ if (NOT output) list (APPEND errors "'${path} is not prefix of 'a/b/d/e'") endif() +set(path "/a/b/..") +cmake_path(IS_PREFIX path "/a/c/../b" NORMALIZE output) +if (NOT output) + list (APPEND errors "'${path} is not prefix of '/a/c/../b'") +endif() check_errors (IS_PREFIX ${errors}) diff --git a/Tests/RunCMake/install/FILES-RENAME-all-check.cmake b/Tests/RunCMake/install/FILES-RENAME-all-check.cmake new file mode 100644 index 0000000..7e9b103 --- /dev/null +++ b/Tests/RunCMake/install/FILES-RENAME-all-check.cmake @@ -0,0 +1 @@ +check_installed([[^src;src/script_Debug\.ps]]) diff --git a/Tests/RunCMake/install/FILES-RENAME-bad-result.txt b/Tests/RunCMake/install/FILES-RENAME-bad-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/FILES-RENAME-bad-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/FILES-RENAME-bad-stderr.txt b/Tests/RunCMake/install/FILES-RENAME-bad-stderr.txt new file mode 100644 index 0000000..9844158 --- /dev/null +++ b/Tests/RunCMake/install/FILES-RENAME-bad-stderr.txt @@ -0,0 +1,6 @@ +CMake Error: + Error evaluating generator expression: + + \$<NOTAGENEX> + + Expression did not evaluate to a known generator expression diff --git a/Tests/RunCMake/install/FILES-RENAME-bad.cmake b/Tests/RunCMake/install/FILES-RENAME-bad.cmake new file mode 100644 index 0000000..5be0bb2 --- /dev/null +++ b/Tests/RunCMake/install/FILES-RENAME-bad.cmake @@ -0,0 +1,4 @@ +install(FILES empty.c + DESTINATION mybin + RENAME $<NOTAGENEX> + ) diff --git a/Tests/RunCMake/install/FILES-RENAME.cmake b/Tests/RunCMake/install/FILES-RENAME.cmake new file mode 100644 index 0000000..5896e64 --- /dev/null +++ b/Tests/RunCMake/install/FILES-RENAME.cmake @@ -0,0 +1,4 @@ +install(FILES script.bat + DESTINATION src + RENAME script_$<CONFIG>.ps + ) diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake index d64d88b..b067b3a 100644 --- a/Tests/RunCMake/install/RunCMakeTest.cmake +++ b/Tests/RunCMake/install/RunCMakeTest.cmake @@ -74,6 +74,7 @@ run_cmake(SkipInstallRulesNoWarning2) run_cmake(DIRECTORY-DIRECTORY-bad) run_cmake(DIRECTORY-DESTINATION-bad) run_cmake(FILES-DESTINATION-bad) +run_cmake(FILES-RENAME-bad) run_cmake(TARGETS-DESTINATION-bad) run_cmake(EXPORT-OldIFace) run_cmake(EXPORT-UnknownExport) @@ -91,6 +92,10 @@ run_cmake(TARGETS-NAMELINK_COMPONENT-bad-exc) run_cmake(FILES-DESTINATION-TYPE) run_cmake(DIRECTORY-DESTINATION-TYPE) +set(RunCMake_TEST_OPTIONS "-DCMAKE_BUILD_TYPE:STRING=Debug") +run_install_test(FILES-RENAME) +unset(RunCMake_TEST_OPTIONS) + if(APPLE) run_cmake(TARGETS-Apple-Defaults) endif() diff --git a/Tests/RunCMake/list/REMOVE_ITEM-NoItemArg.cmake b/Tests/RunCMake/list/REMOVE_ITEM-NoItemArg.cmake new file mode 100644 index 0000000..f69c024 --- /dev/null +++ b/Tests/RunCMake/list/REMOVE_ITEM-NoItemArg.cmake @@ -0,0 +1,5 @@ +set(ls "a" "b" "c") +list(REMOVE_ITEM ls alpha) +if (NOT ls STREQUAL "a;b;c") + message(FATAL_ERROR "list(REMOVE_ITEM) modified for empty item") +endif () diff --git a/Tests/RunCMake/list/RunCMakeTest.cmake b/Tests/RunCMake/list/RunCMakeTest.cmake index b4a91bc..c11891c 100644 --- a/Tests/RunCMake/list/RunCMakeTest.cmake +++ b/Tests/RunCMake/list/RunCMakeTest.cmake @@ -30,6 +30,7 @@ run_cmake(FILTER-NotList) run_cmake(REMOVE_AT-NotList) run_cmake(REMOVE_DUPLICATES-NotList) run_cmake(REMOVE_ITEM-NotList) +run_cmake(REMOVE_ITEM-NoItemArg) run_cmake(REVERSE-NotList) run_cmake(SORT-NotList) |