| # Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| # file Copyright.txt or https://cmake.org/licensing for details. | |
| include_guard(GLOBAL) | |
| #[=======================================================================[.rst: | |
| ExternalProject | |
| --------------- | |
| .. only:: html | |
| .. contents:: | |
| Commands | |
| ^^^^^^^^ | |
| External Project Definition | |
| """"""""""""""""""""""""""" | |
| .. command:: ExternalProject_Add | |
| The ``ExternalProject_Add()`` function creates a custom target to drive | |
| download, update/patch, configure, build, install and test steps of an | |
| external project: | |
| .. code-block:: cmake | |
| ExternalProject_Add(<name> [<option>...]) | |
| The individual steps within the process can be driven independently if | |
| required (e.g. for CDash submission) and extra custom steps can be defined, | |
| along with the ability to control the step dependencies. The directory | |
| structure used for the management of the external project can also be | |
| customized. The function supports a large number of options which can be used | |
| to tailor the external project behavior. | |
| **Directory Options:** | |
| Most of the time, the default directory layout is sufficient. It is largely | |
| an implementation detail that the main project usually doesn't need to | |
| change. In some circumstances, however, control over the directory layout | |
| can be useful or necessary. The directory options are potentially more | |
| useful from the point of view that the main build can use the | |
| :command:`ExternalProject_Get_Property` command to retrieve their values, | |
| thereby allowing the main project to refer to build artifacts of the | |
| external project. | |
| ``PREFIX <dir>`` | |
| Root directory for the external project. Unless otherwise noted below, | |
| all other directories associated with the external project will be | |
| created under here. | |
| ``TMP_DIR <dir>`` | |
| Directory in which to store temporary files. | |
| ``STAMP_DIR <dir>`` | |
| Directory in which to store the timestamps of each step. Log files from | |
| individual steps are also created in here unless overridden by LOG_DIR | |
| (see *Logging Options* below). | |
| ``LOG_DIR <dir>`` | |
| .. versionadded:: 3.14 | |
| Directory in which to store the logs of each step. | |
| ``DOWNLOAD_DIR <dir>`` | |
| Directory in which to store downloaded files before unpacking them. This | |
| directory is only used by the URL download method, all other download | |
| methods use ``SOURCE_DIR`` directly instead. | |
| ``SOURCE_DIR <dir>`` | |
| Source directory into which downloaded contents will be unpacked, or for | |
| non-URL download methods, the directory in which the repository should be | |
| checked out, cloned, etc. If no download method is specified, this must | |
| point to an existing directory where the external project has already | |
| been unpacked or cloned/checked out. | |
| .. note:: | |
| If a download method is specified, any existing contents of the source | |
| directory may be deleted. Only the URL download method checks whether | |
| this directory is either missing or empty before initiating the | |
| download, stopping with an error if it is not empty. All other | |
| download methods silently discard any previous contents of the source | |
| directory. | |
| ``BINARY_DIR <dir>`` | |
| Specify the build directory location. This option is ignored if | |
| ``BUILD_IN_SOURCE`` is enabled. | |
| ``INSTALL_DIR <dir>`` | |
| Installation prefix to be placed in the ``<INSTALL_DIR>`` placeholder. | |
| This does not actually configure the external project to install to | |
| the given prefix. That must be done by passing appropriate arguments | |
| to the external project configuration step, e.g. using ``<INSTALL_DIR>``. | |
| If any of the above ``..._DIR`` options are not specified, their defaults | |
| are computed as follows. If the ``PREFIX`` option is given or the | |
| ``EP_PREFIX`` directory property is set, then an external project is built | |
| and installed under the specified prefix:: | |
| TMP_DIR = <prefix>/tmp | |
| STAMP_DIR = <prefix>/src/<name>-stamp | |
| DOWNLOAD_DIR = <prefix>/src | |
| SOURCE_DIR = <prefix>/src/<name> | |
| BINARY_DIR = <prefix>/src/<name>-build | |
| INSTALL_DIR = <prefix> | |
| LOG_DIR = <STAMP_DIR> | |
| Otherwise, if the ``EP_BASE`` directory property is set then components | |
| of an external project are stored under the specified base:: | |
| TMP_DIR = <base>/tmp/<name> | |
| STAMP_DIR = <base>/Stamp/<name> | |
| DOWNLOAD_DIR = <base>/Download/<name> | |
| SOURCE_DIR = <base>/Source/<name> | |
| BINARY_DIR = <base>/Build/<name> | |
| INSTALL_DIR = <base>/Install/<name> | |
| LOG_DIR = <STAMP_DIR> | |
| If no ``PREFIX``, ``EP_PREFIX``, or ``EP_BASE`` is specified, then the | |
| default is to set ``PREFIX`` to ``<name>-prefix``. Relative paths are | |
| interpreted with respect to :variable:`CMAKE_CURRENT_BINARY_DIR` at the | |
| point where ``ExternalProject_Add()`` is called. | |
| **Download Step Options:** | |
| A download method can be omitted if the ``SOURCE_DIR`` option is used to | |
| point to an existing non-empty directory. Otherwise, one of the download | |
| methods below must be specified (multiple download methods should not be | |
| given) or a custom ``DOWNLOAD_COMMAND`` provided. | |
| ``DOWNLOAD_COMMAND <cmd>...`` | |
| Overrides the command used for the download step | |
| (:manual:`generator expressions <cmake-generator-expressions(7)>` are | |
| supported). If this option is specified, all other download options will | |
| be ignored. Providing an empty string for ``<cmd>`` effectively disables | |
| the download step. | |
| *URL Download* | |
| ``URL <url1> [<url2>...]`` | |
| List of paths and/or URL(s) of the external project's source. When more | |
| than one URL is given, they are tried in turn until one succeeds. A URL | |
| may be an ordinary path in the local file system (in which case it | |
| must be the only URL provided) or any downloadable URL supported by the | |
| :command:`file(DOWNLOAD)` command. A local filesystem path may refer to | |
| either an existing directory or to an archive file, whereas a URL is | |
| expected to point to a file which can be treated as an archive. When an | |
| archive is used, it will be unpacked automatically unless the | |
| ``DOWNLOAD_NO_EXTRACT`` option is set to prevent it. The archive type | |
| is determined by inspecting the actual content rather than using logic | |
| based on the file extension. | |
| .. versionchanged:: 3.7 | |
| Multiple URLs are allowed. | |
| ``URL_HASH <algo>=<hashValue>`` | |
| Hash of the archive file to be downloaded. The argument should be of | |
| the form ``<algo>=<hashValue>`` where ``algo`` can be any of the hashing | |
| algorithms supported by the :command:`file()` command. Specifying this | |
| option is strongly recommended for URL downloads, as it ensures the | |
| integrity of the downloaded content. It is also used as a check for a | |
| previously downloaded file, allowing connection to the remote location | |
| to be avoided altogether if the local directory already has a file from | |
| an earlier download that matches the specified hash. | |
| ``URL_MD5 <md5>`` | |
| Equivalent to ``URL_HASH MD5=<md5>``. | |
| ``DOWNLOAD_NAME <fname>`` | |
| File name to use for the downloaded file. If not given, the end of the | |
| URL is used to determine the file name. This option is rarely needed, | |
| the default name is generally suitable and is not normally used outside | |
| of code internal to the ``ExternalProject`` module. | |
| ``DOWNLOAD_NO_EXTRACT <bool>`` | |
| .. versionadded:: 3.6 | |
| Allows the extraction part of the download step to be disabled by | |
| passing a boolean true value for this option. If this option is not | |
| given, the downloaded contents will be unpacked automatically if | |
| required. If extraction has been disabled, the full path to the | |
| downloaded file is available as ``<DOWNLOADED_FILE>`` in subsequent | |
| steps or as the property ``DOWNLOADED_FILE`` with the | |
| :command:`ExternalProject_Get_Property` command. | |
| ``DOWNLOAD_NO_PROGRESS <bool>`` | |
| Can be used to disable logging the download progress. If this option is | |
| not given, download progress messages will be logged. | |
| ``TIMEOUT <seconds>`` | |
| Maximum time allowed for file download operations. | |
| ``INACTIVITY_TIMEOUT <seconds>`` | |
| .. versionadded:: 3.19 | |
| Terminate the operation after a period of inactivity. | |
| ``HTTP_USERNAME <username>`` | |
| .. versionadded:: 3.7 | |
| Username for the download operation if authentication is required. | |
| ``HTTP_PASSWORD <password>`` | |
| .. versionadded:: 3.7 | |
| Password for the download operation if authentication is required. | |
| ``HTTP_HEADER <header1> [<header2>...]`` | |
| .. versionadded:: 3.7 | |
| Provides an arbitrary list of HTTP headers for the download operation. | |
| This can be useful for accessing content in systems like AWS, etc. | |
| ``TLS_VERIFY <bool>`` | |
| Specifies whether certificate verification should be performed for | |
| https URLs. If this option is not provided, the default behavior is | |
| determined by the :variable:`CMAKE_TLS_VERIFY` variable (see | |
| :command:`file(DOWNLOAD)`). If that is also not set, certificate | |
| verification will not be performed. In situations where ``URL_HASH`` | |
| cannot be provided, this option can be an alternative verification | |
| measure. | |
| .. versionchanged:: 3.6 | |
| This option also applies to ``git clone`` invocations. | |
| ``TLS_CAINFO <file>`` | |
| Specify a custom certificate authority file to use if ``TLS_VERIFY`` | |
| is enabled. If this option is not specified, the value of the | |
| :variable:`CMAKE_TLS_CAINFO` variable will be used instead (see | |
| :command:`file(DOWNLOAD)`) | |
| ``NETRC <level>`` | |
| .. versionadded:: 3.11 | |
| Specify whether the ``.netrc`` file is to be used for operation. | |
| If this option is not specified, the value of the :variable:`CMAKE_NETRC` | |
| variable will be used instead (see :command:`file(DOWNLOAD)`) | |
| Valid levels are: | |
| ``IGNORED`` | |
| The ``.netrc`` file is ignored. | |
| This is the default. | |
| ``OPTIONAL`` | |
| The ``.netrc`` file is optional, and information in the URL | |
| is preferred. The file will be scanned to find which ever | |
| information is not specified in the URL. | |
| ``REQUIRED`` | |
| The ``.netrc`` file is required, and information in the URL | |
| is ignored. | |
| ``NETRC_FILE <file>`` | |
| .. versionadded:: 3.11 | |
| Specify an alternative ``.netrc`` file to the one in your home directory | |
| if the ``NETRC`` level is ``OPTIONAL`` or ``REQUIRED``. If this option | |
| is not specified, the value of the :variable:`CMAKE_NETRC_FILE` variable | |
| will be used instead (see :command:`file(DOWNLOAD)`) | |
| .. versionadded:: 3.1 | |
| Added support for `tbz2`, `.tar.xz`, `.txz`, and `.7z` extensions. | |
| *Git* | |
| NOTE: A git version of 1.6.5 or later is required if this download method | |
| is used. | |
| ``GIT_REPOSITORY <url>`` | |
| URL of the git repository. Any URL understood by the ``git`` command | |
| may be used. | |
| ``GIT_TAG <tag>`` | |
| Git branch name, tag or commit hash. Note that branch names and tags | |
| should generally be specified as remote names (i.e. ``origin/myBranch`` | |
| rather than simply ``myBranch``). This ensures that if the remote end | |
| has its tag moved or branch rebased or history rewritten, the local | |
| clone will still be updated correctly. In general, however, specifying | |
| a commit hash should be preferred for a number of reasons: | |
| - If the local clone already has the commit corresponding to the hash, | |
| no ``git fetch`` needs to be performed to check for changes each time | |
| CMake is re-run. This can result in a significant speed up if many | |
| external projects are being used. | |
| - Using a specific git hash ensures that the main project's own history | |
| is fully traceable to a specific point in the external project's | |
| evolution. If a branch or tag name is used instead, then checking out | |
| a specific commit of the main project doesn't necessarily pin the | |
| whole build to a specific point in the life of the external project. | |
| The lack of such deterministic behavior makes the main project lose | |
| traceability and repeatability. | |
| If ``GIT_SHALLOW`` is enabled then ``GIT_TAG`` works only with | |
| branch names and tags. A commit hash is not allowed. | |
| ``GIT_REMOTE_NAME <name>`` | |
| The optional name of the remote. If this option is not specified, it | |
| defaults to ``origin``. | |
| ``GIT_SUBMODULES <module>...`` | |
| Specific git submodules that should also be updated. If this option is | |
| not provided, all git submodules will be updated. | |
| .. versionchanged:: 3.16 | |
| When :policy:`CMP0097` is set to ``NEW``, if this value is set | |
| to an empty string then no submodules are initialized or updated. | |
| ``GIT_SUBMODULES_RECURSE <bool>`` | |
| .. versionadded:: 3.17 | |
| Specify whether git submodules (if any) should update recursively by | |
| passing the ``--recursive`` flag to ``git submodule update``. | |
| If not specified, the default is on. | |
| ``GIT_SHALLOW <bool>`` | |
| .. versionadded:: 3.6 | |
| When this option is enabled, the ``git clone`` operation will be given | |
| the ``--depth 1`` option. This performs a shallow clone, which avoids | |
| downloading the whole history and instead retrieves just the commit | |
| denoted by the ``GIT_TAG`` option. | |
| ``GIT_PROGRESS <bool>`` | |
| .. versionadded:: 3.8 | |
| When enabled, this option instructs the ``git clone`` operation to | |
| report its progress by passing it the ``--progress`` option. Without | |
| this option, the clone step for large projects may appear to make the | |
| build stall, since nothing will be logged until the clone operation | |
| finishes. While this option can be used to provide progress to prevent | |
| the appearance of the build having stalled, it may also make the build | |
| overly noisy if lots of external projects are used. | |
| ``GIT_CONFIG <option1> [<option2>...]`` | |
| .. versionadded:: 3.8 | |
| Specify a list of config options to pass to ``git clone``. Each option | |
| listed will be transformed into its own ``--config <option>`` on the | |
| ``git clone`` command line, with each option required to be in the | |
| form ``key=value``. | |
| ``GIT_REMOTE_UPDATE_STRATEGY <strategy>`` | |
| .. versionadded:: 3.18 | |
| When ``GIT_TAG`` refers to a remote branch, this option can be used to | |
| specify how the update step behaves. The ``<strategy>`` must be one of | |
| the following: | |
| ``CHECKOUT`` | |
| Ignore the local branch and always checkout the branch specified by | |
| ``GIT_TAG``. | |
| ``REBASE`` | |
| Try to rebase the current branch to the one specified by ``GIT_TAG``. | |
| If there are local uncommitted changes, they will be stashed first | |
| and popped again after rebasing. If rebasing or popping stashed | |
| changes fail, abort the rebase and halt with an error. | |
| When ``GIT_REMOTE_UPDATE_STRATEGY`` is not present, this is the | |
| default strategy unless the default has been overridden with | |
| ``CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY`` (see below). | |
| ``REBASE_CHECKOUT`` | |
| Same as ``REBASE`` except if the rebase fails, an annotated tag will | |
| be created at the original ``HEAD`` position from before the rebase | |
| and then checkout ``GIT_TAG`` just like the ``CHECKOUT`` strategy. | |
| The message stored on the annotated tag will give information about | |
| what was attempted and the tag name will include a timestamp so that | |
| each failed run will add a new tag. This strategy ensures no changes | |
| will be lost, but updates should always succeed if ``GIT_TAG`` refers | |
| to a valid ref unless there are uncommitted changes that cannot be | |
| popped successfully. | |
| The variable ``CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY`` can be set to | |
| override the default strategy. This variable should not be set by a | |
| project, it is intended for the user to set. It is primarily intended | |
| for use in continuous integration scripts to ensure that when history | |
| is rewritten on a remote branch, the build doesn't end up with unintended | |
| changes or failed builds resulting from conflicts during rebase operations. | |
| *Subversion* | |
| ``SVN_REPOSITORY <url>`` | |
| URL of the Subversion repository. | |
| ``SVN_REVISION -r<rev>`` | |
| Revision to checkout from the Subversion repository. | |
| ``SVN_USERNAME <username>`` | |
| Username for the Subversion checkout and update. | |
| ``SVN_PASSWORD <password>`` | |
| Password for the Subversion checkout and update. | |
| ``SVN_TRUST_CERT <bool>`` | |
| Specifies whether to trust the Subversion server site certificate. If | |
| enabled, the ``--trust-server-cert`` option is passed to the ``svn`` | |
| checkout and update commands. | |
| *Mercurial* | |
| ``HG_REPOSITORY <url>`` | |
| URL of the mercurial repository. | |
| ``HG_TAG <tag>`` | |
| Mercurial branch name, tag or commit id. | |
| *CVS* | |
| ``CVS_REPOSITORY <cvsroot>`` | |
| CVSROOT of the CVS repository. | |
| ``CVS_MODULE <mod>`` | |
| Module to checkout from the CVS repository. | |
| ``CVS_TAG <tag>`` | |
| Tag to checkout from the CVS repository. | |
| **Update Step Options:** | |
| Whenever CMake is re-run, by default the external project's sources will be | |
| updated if the download method supports updates (e.g. a git repository | |
| would be checked if the ``GIT_TAG`` does not refer to a specific commit). | |
| ``UPDATE_COMMAND <cmd>...`` | |
| Overrides the download method's update step with a custom command. | |
| The command may use | |
| :manual:`generator expressions <cmake-generator-expressions(7)>`. | |
| ``UPDATE_DISCONNECTED <bool>`` | |
| .. versionadded:: 3.2 | |
| When enabled, this option causes the update step to be skipped. It does | |
| not, however, prevent the download step. The update step can still be | |
| added as a step target (see :command:`ExternalProject_Add_StepTargets`) | |
| and called manually. This is useful if you want to allow developers to | |
| build the project when disconnected from the network (the network may | |
| still be needed for the download step though). | |
| When this option is present, it is generally advisable to make the value | |
| a cache variable under the developer's control rather than hard-coding | |
| it. If this option is not present, the default value is taken from the | |
| ``EP_UPDATE_DISCONNECTED`` directory property. If that is also not | |
| defined, updates are performed as normal. The ``EP_UPDATE_DISCONNECTED`` | |
| directory property is intended as a convenience for controlling the | |
| ``UPDATE_DISCONNECTED`` behavior for an entire section of a project's | |
| directory hierarchy and may be a more convenient method of giving | |
| developers control over whether or not to perform updates (assuming the | |
| project also provides a cache variable or some other convenient method | |
| for setting the directory property). | |
| This may cause a step target to be created automatically for the | |
| ``download`` step. See policy :policy:`CMP0114`. | |
| **Patch Step Options:** | |
| ``PATCH_COMMAND <cmd>...`` | |
| Specifies a custom command to patch the sources after an update. By | |
| default, no patch command is defined. Note that it can be quite difficult | |
| to define an appropriate patch command that performs robustly, especially | |
| for download methods such as git where changing the ``GIT_TAG`` will not | |
| discard changes from a previous patch, but the patch command will be | |
| called again after updating to the new tag. | |
| **Configure Step Options:** | |
| The configure step is run after the download and update steps. By default, | |
| the external project is assumed to be a CMake project, but this can be | |
| overridden if required. | |
| ``CONFIGURE_COMMAND <cmd>...`` | |
| The default configure command runs CMake with a few options based on | |
| the main project. The options added are typically only those needed to | |
| use the same generator as the main project, but the ``CMAKE_GENERATOR`` | |
| option can be given to override this. The project is responsible for | |
| adding any toolchain details, flags or other settings it wants to | |
| re-use from the main project or otherwise specify (see ``CMAKE_ARGS``, | |
| ``CMAKE_CACHE_ARGS`` and ``CMAKE_CACHE_DEFAULT_ARGS`` below). | |
| For non-CMake external projects, the ``CONFIGURE_COMMAND`` option must | |
| be used to override the default configure command | |
| (:manual:`generator expressions <cmake-generator-expressions(7)>` are | |
| supported). For projects that require no configure step, specify this | |
| option with an empty string as the command to execute. | |
| ``CMAKE_COMMAND /.../cmake`` | |
| Specify an alternative cmake executable for the configure step (use an | |
| absolute path). This is generally not recommended, since it is | |
| usually desirable to use the same CMake version throughout the whole | |
| build. This option is ignored if a custom configure command has been | |
| specified with ``CONFIGURE_COMMAND``. | |
| ``CMAKE_GENERATOR <gen>`` | |
| Override the CMake generator used for the configure step. Without this | |
| option, the same generator as the main build will be used. This option is | |
| ignored if a custom configure command has been specified with the | |
| ``CONFIGURE_COMMAND`` option. | |
| ``CMAKE_GENERATOR_PLATFORM <platform>`` | |
| .. versionadded:: 3.1 | |
| Pass a generator-specific platform name to the CMake command (see | |
| :variable:`CMAKE_GENERATOR_PLATFORM`). It is an error to provide this | |
| option without the ``CMAKE_GENERATOR`` option. | |
| ``CMAKE_GENERATOR_TOOLSET <toolset>`` | |
| Pass a generator-specific toolset name to the CMake command (see | |
| :variable:`CMAKE_GENERATOR_TOOLSET`). It is an error to provide this | |
| option without the ``CMAKE_GENERATOR`` option. | |
| ``CMAKE_GENERATOR_INSTANCE <instance>`` | |
| .. versionadded:: 3.11 | |
| Pass a generator-specific instance selection to the CMake command (see | |
| :variable:`CMAKE_GENERATOR_INSTANCE`). It is an error to provide this | |
| option without the ``CMAKE_GENERATOR`` option. | |
| ``CMAKE_ARGS <arg>...`` | |
| The specified arguments are passed to the ``cmake`` command line. They | |
| can be any argument the ``cmake`` command understands, not just cache | |
| values defined by ``-D...`` arguments (see also | |
| :manual:`CMake Options <cmake(1)>`). | |
| .. versionadded:: 3.3 | |
| Arguments may use :manual:`generator expressions <cmake-generator-expressions(7)>`. | |
| ``CMAKE_CACHE_ARGS <arg>...`` | |
| This is an alternate way of specifying cache variables where command line | |
| length issues may become a problem. The arguments are expected to be in | |
| the form ``-Dvar:STRING=value``, which are then transformed into | |
| CMake :command:`set` commands with the ``FORCE`` option used. These | |
| ``set()`` commands are written to a pre-load script which is then applied | |
| using the :manual:`cmake -C <cmake(1)>` command line option. | |
| .. versionadded:: 3.3 | |
| Arguments may use :manual:`generator expressions <cmake-generator-expressions(7)>`. | |
| ``CMAKE_CACHE_DEFAULT_ARGS <arg>...`` | |
| .. versionadded:: 3.2 | |
| This is the same as the ``CMAKE_CACHE_ARGS`` option except the ``set()`` | |
| commands do not include the ``FORCE`` keyword. This means the values act | |
| as initial defaults only and will not override any variables already set | |
| from a previous run. Use this option with care, as it can lead to | |
| different behavior depending on whether the build starts from a fresh | |
| build directory or re-uses previous build contents. | |
| .. versionadded:: 3.15 | |
| If the CMake generator is the ``Green Hills MULTI`` and not overridden then | |
| the original project's settings for the GHS toolset and target system | |
| customization cache variables are propagated into the external project. | |
| ``SOURCE_SUBDIR <dir>`` | |
| .. versionadded:: 3.7 | |
| When no ``CONFIGURE_COMMAND`` option is specified, the configure step | |
| assumes the external project has a ``CMakeLists.txt`` file at the top of | |
| its source tree (i.e. in ``SOURCE_DIR``). The ``SOURCE_SUBDIR`` option | |
| can be used to point to an alternative directory within the source tree | |
| to use as the top of the CMake source tree instead. This must be a | |
| relative path and it will be interpreted as being relative to | |
| ``SOURCE_DIR``. | |
| .. versionadded:: 3.14 | |
| 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 | |
| Makefile-based build and simply run ``make`` with no arguments as the | |
| default build step. This can be overridden with custom build commands if | |
| required. | |
| If both the main project and the external project use make as their build | |
| tool, the build step of the external project is invoked as a recursive | |
| make using ``$(MAKE)``. This will communicate some build tool settings | |
| from the main project to the external project. If either the main project | |
| or external project is not using make, no build tool settings will be | |
| passed to the external project other than those established by the | |
| configure step (i.e. running ``ninja -v`` in the main project will not | |
| pass ``-v`` to the external project's build step, even if it also uses | |
| ``ninja`` as its build tool). | |
| ``BUILD_COMMAND <cmd>...`` | |
| Overrides the default build command | |
| (:manual:`generator expressions <cmake-generator-expressions(7)>` are | |
| supported). If this option is not given, the default build command will | |
| be chosen to integrate with the main build in the most appropriate way | |
| (e.g. using recursive ``make`` for Makefile generators or | |
| ``cmake --build`` if the project uses a CMake build). This option can be | |
| specified with an empty string as the command to make the build step do | |
| nothing. | |
| ``BUILD_IN_SOURCE <bool>`` | |
| When this option is enabled, the build will be done directly within the | |
| external project's source tree. This should generally be avoided, the use | |
| of a separate build directory is usually preferred, but it can be useful | |
| when the external project assumes an in-source build. The ``BINARY_DIR`` | |
| option should not be specified if building in-source. | |
| ``BUILD_ALWAYS <bool>`` | |
| Enabling this option forces the build step to always be run. This can be | |
| the easiest way to robustly ensure that the external project's own build | |
| dependencies are evaluated rather than relying on the default | |
| success timestamp-based method. This option is not normally needed unless | |
| developers are expected to modify something the external project's build | |
| depends on in a way that is not detectable via the step target | |
| dependencies (e.g. ``SOURCE_DIR`` is used without a download method and | |
| developers might modify the sources in ``SOURCE_DIR``). | |
| ``BUILD_BYPRODUCTS <file>...`` | |
| .. versionadded:: 3.2 | |
| Specifies files that will be generated by the build command but which | |
| might or might not have their modification time updated by subsequent | |
| builds. These ultimately get passed through as ``BYPRODUCTS`` to the | |
| build step's own underlying call to :command:`add_custom_command`. | |
| **Install Step Options:** | |
| If the configure step assumed the external project uses CMake as its build | |
| system, the install step will also. Otherwise, the install step will assume | |
| a Makefile-based build and simply run ``make install`` as the default build | |
| step. This can be overridden with custom install commands if required. | |
| ``INSTALL_COMMAND <cmd>...`` | |
| The external project's own install step is invoked as part of the main | |
| project's *build*. It is done after the external project's build step | |
| and may be before or after the external project's test step (see the | |
| ``TEST_BEFORE_INSTALL`` option below). The external project's install | |
| rules are not part of the main project's install rules, so if anything | |
| from the external project should be installed as part of the main build, | |
| these need to be specified in the main build as additional | |
| :command:`install` commands. The default install step builds the | |
| ``install`` target of the external project, but this can be overridden | |
| with a custom command using this option | |
| (:manual:`generator expressions <cmake-generator-expressions(7)>` are | |
| supported). Passing an empty string as the ``<cmd>`` makes the install | |
| step do nothing. | |
| .. note:: | |
| If the :envvar:`CMAKE_INSTALL_MODE` environment variable is set when the | |
| main project is built, it will only have an effect if the following | |
| conditions are met: | |
| * The main project's configure step assumed the external project uses | |
| CMake as its build system. | |
| * The external project's install command actually runs. Note that due | |
| to the way ``ExternalProject`` may use timestamps internally, if | |
| nothing the install step depends on needs to be re-executed, the | |
| install command might also not need to run. | |
| Note also that ``ExternalProject`` does not check whether the | |
| :envvar:`CMAKE_INSTALL_MODE` environment variable changes from one run | |
| to another. | |
| **Test Step Options:** | |
| The test step is only defined if at least one of the following ``TEST_...`` | |
| options are provided. | |
| ``TEST_COMMAND <cmd>...`` | |
| Overrides the default test command | |
| (:manual:`generator expressions <cmake-generator-expressions(7)>` are | |
| supported). If this option is not given, the default behavior of the test | |
| step is to build the external project's own ``test`` target. This option | |
| can be specified with ``<cmd>`` as an empty string, which allows the test | |
| step to still be defined, but it will do nothing. Do not specify any of | |
| the other ``TEST_...`` options if providing an empty string as the test | |
| command, but prefer to omit all ``TEST_...`` options altogether if the | |
| test step target is not needed. | |
| ``TEST_BEFORE_INSTALL <bool>`` | |
| When this option is enabled, the test step will be executed before the | |
| install step. The default behavior is for the test step to run after the | |
| install step. | |
| ``TEST_AFTER_INSTALL <bool>`` | |
| This option is mainly useful as a way to indicate that the test step is | |
| desired but all default behavior is sufficient. Specifying this option | |
| with a boolean true value ensures the test step is defined and that it | |
| comes after the install step. If both ``TEST_BEFORE_INSTALL`` and | |
| ``TEST_AFTER_INSTALL`` are enabled, the latter is silently ignored. | |
| ``TEST_EXCLUDE_FROM_MAIN <bool>`` | |
| .. versionadded:: 3.2 | |
| If enabled, the main build's default ALL target will not depend on the | |
| test step. This can be a useful way of ensuring the test step is defined | |
| but only gets invoked when manually requested. | |
| This may cause a step target to be created automatically for either | |
| the ``install`` or ``build`` step. See policy :policy:`CMP0114`. | |
| **Output Logging Options:** | |
| Each of the following ``LOG_...`` options can be used to wrap the relevant | |
| step in a script to capture its output to files. The log files will be | |
| created in ``LOG_DIR`` if supplied or otherwise the ``STAMP_DIR`` | |
| directory with step-specific file names. | |
| ``LOG_DOWNLOAD <bool>`` | |
| When enabled, the output of the download step is logged to files. | |
| ``LOG_UPDATE <bool>`` | |
| When enabled, the output of the update step is logged to files. | |
| ``LOG_PATCH <bool>`` | |
| .. versionadded:: 3.14 | |
| When enabled, the output of the patch step is logged to files. | |
| ``LOG_CONFIGURE <bool>`` | |
| When enabled, the output of the configure step is logged to files. | |
| ``LOG_BUILD <bool>`` | |
| When enabled, the output of the build step is logged to files. | |
| ``LOG_INSTALL <bool>`` | |
| When enabled, the output of the install step is logged to files. | |
| ``LOG_TEST <bool>`` | |
| When enabled, the output of the test step is logged to files. | |
| ``LOG_MERGED_STDOUTERR <bool>`` | |
| .. versionadded:: 3.14 | |
| When enabled, stdout and stderr will be merged for any step whose | |
| output is being logged to files. | |
| ``LOG_OUTPUT_ON_FAILURE <bool>`` | |
| .. versionadded:: 3.14 | |
| This option only has an effect if at least one of the other ``LOG_<step>`` | |
| options is enabled. If an error occurs for a step which has logging to | |
| file enabled, that step's output will be printed to the console if | |
| ``LOG_OUTPUT_ON_FAILURE`` is set to true. For cases where a large amount | |
| of output is recorded, just the end of that output may be printed to the | |
| console. | |
| **Terminal Access Options:** | |
| .. versionadded:: 3.4 | |
| Steps can be given direct access to the terminal in some cases. Giving a | |
| step access to the terminal may allow it to receive terminal input if | |
| required, such as for authentication details not provided by other options. | |
| With the :generator:`Ninja` generator, these options place the steps in the | |
| ``console`` :prop_gbl:`job pool <JOB_POOLS>`. Each step can be given access | |
| to the terminal individually via the following options: | |
| ``USES_TERMINAL_DOWNLOAD <bool>`` | |
| Give the download step access to the terminal. | |
| ``USES_TERMINAL_UPDATE <bool>`` | |
| Give the update step access to the terminal. | |
| ``USES_TERMINAL_PATCH <bool>`` | |
| .. versionadded:: 3.23 | |
| Give the patch step access to the terminal. | |
| ``USES_TERMINAL_CONFIGURE <bool>`` | |
| Give the configure step access to the terminal. | |
| ``USES_TERMINAL_BUILD <bool>`` | |
| Give the build step access to the terminal. | |
| ``USES_TERMINAL_INSTALL <bool>`` | |
| Give the install step access to the terminal. | |
| ``USES_TERMINAL_TEST <bool>`` | |
| Give the test step access to the terminal. | |
| **Target Options:** | |
| ``DEPENDS <targets>...`` | |
| Specify other targets on which the external project depends. The other | |
| targets will be brought up to date before any of the external project's | |
| steps are executed. Because the external project uses additional custom | |
| targets internally for each step, the ``DEPENDS`` option is the most | |
| convenient way to ensure all of those steps depend on the other targets. | |
| Simply doing | |
| :command:`add_dependencies(\<name\> \<targets\>) <add_dependencies>` will | |
| not make any of the steps dependent on ``<targets>``. | |
| ``EXCLUDE_FROM_ALL <bool>`` | |
| When enabled, this option excludes the external project from the default | |
| ALL target of the main build. | |
| ``STEP_TARGETS <step-target>...`` | |
| Generate custom targets for the specified steps. This is required if the | |
| steps need to be triggered manually or if they need to be used as | |
| dependencies of other targets. If this option is not specified, the | |
| default value is taken from the ``EP_STEP_TARGETS`` directory property. | |
| See :command:`ExternalProject_Add_StepTargets` below for further | |
| discussion of the effects of this option. | |
| ``INDEPENDENT_STEP_TARGETS <step-target>...`` | |
| .. deprecated:: 3.19 | |
| This is allowed only if policy :policy:`CMP0114` is not set to ``NEW``. | |
| Generates custom targets for the specified steps and prevent these targets | |
| from having the usual dependencies applied to them. If this option is not | |
| specified, the default value is taken from the | |
| ``EP_INDEPENDENT_STEP_TARGETS`` directory property. This option is mostly | |
| useful for allowing individual steps to be driven independently, such as | |
| for a CDash setup where each step should be initiated and reported | |
| individually rather than as one whole build. See | |
| :command:`ExternalProject_Add_StepTargets` below for further discussion | |
| of the effects of this option. | |
| **Miscellaneous Options:** | |
| ``LIST_SEPARATOR <sep>`` | |
| For any of the various ``..._COMMAND`` options, and ``CMAKE_ARGS``, | |
| replace ``;`` with ``<sep>`` in the specified command lines. | |
| This can be useful where list variables may be given in commands where | |
| they should end up as space-separated arguments (``<sep>`` would be a | |
| single space character string in this case). | |
| ``COMMAND <cmd>...`` | |
| Any of the other ``..._COMMAND`` options can have additional commands | |
| appended to them by following them with as many ``COMMAND ...`` options | |
| as needed | |
| (:manual:`generator expressions <cmake-generator-expressions(7)>` are | |
| supported). For example: | |
| .. code-block:: cmake | |
| ExternalProject_Add(example | |
| ... # Download options, etc. | |
| BUILD_COMMAND ${CMAKE_COMMAND} -E echo "Starting $<CONFIG> build" | |
| COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --config $<CONFIG> | |
| COMMAND ${CMAKE_COMMAND} -E echo "$<CONFIG> build complete" | |
| ) | |
| It should also be noted that each build step is created via a call to | |
| :command:`ExternalProject_Add_Step`. See that command's documentation for the | |
| automatic substitutions that are supported for some options. | |
| Obtaining Project Properties | |
| """""""""""""""""""""""""""" | |
| .. command:: ExternalProject_Get_Property | |
| The ``ExternalProject_Get_Property()`` function retrieves external project | |
| target properties: | |
| .. code-block:: cmake | |
| ExternalProject_Get_Property(<name> <prop1> [<prop2>...]) | |
| The function stores property values in variables of the same name. Property | |
| names correspond to the keyword argument names of ``ExternalProject_Add()``. | |
| For example, the source directory might be retrieved like so: | |
| .. code-block:: cmake | |
| ExternalProject_Get_property(myExtProj SOURCE_DIR) | |
| message("Source dir of myExtProj = ${SOURCE_DIR}") | |
| Explicit Step Management | |
| """""""""""""""""""""""" | |
| The ``ExternalProject_Add()`` function on its own is often sufficient for | |
| incorporating an external project into the main build. Certain scenarios | |
| require additional work to implement desired behavior, such as adding in a | |
| custom step or making steps available as manually triggerable targets. The | |
| ``ExternalProject_Add_Step()``, ``ExternalProject_Add_StepTargets()`` and | |
| ``ExternalProject_Add_StepDependencies`` functions provide the lower level | |
| control needed to implement such step-level capabilities. | |
| .. command:: ExternalProject_Add_Step | |
| The ``ExternalProject_Add_Step()`` function specifies an additional custom | |
| step for an external project defined by an earlier call to | |
| :command:`ExternalProject_Add`: | |
| .. code-block:: cmake | |
| ExternalProject_Add_Step(<name> <step> [<option>...]) | |
| ``<name>`` is the same as the name passed to the original call to | |
| :command:`ExternalProject_Add`. The specified ``<step>`` must not be one of | |
| the pre-defined steps (``mkdir``, ``download``, ``update``, | |
| ``patch``, ``configure``, ``build``, ``install`` or ``test``). The supported | |
| options are: | |
| ``COMMAND <cmd>...`` | |
| The command line to be executed by this custom step | |
| (:manual:`generator expressions <cmake-generator-expressions(7)>` are | |
| supported). This option can be repeated multiple times to specify multiple | |
| commands to be executed in order. | |
| ``COMMENT "<text>..."`` | |
| Text to be printed when the custom step executes. | |
| ``DEPENDEES <step>...`` | |
| Other steps (custom or pre-defined) on which this step depends. | |
| ``DEPENDERS <step>...`` | |
| Other steps (custom or pre-defined) that depend on this new custom step. | |
| ``DEPENDS <file>...`` | |
| Files on which this custom step depends. | |
| ``INDEPENDENT <bool>`` | |
| .. versionadded:: 3.19 | |
| Specifies whether this step is independent of the external dependencies | |
| specified by the :command:`ExternalProject_Add`'s ``DEPENDS`` option. | |
| The default is ``FALSE``. Steps marked as independent may depend only | |
| on other steps marked independent. See policy :policy:`CMP0114`. | |
| Note that this use of the term "independent" refers only to independence | |
| from external targets specified by the ``DEPENDS`` option and is | |
| orthogonal to a step's dependencies on other steps. | |
| If a step target is created for an independent step by the | |
| :command:`ExternalProject_Add` ``STEP_TARGETS`` option or by the | |
| :command:`ExternalProject_Add_StepTargets` function, it will not depend | |
| on the external targets, but may depend on targets for other steps. | |
| ``BYPRODUCTS <file>...`` | |
| .. versionadded:: 3.2 | |
| Files that will be generated by this custom step but which might or might | |
| not have their modification time updated by subsequent builds. This list of | |
| files will ultimately be passed through as the ``BYPRODUCTS`` option to the | |
| :command:`add_custom_command` used to implement the custom step internally. | |
| ``ALWAYS <bool>`` | |
| When enabled, this option specifies that the custom step should always be | |
| run (i.e. that it is always considered out of date). | |
| ``EXCLUDE_FROM_MAIN <bool>`` | |
| When enabled, this option specifies that the external project's main target | |
| does not depend on the custom step. | |
| This may cause step targets to be created automatically for the steps on | |
| which this step depends. See policy :policy:`CMP0114`. | |
| ``WORKING_DIRECTORY <dir>`` | |
| Specifies the working directory to set before running the custom step's | |
| command. If this option is not specified, the directory will be the value | |
| of the :variable:`CMAKE_CURRENT_BINARY_DIR` at the point where | |
| ``ExternalProject_Add_Step()`` was called. | |
| ``LOG <bool>`` | |
| If set, this causes the output from the custom step to be captured to files | |
| in the external project's ``LOG_DIR`` if supplied or ``STAMP_DIR``. | |
| ``USES_TERMINAL <bool>`` | |
| If enabled, this gives the custom step direct access to the terminal if | |
| possible. | |
| The command line, comment, working directory and byproducts of every | |
| standard and custom step are processed to replace the tokens | |
| ``<SOURCE_DIR>``, ``<SOURCE_SUBDIR>``, ``<BINARY_DIR>``, ``<INSTALL_DIR>`` | |
| ``<TMP_DIR>``, ``<DOWNLOAD_DIR>`` and ``<DOWNLOADED_FILE>`` with their | |
| corresponding property values defined in the original call to | |
| :command:`ExternalProject_Add`. | |
| .. versionadded:: 3.3 | |
| Token replacement is extended to byproducts. | |
| .. versionadded:: 3.11 | |
| The ``<DOWNLOAD_DIR>`` substitution token. | |
| .. command:: ExternalProject_Add_StepTargets | |
| The ``ExternalProject_Add_StepTargets()`` function generates targets for the | |
| steps listed. The name of each created target will be of the form | |
| ``<name>-<step>``: | |
| .. code-block:: cmake | |
| ExternalProject_Add_StepTargets(<name> <step1> [<step2>...]) | |
| Creating a target for a step allows it to be used as a dependency of another | |
| target or to be triggered manually. Having targets for specific steps also | |
| allows them to be driven independently of each other by specifying targets on | |
| build command lines. For example, you may be submitting to a sub-project | |
| based dashboard where you want to drive the configure portion of the build, | |
| then submit to the dashboard, followed by the build portion, followed | |
| by tests. If you invoke a custom target that depends on a step halfway | |
| through the step dependency chain, then all the previous steps will also run | |
| to ensure everything is up to date. | |
| Internally, :command:`ExternalProject_Add` calls | |
| :command:`ExternalProject_Add_Step` to create each step. If any | |
| ``STEP_TARGETS`` were specified, then ``ExternalProject_Add_StepTargets()`` | |
| will also be called after :command:`ExternalProject_Add_Step`. Even if a | |
| step is not mentioned in the ``STEP_TARGETS`` option, | |
| ``ExternalProject_Add_StepTargets()`` can still be called later to manually | |
| define a target for the step. | |
| The ``STEP_TARGETS`` option for :command:`ExternalProject_Add` is generally | |
| the easiest way to ensure targets are created for specific steps of interest. | |
| For custom steps, ``ExternalProject_Add_StepTargets()`` must be called | |
| explicitly if a target should also be created for that custom step. | |
| An alternative to these two options is to populate the ``EP_STEP_TARGETS`` | |
| directory property. It acts as a default for the step target options and | |
| can save having to repeatedly specify the same set of step targets when | |
| multiple external projects are being defined. | |
| .. versionadded:: 3.19 | |
| If :policy:`CMP0114` is set to ``NEW``, step targets are fully responsible | |
| for holding the custom commands implementing their steps. The primary target | |
| created by ``ExternalProject_Add`` depends on the step targets, and the | |
| step targets depend on each other. The target-level dependencies match | |
| the file-level dependencies used by the custom commands for each step. | |
| The targets for steps created with :command:`ExternalProject_Add_Step`'s | |
| ``INDEPENDENT`` option do not depend on the external targets specified | |
| by :command:`ExternalProject_Add`'s ``DEPENDS`` option. The predefined | |
| steps ``mkdir``, ``download``, ``update``, and ``patch`` are independent. | |
| If :policy:`CMP0114` is not ``NEW``, the following deprecated behavior | |
| is available: | |
| * A deprecated ``NO_DEPENDS`` option may be specified immediately after the | |
| ``<name>`` and before the first step. | |
| If the ``NO_DEPENDS`` option is specified, the step target will not depend on | |
| the dependencies of the external project (i.e. on any dependencies of the | |
| ``<name>`` custom target created by :command:`ExternalProject_Add`). This is | |
| usually safe for the ``download``, ``update`` and ``patch`` steps, since they | |
| do not typically require that the dependencies are updated and built. Using | |
| ``NO_DEPENDS`` for any of the other pre-defined steps, however, may break | |
| parallel builds. Only use ``NO_DEPENDS`` where it is certain that the named | |
| steps genuinely do not have dependencies. For custom steps, consider whether | |
| or not the custom commands require the dependencies to be configured, built | |
| and installed. | |
| * The ``INDEPENDENT_STEP_TARGETS`` option for :command:`ExternalProject_Add`, | |
| or the ``EP_INDEPENDENT_STEP_TARGETS`` directory property, tells the | |
| function to call ``ExternalProject_Add_StepTargets()`` internally | |
| using the ``NO_DEPENDS`` option for the specified steps. | |
| .. command:: ExternalProject_Add_StepDependencies | |
| .. versionadded:: 3.2 | |
| The ``ExternalProject_Add_StepDependencies()`` function can be used to add | |
| dependencies to a step. The dependencies added must be targets CMake already | |
| knows about (these can be ordinary executable or library targets, custom | |
| targets or even step targets of another external project): | |
| .. code-block:: cmake | |
| ExternalProject_Add_StepDependencies(<name> <step> <target1> [<target2>...]) | |
| This function takes care to set both target and file level dependencies and | |
| will ensure that parallel builds will not break. It should be used instead of | |
| :command:`add_dependencies` whenever adding a dependency for some of the step | |
| targets generated by the ``ExternalProject`` module. | |
| Examples | |
| ^^^^^^^^ | |
| The following example shows how to download and build a hypothetical project | |
| called *FooBar* from github: | |
| .. code-block:: cmake | |
| include(ExternalProject) | |
| ExternalProject_Add(foobar | |
| GIT_REPOSITORY [email protected]:FooCo/FooBar.git | |
| GIT_TAG origin/release/1.2.3 | |
| ) | |
| For the sake of the example, also define a second hypothetical external project | |
| called *SecretSauce*, which is downloaded from a web server. Two URLs are given | |
| to take advantage of a faster internal network if available, with a fallback to | |
| a slower external server. The project is a typical ``Makefile`` project with no | |
| configure step, so some of the default commands are overridden. The build is | |
| only required to build the *sauce* target: | |
| .. code-block:: cmake | |
| find_program(MAKE_EXE NAMES gmake nmake make) | |
| ExternalProject_Add(secretsauce | |
| URL http://intranet.somecompany.com/artifacts/sauce-2.7.tgz | |
| https://www.somecompany.com/downloads/sauce-2.7.zip | |
| URL_HASH MD5=d41d8cd98f00b204e9800998ecf8427e | |
| CONFIGURE_COMMAND "" | |
| BUILD_COMMAND ${MAKE_EXE} sauce | |
| ) | |
| Suppose the build step of ``secretsauce`` requires that ``foobar`` must already | |
| be built. This could be enforced like so: | |
| .. code-block:: cmake | |
| ExternalProject_Add_StepDependencies(secretsauce build foobar) | |
| Another alternative would be to create a custom target for ``foobar``'s build | |
| step and make ``secretsauce`` depend on that rather than the whole ``foobar`` | |
| project. This would mean ``foobar`` only needs to be built, it doesn't need to | |
| run its install or test steps before ``secretsauce`` can be built. The | |
| dependency can also be defined along with the ``secretsauce`` project: | |
| .. code-block:: cmake | |
| ExternalProject_Add_StepTargets(foobar build) | |
| ExternalProject_Add(secretsauce | |
| URL http://intranet.somecompany.com/artifacts/sauce-2.7.tgz | |
| https://www.somecompany.com/downloads/sauce-2.7.zip | |
| URL_HASH MD5=d41d8cd98f00b204e9800998ecf8427e | |
| CONFIGURE_COMMAND "" | |
| BUILD_COMMAND ${MAKE_EXE} sauce | |
| DEPENDS foobar-build | |
| ) | |
| Instead of calling :command:`ExternalProject_Add_StepTargets`, the target could | |
| be defined along with the ``foobar`` project itself: | |
| .. code-block:: cmake | |
| ExternalProject_Add(foobar | |
| GIT_REPOSITORY [email protected]:FooCo/FooBar.git | |
| GIT_TAG origin/release/1.2.3 | |
| STEP_TARGETS build | |
| ) | |
| If many external projects should have the same set of step targets, setting a | |
| directory property may be more convenient. The ``build`` step target could be | |
| created automatically by setting the ``EP_STEP_TARGETS`` directory property | |
| before creating the external projects with :command:`ExternalProject_Add`: | |
| .. code-block:: cmake | |
| set_property(DIRECTORY PROPERTY EP_STEP_TARGETS build) | |
| Lastly, suppose that ``secretsauce`` provides a script called ``makedoc`` which | |
| can be used to generate its own documentation. Further suppose that the script | |
| expects the output directory to be provided as the only parameter and that it | |
| should be run from the ``secretsauce`` source directory. A custom step and a | |
| custom target to trigger the script can be defined like so: | |
| .. code-block:: cmake | |
| ExternalProject_Add_Step(secretsauce docs | |
| COMMAND <SOURCE_DIR>/makedoc <BINARY_DIR> | |
| WORKING_DIRECTORY <SOURCE_DIR> | |
| COMMENT "Building secretsauce docs" | |
| ALWAYS TRUE | |
| EXCLUDE_FROM_MAIN TRUE | |
| ) | |
| ExternalProject_Add_StepTargets(secretsauce docs) | |
| The custom step could then be triggered from the main build like so:: | |
| cmake --build . --target secretsauce-docs | |
| #]=======================================================================] | |
| cmake_policy(PUSH) | |
| cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced | |
| cmake_policy(SET CMP0057 NEW) # if() supports IN_LIST | |
| macro(_ep_get_hash_algos out_var) | |
| set(${out_var} | |
| MD5 | |
| SHA1 | |
| SHA224 | |
| SHA256 | |
| SHA384 | |
| SHA512 | |
| SHA3_224 | |
| SHA3_256 | |
| SHA3_384 | |
| SHA3_512 | |
| ) | |
| endmacro() | |
| macro(_ep_get_hash_regex out_var) | |
| _ep_get_hash_algos(${out_var}) | |
| list(JOIN ${out_var} "|" ${out_var}) | |
| set(${out_var} "^(${${out_var}})=([0-9A-Fa-f]+)$") | |
| endmacro() | |
| function(_ep_parse_arguments f keywords name ns args) | |
| # Transfer the arguments to this function into target properties for the | |
| # new custom target we just added so that we can set up all the build steps | |
| # correctly based on target properties. | |
| # | |
| # Because some keywords can be repeated, we can't use cmake_parse_arguments(). | |
| # Instead, we loop through ARGN and consider the namespace starting with an | |
| # upper-case letter followed by at least two more upper-case letters, | |
| # numbers or underscores to be keywords. | |
| set(key) | |
| foreach(arg IN LISTS args) | |
| set(is_value 1) | |
| if(arg MATCHES "^[A-Z][A-Z0-9_][A-Z0-9_]+$" AND | |
| NOT (("x${arg}x" STREQUAL "x${key}x") AND ("x${key}x" STREQUAL "xCOMMANDx")) AND | |
| NOT arg MATCHES "^(TRUE|FALSE)$") | |
| if(arg IN_LIST keywords) | |
| set(is_value 0) | |
| endif() | |
| endif() | |
| if(is_value) | |
| if(key) | |
| # Value | |
| if(NOT arg STREQUAL "") | |
| set_property(TARGET ${name} APPEND PROPERTY ${ns}${key} "${arg}") | |
| else() | |
| get_property(have_key TARGET ${name} PROPERTY ${ns}${key} SET) | |
| if(have_key) | |
| get_property(value TARGET ${name} PROPERTY ${ns}${key}) | |
| set_property(TARGET ${name} PROPERTY ${ns}${key} "${value};${arg}") | |
| else() | |
| set_property(TARGET ${name} PROPERTY ${ns}${key} "${arg}") | |
| endif() | |
| endif() | |
| else() | |
| # Missing Keyword | |
| message(AUTHOR_WARNING "value '${arg}' with no previous keyword in ${f}") | |
| endif() | |
| else() | |
| set(key "${arg}") | |
| endif() | |
| endforeach() | |
| endfunction() | |
| define_property(DIRECTORY PROPERTY "EP_BASE" INHERITED | |
| BRIEF_DOCS "Base directory for External Project storage." | |
| FULL_DOCS | |
| "See documentation of the ExternalProject_Add() function in the " | |
| "ExternalProject module." | |
| ) | |
| define_property(DIRECTORY PROPERTY "EP_PREFIX" INHERITED | |
| BRIEF_DOCS "Top prefix for External Project storage." | |
| FULL_DOCS | |
| "See documentation of the ExternalProject_Add() function in the " | |
| "ExternalProject module." | |
| ) | |
| define_property(DIRECTORY PROPERTY "EP_STEP_TARGETS" INHERITED | |
| BRIEF_DOCS | |
| "List of ExternalProject steps that automatically get corresponding targets" | |
| FULL_DOCS | |
| "These targets will be dependent on the main target dependencies. " | |
| "See documentation of the ExternalProject_Add_StepTargets() function in the " | |
| "ExternalProject module." | |
| ) | |
| define_property(DIRECTORY PROPERTY "EP_INDEPENDENT_STEP_TARGETS" INHERITED | |
| BRIEF_DOCS | |
| "List of ExternalProject steps that automatically get corresponding targets" | |
| FULL_DOCS | |
| "These targets will not be dependent on the main target dependencies. " | |
| "See documentation of the ExternalProject_Add_StepTargets() function in the " | |
| "ExternalProject module." | |
| ) | |
| define_property(DIRECTORY PROPERTY "EP_UPDATE_DISCONNECTED" INHERITED | |
| BRIEF_DOCS "Never update automatically from the remote repo." | |
| FULL_DOCS | |
| "See documentation of the ExternalProject_Add() function in the " | |
| "ExternalProject module." | |
| ) | |
| function(_ep_write_gitclone_script | |
| script_filename | |
| source_dir | |
| git_EXECUTABLE | |
| git_repository | |
| git_tag | |
| git_remote_name | |
| init_submodules | |
| git_submodules_recurse | |
| git_submodules | |
| git_shallow | |
| git_progress | |
| git_config | |
| src_name | |
| work_dir | |
| gitclone_infofile | |
| gitclone_stampfile | |
| tls_verify) | |
| if(NOT GIT_VERSION_STRING VERSION_LESS 1.8.5) | |
| # Use `git checkout <tree-ish> --` to avoid ambiguity with a local path. | |
| set(git_checkout_explicit-- "--") | |
| else() | |
| # Use `git checkout <branch>` even though this risks ambiguity with a | |
| # local path. Unfortunately we cannot use `git checkout <tree-ish> --` | |
| # because that will not search for remote branch names, a common use case. | |
| set(git_checkout_explicit-- "") | |
| endif() | |
| if("${git_tag}" STREQUAL "") | |
| message(FATAL_ERROR "Tag for git checkout should not be empty.") | |
| endif() | |
| if(GIT_VERSION_STRING VERSION_LESS 2.20 OR 2.21 VERSION_LESS_EQUAL GIT_VERSION_STRING) | |
| set(git_clone_options "--no-checkout") | |
| else() | |
| set(git_clone_options) | |
| endif() | |
| if(git_shallow) | |
| if(NOT GIT_VERSION_STRING VERSION_LESS 1.7.10) | |
| list(APPEND git_clone_options "--depth 1 --no-single-branch") | |
| else() | |
| list(APPEND git_clone_options "--depth 1") | |
| endif() | |
| endif() | |
| if(git_progress) | |
| list(APPEND git_clone_options --progress) | |
| endif() | |
| foreach(config IN LISTS git_config) | |
| list(APPEND git_clone_options --config \"${config}\") | |
| endforeach() | |
| if(NOT ${git_remote_name} STREQUAL "origin") | |
| list(APPEND git_clone_options --origin \"${git_remote_name}\") | |
| endif() | |
| string (REPLACE ";" " " git_clone_options "${git_clone_options}") | |
| set(git_options) | |
| # disable cert checking if explicitly told not to do it | |
| if(NOT "x${tls_verify}" STREQUAL "x" AND NOT tls_verify) | |
| set(git_options | |
| -c http.sslVerify=false) | |
| endif() | |
| string (REPLACE ";" " " git_options "${git_options}") | |
| configure_file( | |
| ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/gitclone.cmake.in | |
| ${script_filename} | |
| @ONLY | |
| ) | |
| endfunction() | |
| function(_ep_write_hgclone_script | |
| script_filename | |
| source_dir | |
| hg_EXECUTABLE | |
| hg_repository | |
| hg_tag | |
| src_name | |
| work_dir | |
| hgclone_infofile | |
| hgclone_stampfile) | |
| if("${hg_tag}" STREQUAL "") | |
| message(FATAL_ERROR "Tag for hg checkout should not be empty.") | |
| endif() | |
| configure_file( | |
| ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/hgclone.cmake.in | |
| ${script_filename} | |
| @ONLY | |
| ) | |
| endfunction() | |
| function(_ep_write_gitupdate_script | |
| script_filename | |
| git_EXECUTABLE | |
| git_tag | |
| git_remote_name | |
| init_submodules | |
| git_submodules_recurse | |
| git_submodules | |
| git_repository | |
| work_dir | |
| git_update_strategy) | |
| if("${git_tag}" STREQUAL "") | |
| message(FATAL_ERROR "Tag for git checkout should not be empty.") | |
| endif() | |
| set(git_stash_save_options --quiet) | |
| if(GIT_VERSION_STRING VERSION_GREATER_EQUAL 1.7.7) | |
| # This avoids stashing files covered by .gitignore | |
| list(APPEND git_stash_save_options --include-untracked) | |
| elseif(GIT_VERSION_STRING VERSION_GREATER_EQUAL 1.7.6) | |
| # Untracked files, but also ignored files, so potentially slower | |
| list(APPEND git_stash_save_options --all) | |
| endif() | |
| configure_file( | |
| "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/gitupdate.cmake.in" | |
| "${script_filename}" | |
| @ONLY | |
| ) | |
| endfunction() | |
| function(_ep_write_downloadfile_script | |
| script_filename | |
| REMOTE | |
| LOCAL | |
| timeout | |
| inactivity_timeout | |
| no_progress | |
| hash | |
| tls_verify | |
| tls_cainfo | |
| userpwd | |
| http_headers | |
| netrc | |
| netrc_file) | |
| if(timeout) | |
| set(TIMEOUT_ARGS TIMEOUT ${timeout}) | |
| set(TIMEOUT_MSG "${timeout} seconds") | |
| else() | |
| set(TIMEOUT_ARGS "# no TIMEOUT") | |
| set(TIMEOUT_MSG "none") | |
| endif() | |
| if(inactivity_timeout) | |
| set(INACTIVITY_TIMEOUT_ARGS INACTIVITY_TIMEOUT ${inactivity_timeout}) | |
| set(INACTIVITY_TIMEOUT_MSG "${inactivity_timeout} seconds") | |
| else() | |
| set(INACTIVITY_TIMEOUT_ARGS "# no INACTIVITY_TIMEOUT") | |
| set(INACTIVITY_TIMEOUT_MSG "none") | |
| endif() | |
| if(no_progress) | |
| set(SHOW_PROGRESS "") | |
| else() | |
| set(SHOW_PROGRESS "SHOW_PROGRESS") | |
| endif() | |
| _ep_get_hash_regex(_ep_hash_regex) | |
| if("${hash}" MATCHES "${_ep_hash_regex}") | |
| set(ALGO "${CMAKE_MATCH_1}") | |
| string(TOLOWER "${CMAKE_MATCH_2}" EXPECT_VALUE) | |
| else() | |
| set(ALGO "") | |
| set(EXPECT_VALUE "") | |
| endif() | |
| set(TLS_VERIFY_CODE "") | |
| set(TLS_CAINFO_CODE "") | |
| set(NETRC_CODE "") | |
| set(NETRC_FILE_CODE "") | |
| # check for curl globals in the project | |
| if(DEFINED CMAKE_TLS_VERIFY) | |
| set(TLS_VERIFY_CODE "set(CMAKE_TLS_VERIFY ${CMAKE_TLS_VERIFY})") | |
| endif() | |
| if(DEFINED CMAKE_TLS_CAINFO) | |
| set(TLS_CAINFO_CODE "set(CMAKE_TLS_CAINFO \"${CMAKE_TLS_CAINFO}\")") | |
| endif() | |
| if(DEFINED CMAKE_NETRC) | |
| set(NETRC_CODE "set(CMAKE_NETRC \"${CMAKE_NETRC}\")") | |
| endif() | |
| if(DEFINED CMAKE_NETRC_FILE) | |
| set(NETRC_FILE_CODE "set(CMAKE_NETRC_FILE \"${CMAKE_NETRC_FILE}\")") | |
| endif() | |
| # now check for curl locals so that the local values | |
| # will override the globals | |
| # check for tls_verify argument | |
| string(LENGTH "${tls_verify}" tls_verify_len) | |
| if(tls_verify_len GREATER 0) | |
| set(TLS_VERIFY_CODE "set(CMAKE_TLS_VERIFY ${tls_verify})") | |
| endif() | |
| # check for tls_cainfo argument | |
| string(LENGTH "${tls_cainfo}" tls_cainfo_len) | |
| if(tls_cainfo_len GREATER 0) | |
| set(TLS_CAINFO_CODE "set(CMAKE_TLS_CAINFO \"${tls_cainfo}\")") | |
| endif() | |
| # check for netrc argument | |
| string(LENGTH "${netrc}" netrc_len) | |
| if(netrc_len GREATER 0) | |
| set(NETRC_CODE "set(CMAKE_NETRC \"${netrc}\")") | |
| endif() | |
| # check for netrc_file argument | |
| string(LENGTH "${netrc_file}" netrc_file_len) | |
| if(netrc_file_len GREATER 0) | |
| set(NETRC_FILE_CODE "set(CMAKE_NETRC_FILE \"${netrc_file}\")") | |
| endif() | |
| if(userpwd STREQUAL ":") | |
| set(USERPWD_ARGS) | |
| else() | |
| set(USERPWD_ARGS USERPWD "${userpwd}") | |
| endif() | |
| set(HTTP_HEADERS_ARGS "") | |
| if(NOT http_headers STREQUAL "") | |
| foreach(header ${http_headers}) | |
| set( | |
| HTTP_HEADERS_ARGS | |
| "HTTPHEADER \"${header}\"\n ${HTTP_HEADERS_ARGS}" | |
| ) | |
| endforeach() | |
| endif() | |
| # Used variables: | |
| # * TLS_VERIFY_CODE | |
| # * TLS_CAINFO_CODE | |
| # * ALGO | |
| # * EXPECT_VALUE | |
| # * REMOTE | |
| # * LOCAL | |
| # * SHOW_PROGRESS | |
| # * TIMEOUT_ARGS | |
| # * TIMEOUT_MSG | |
| # * USERPWD_ARGS | |
| # * HTTP_HEADERS_ARGS | |
| configure_file( | |
| "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/download.cmake.in" | |
| "${script_filename}" | |
| @ONLY | |
| ) | |
| endfunction() | |
| function(_ep_write_verifyfile_script script_filename LOCAL hash) | |
| _ep_get_hash_regex(_ep_hash_regex) | |
| if("${hash}" MATCHES "${_ep_hash_regex}") | |
| set(ALGO "${CMAKE_MATCH_1}") | |
| string(TOLOWER "${CMAKE_MATCH_2}" EXPECT_VALUE) | |
| else() | |
| set(ALGO "") | |
| set(EXPECT_VALUE "") | |
| endif() | |
| # Used variables: | |
| # * ALGO | |
| # * EXPECT_VALUE | |
| # * LOCAL | |
| configure_file( | |
| "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/verify.cmake.in" | |
| "${script_filename}" | |
| @ONLY | |
| ) | |
| endfunction() | |
| function(_ep_write_extractfile_script script_filename name filename directory) | |
| set(args "") | |
| if(filename MATCHES "(\\.|=)(7z|tar\\.bz2|tar\\.gz|tar\\.xz|tbz2|tgz|txz|zip)$") | |
| set(args xfz) | |
| endif() | |
| if(filename MATCHES "(\\.|=)tar$") | |
| set(args xf) | |
| endif() | |
| if(args STREQUAL "") | |
| message(SEND_ERROR "error: do not know how to extract '${filename}' -- known types are .7z, .tar, .tar.bz2, .tar.gz, .tar.xz, .tbz2, .tgz, .txz and .zip") | |
| return() | |
| endif() | |
| configure_file( | |
| "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/extractfile.cmake.in" | |
| "${script_filename}" | |
| @ONLY | |
| ) | |
| endfunction() | |
| function(_ep_set_directories name) | |
| get_property(prefix TARGET ${name} PROPERTY _EP_PREFIX) | |
| if(NOT prefix) | |
| get_property(prefix DIRECTORY PROPERTY EP_PREFIX) | |
| if(NOT prefix) | |
| get_property(base DIRECTORY PROPERTY EP_BASE) | |
| if(NOT base) | |
| set(prefix "${name}-prefix") | |
| endif() | |
| endif() | |
| endif() | |
| if(prefix) | |
| file(TO_CMAKE_PATH "${prefix}" prefix) | |
| set(tmp_default "${prefix}/tmp") | |
| set(download_default "${prefix}/src") | |
| set(source_default "${prefix}/src/${name}") | |
| set(binary_default "${prefix}/src/${name}-build") | |
| set(stamp_default "${prefix}/src/${name}-stamp") | |
| set(install_default "${prefix}") | |
| else() | |
| file(TO_CMAKE_PATH "${base}" base) | |
| set(tmp_default "${base}/tmp/${name}") | |
| set(download_default "${base}/Download/${name}") | |
| set(source_default "${base}/Source/${name}") | |
| set(binary_default "${base}/Build/${name}") | |
| set(stamp_default "${base}/Stamp/${name}") | |
| set(install_default "${base}/Install/${name}") | |
| endif() | |
| get_property(build_in_source TARGET ${name} PROPERTY _EP_BUILD_IN_SOURCE) | |
| if(build_in_source) | |
| get_property(have_binary_dir TARGET ${name} PROPERTY _EP_BINARY_DIR SET) | |
| if(have_binary_dir) | |
| message(FATAL_ERROR | |
| "External project ${name} has both BINARY_DIR and BUILD_IN_SOURCE!") | |
| endif() | |
| endif() | |
| set(top "${CMAKE_CURRENT_BINARY_DIR}") | |
| # Apply defaults and convert to absolute paths. | |
| set(places stamp download source binary install tmp) | |
| foreach(var ${places}) | |
| string(TOUPPER "${var}" VAR) | |
| get_property(${var}_dir TARGET ${name} PROPERTY _EP_${VAR}_DIR) | |
| if(NOT ${var}_dir) | |
| set(${var}_dir "${${var}_default}") | |
| endif() | |
| if(NOT IS_ABSOLUTE "${${var}_dir}") | |
| get_filename_component(${var}_dir "${top}/${${var}_dir}" ABSOLUTE) | |
| endif() | |
| file(TO_CMAKE_PATH "${${var}_dir}" ${var}_dir) | |
| set_property(TARGET ${name} PROPERTY _EP_${VAR}_DIR "${${var}_dir}") | |
| endforeach() | |
| # Special case for default log directory based on stamp directory. | |
| get_property(log_dir TARGET ${name} PROPERTY _EP_LOG_DIR) | |
| if(NOT log_dir) | |
| get_property(log_dir TARGET ${name} PROPERTY _EP_STAMP_DIR) | |
| endif() | |
| if(NOT IS_ABSOLUTE "${log_dir}") | |
| get_filename_component(log_dir "${top}/${log_dir}" ABSOLUTE) | |
| endif() | |
| file(TO_CMAKE_PATH "${log_dir}" log_dir) | |
| set_property(TARGET ${name} PROPERTY _EP_LOG_DIR "${log_dir}") | |
| get_property(source_subdir TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR) | |
| if(NOT source_subdir) | |
| set_property(TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR "") | |
| elseif(IS_ABSOLUTE "${source_subdir}") | |
| message(FATAL_ERROR | |
| "External project ${name} has non-relative SOURCE_SUBDIR!") | |
| else() | |
| # Prefix with a slash so that when appended to the source directory, it | |
| # behaves as expected. | |
| file(TO_CMAKE_PATH "${source_subdir}" source_subdir) | |
| set_property(TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR "/${source_subdir}") | |
| endif() | |
| if(build_in_source) | |
| get_property(source_dir TARGET ${name} PROPERTY _EP_SOURCE_DIR) | |
| if(source_subdir) | |
| set_property(TARGET ${name} PROPERTY _EP_BINARY_DIR "${source_dir}/${source_subdir}") | |
| else() | |
| set_property(TARGET ${name} PROPERTY _EP_BINARY_DIR "${source_dir}") | |
| endif() | |
| endif() | |
| # This script will be used both here and by the mkdir step. We create the | |
| # directories now at configure time and ensure they exist again at build | |
| # time (since somebody might remove one of the required directories and try | |
| # to rebuild without re-running cmake). They need to exist now at makefile | |
| # generation time for Borland make and wmake so that CMake may generate | |
| # makefiles with "cd C:\short\paths\with\no\spaces" commands in them. | |
| set(script_filename "${tmp_dir}/${name}-mkdirs.cmake") | |
| configure_file( | |
| ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/mkdirs.cmake.in | |
| ${script_filename} | |
| @ONLY | |
| ) | |
| include(${script_filename}) | |
| endfunction() | |
| # IMPORTANT: this MUST be a macro and not a function because of the | |
| # in-place replacements that occur in each ${var} | |
| # | |
| macro(_ep_replace_location_tags target_name) | |
| set(vars ${ARGN}) | |
| foreach(var ${vars}) | |
| if(${var}) | |
| foreach(dir SOURCE_DIR SOURCE_SUBDIR BINARY_DIR INSTALL_DIR TMP_DIR DOWNLOAD_DIR DOWNLOADED_FILE LOG_DIR) | |
| get_property(val TARGET ${target_name} PROPERTY _EP_${dir}) | |
| string(REPLACE "<${dir}>" "${val}" ${var} "${${var}}") | |
| endforeach() | |
| endif() | |
| endforeach() | |
| endmacro() | |
| function(_ep_command_line_to_initial_cache var args force) | |
| set(script_initial_cache "") | |
| set(regex "^([^:]+):([^=]+)=(.*)$") | |
| set(setArg "") | |
| set(forceArg "") | |
| if(force) | |
| set(forceArg "FORCE") | |
| endif() | |
| foreach(line ${args}) | |
| if("${line}" MATCHES "^-D(.*)") | |
| set(line "${CMAKE_MATCH_1}") | |
| if(NOT "${setArg}" STREQUAL "") | |
| # This is required to build up lists in variables, or complete an entry | |
| string(APPEND setArg "${accumulator}\" CACHE ${type} \"Initial cache\" ${forceArg})") | |
| string(APPEND script_initial_cache "\n${setArg}") | |
| set(accumulator "") | |
| set(setArg "") | |
| endif() | |
| if("${line}" MATCHES "${regex}") | |
| set(name "${CMAKE_MATCH_1}") | |
| set(type "${CMAKE_MATCH_2}") | |
| set(value "${CMAKE_MATCH_3}") | |
| set(setArg "set(${name} \"${value}") | |
| else() | |
| message(WARNING "Line '${line}' does not match regex. Ignoring.") | |
| endif() | |
| else() | |
| # Assume this is a list to append to the last var | |
| string(APPEND accumulator ";${line}") | |
| endif() | |
| endforeach() | |
| # Catch the final line of the args | |
| if(NOT "${setArg}" STREQUAL "") | |
| string(APPEND setArg "${accumulator}\" CACHE ${type} \"Initial cache\" ${forceArg})") | |
| string(APPEND script_initial_cache "\n${setArg}") | |
| endif() | |
| set(${var} ${script_initial_cache} PARENT_SCOPE) | |
| endfunction() | |
| function(_ep_write_initial_cache target_name script_filename script_initial_cache) | |
| # Write out values into an initial cache, that will be passed to CMake with -C | |
| # Replace location tags. | |
| _ep_replace_location_tags(${target_name} script_initial_cache) | |
| _ep_replace_location_tags(${target_name} script_filename) | |
| # Replace list separators. | |
| get_property(sep TARGET ${target_name} PROPERTY _EP_LIST_SEPARATOR) | |
| if(sep AND script_initial_cache) | |
| string(REPLACE "${sep}" ";" script_initial_cache "${script_initial_cache}") | |
| endif() | |
| # Write out the initial cache file to the location specified. | |
| file(GENERATE OUTPUT "${script_filename}" CONTENT "${script_initial_cache}") | |
| endfunction() | |
| function(ExternalProject_Get_Property name) | |
| foreach(var ${ARGN}) | |
| string(TOUPPER "${var}" VAR) | |
| get_property(is_set TARGET ${name} PROPERTY _EP_${VAR} SET) | |
| if(NOT is_set) | |
| message(FATAL_ERROR "External project \"${name}\" has no ${var}") | |
| endif() | |
| get_property(${var} TARGET ${name} PROPERTY _EP_${VAR}) | |
| set(${var} "${${var}}" PARENT_SCOPE) | |
| endforeach() | |
| endfunction() | |
| function(_ep_get_configure_command_id name cfg_cmd_id_var) | |
| get_target_property(cmd ${name} _EP_CONFIGURE_COMMAND) | |
| if(cmd STREQUAL "") | |
| # Explicit empty string means no configure step for this project | |
| set(${cfg_cmd_id_var} "none" PARENT_SCOPE) | |
| else() | |
| if(NOT cmd) | |
| # Default is "use cmake": | |
| set(${cfg_cmd_id_var} "cmake" PARENT_SCOPE) | |
| else() | |
| # Otherwise we have to analyze the value: | |
| if(cmd MATCHES "^[^;]*/configure") | |
| set(${cfg_cmd_id_var} "configure" PARENT_SCOPE) | |
| elseif(cmd MATCHES "^[^;]*/cmake" AND NOT cmd MATCHES ";-[PE];") | |
| set(${cfg_cmd_id_var} "cmake" PARENT_SCOPE) | |
| elseif(cmd MATCHES "config") | |
| set(${cfg_cmd_id_var} "configure" PARENT_SCOPE) | |
| else() | |
| set(${cfg_cmd_id_var} "unknown:${cmd}" PARENT_SCOPE) | |
| endif() | |
| endif() | |
| endif() | |
| endfunction() | |
| function(_ep_get_build_command name step cmd_var) | |
| set(cmd "") | |
| set(args) | |
| _ep_get_configure_command_id(${name} cfg_cmd_id) | |
| if(cfg_cmd_id STREQUAL "cmake") | |
| # CMake project. Select build command based on generator. | |
| get_target_property(cmake_generator ${name} _EP_CMAKE_GENERATOR) | |
| if("${CMAKE_GENERATOR}" MATCHES "Make" AND | |
| ("${cmake_generator}" MATCHES "Make" OR NOT cmake_generator)) | |
| # The project uses the same Makefile generator. Use recursive make. | |
| set(cmd "$(MAKE)") | |
| if(step STREQUAL "INSTALL") | |
| set(args install) | |
| endif() | |
| if("x${step}x" STREQUAL "xTESTx") | |
| set(args test) | |
| endif() | |
| else() | |
| # Drive the project with "cmake --build". | |
| get_target_property(cmake_command ${name} _EP_CMAKE_COMMAND) | |
| if(cmake_command) | |
| set(cmd "${cmake_command}") | |
| else() | |
| set(cmd "${CMAKE_COMMAND}") | |
| endif() | |
| set(args --build ".") | |
| get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) | |
| if(_isMultiConfig) | |
| if (CMAKE_CFG_INTDIR AND | |
| NOT CMAKE_CFG_INTDIR STREQUAL "." AND | |
| NOT CMAKE_CFG_INTDIR MATCHES "\\$") | |
| # CMake 3.4 and below used the CMAKE_CFG_INTDIR placeholder value | |
| # provided by multi-configuration generators. Some projects were | |
| # taking advantage of that undocumented implementation detail to | |
| # specify a specific configuration here. They should use | |
| # BUILD_COMMAND to change the default command instead, but for | |
| # compatibility honor the value. | |
| set(config ${CMAKE_CFG_INTDIR}) | |
| message(AUTHOR_WARNING "CMAKE_CFG_INTDIR should not be set by project code.\n" | |
| "To get a non-default build command, use the BUILD_COMMAND option.") | |
| else() | |
| set(config $<CONFIG>) | |
| endif() | |
| list(APPEND args --config ${config}) | |
| endif() | |
| if(step STREQUAL "INSTALL") | |
| list(APPEND args --target install) | |
| endif() | |
| # But for "TEST" drive the project with corresponding "ctest". | |
| if("x${step}x" STREQUAL "xTESTx") | |
| string(REGEX REPLACE "^(.*/)cmake([^/]*)$" "\\1ctest\\2" cmd "${cmd}") | |
| set(args "") | |
| if(_isMultiConfig) | |
| list(APPEND args -C ${config}) | |
| endif() | |
| endif() | |
| endif() | |
| else() | |
| # Non-CMake project. Guess "make" and "make install" and "make test". | |
| if("${CMAKE_GENERATOR}" MATCHES "Makefiles") | |
| # Try to get the parallel arguments | |
| set(cmd "$(MAKE)") | |
| else() | |
| set(cmd "make") | |
| endif() | |
| if(step STREQUAL "INSTALL") | |
| set(args install) | |
| endif() | |
| if("x${step}x" STREQUAL "xTESTx") | |
| set(args test) | |
| endif() | |
| endif() | |
| # Use user-specified arguments instead of default arguments, if any. | |
| get_property(have_args TARGET ${name} PROPERTY _EP_${step}_ARGS SET) | |
| if(have_args) | |
| get_target_property(args ${name} _EP_${step}_ARGS) | |
| endif() | |
| if(NOT "${args}" STREQUAL "") | |
| # args could have empty items, so we must quote it to prevent them | |
| # from being silently removed | |
| list(APPEND cmd "${args}") | |
| endif() | |
| set(${cmd_var} "${cmd}" PARENT_SCOPE) | |
| endfunction() | |
| function(_ep_write_log_script name step cmd_var) | |
| ExternalProject_Get_Property(${name} log_dir) | |
| ExternalProject_Get_Property(${name} stamp_dir) | |
| set(command "${${cmd_var}}") | |
| set(make "") | |
| set(code_cygpath_make "") | |
| if(command MATCHES "^\\$\\(MAKE\\)") | |
| # GNU make recognizes the string "$(MAKE)" as recursive make, so | |
| # ensure that it appears directly in the makefile. | |
| string(REGEX REPLACE "^\\$\\(MAKE\\)" "\${make}" command "${command}") | |
| set(make "-Dmake=$(MAKE)") | |
| if(WIN32 AND NOT CYGWIN) | |
| set(code_cygpath_make " | |
| if(\${make} MATCHES \"^/\") | |
| execute_process( | |
| COMMAND cygpath -w \${make} | |
| OUTPUT_VARIABLE cygpath_make | |
| ERROR_VARIABLE cygpath_make | |
| RESULT_VARIABLE cygpath_error | |
| OUTPUT_STRIP_TRAILING_WHITESPACE | |
| ) | |
| if(NOT cygpath_error) | |
| set(make \${cygpath_make}) | |
| endif() | |
| endif() | |
| ") | |
| endif() | |
| endif() | |
| set(config "") | |
| if("${CMAKE_CFG_INTDIR}" MATCHES "^\\$") | |
| string(REPLACE "${CMAKE_CFG_INTDIR}" "\${config}" command "${command}") | |
| set(config "-Dconfig=${CMAKE_CFG_INTDIR}") | |
| endif() | |
| # Wrap multiple 'COMMAND' lines up into a second-level wrapper | |
| # script so all output can be sent to one log file. | |
| if(command MATCHES "(^|;)COMMAND;") | |
| set(code_execute_process " | |
| ${code_cygpath_make} | |
| execute_process(COMMAND \${command} RESULT_VARIABLE result) | |
| if(result) | |
| set(msg \"Command failed (\${result}):\\n\") | |
| foreach(arg IN LISTS command) | |
| set(msg \"\${msg} '\${arg}'\") | |
| endforeach() | |
| message(FATAL_ERROR \"\${msg}\") | |
| endif() | |
| ") | |
| set(code "") | |
| set(cmd "") | |
| set(sep "") | |
| foreach(arg IN LISTS command) | |
| if("x${arg}" STREQUAL "xCOMMAND") | |
| if(NOT "x${cmd}" STREQUAL "x") | |
| string(APPEND code "set(command \"${cmd}\")${code_execute_process}") | |
| endif() | |
| set(cmd "") | |
| set(sep "") | |
| else() | |
| string(APPEND cmd "${sep}${arg}") | |
| set(sep ";") | |
| endif() | |
| endforeach() | |
| string(APPEND code "set(command \"${cmd}\")${code_execute_process}") | |
| file(GENERATE OUTPUT "${stamp_dir}/${name}-${step}-$<CONFIG>-impl.cmake" CONTENT "${code}") | |
| set(command ${CMAKE_COMMAND} "-Dmake=\${make}" "-Dconfig=\${config}" -P ${stamp_dir}/${name}-${step}-$<CONFIG>-impl.cmake) | |
| endif() | |
| # Wrap the command in a script to log output to files. | |
| set(script ${stamp_dir}/${name}-${step}-$<CONFIG>.cmake) | |
| set(logbase ${log_dir}/${name}-${step}) | |
| get_property(log_merged TARGET ${name} PROPERTY _EP_LOG_MERGED_STDOUTERR) | |
| get_property(log_output_on_failure TARGET ${name} PROPERTY _EP_LOG_OUTPUT_ON_FAILURE) | |
| if (log_merged) | |
| set(stdout_log "${logbase}.log") | |
| set(stderr_log "${logbase}.log") | |
| else() | |
| set(stdout_log "${logbase}-out.log") | |
| set(stderr_log "${logbase}-err.log") | |
| endif() | |
| set(code " | |
| cmake_minimum_required(VERSION 3.15) | |
| ${code_cygpath_make} | |
| set(command \"${command}\") | |
| set(log_merged \"${log_merged}\") | |
| set(log_output_on_failure \"${log_output_on_failure}\") | |
| set(stdout_log \"${stdout_log}\") | |
| set(stderr_log \"${stderr_log}\") | |
| execute_process( | |
| COMMAND \${command} | |
| RESULT_VARIABLE result | |
| OUTPUT_FILE \"\${stdout_log}\" | |
| ERROR_FILE \"\${stderr_log}\" | |
| ) | |
| macro(read_up_to_max_size log_file output_var) | |
| file(SIZE \${log_file} determined_size) | |
| set(max_size 10240) | |
| if (determined_size GREATER max_size) | |
| math(EXPR seek_position \"\${determined_size} - \${max_size}\") | |
| file(READ \${log_file} \${output_var} OFFSET \${seek_position}) | |
| set(\${output_var} \"...skipping to end...\\n\${\${output_var}}\") | |
| else() | |
| file(READ \${log_file} \${output_var}) | |
| endif() | |
| endmacro() | |
| if(result) | |
| set(msg \"Command failed: \${result}\\n\") | |
| foreach(arg IN LISTS command) | |
| set(msg \"\${msg} '\${arg}'\") | |
| endforeach() | |
| if (\${log_merged}) | |
| set(msg \"\${msg}\\nSee also\\n \${stderr_log}\") | |
| else() | |
| set(msg \"\${msg}\\nSee also\\n ${logbase}-*.log\") | |
| endif() | |
| if (\${log_output_on_failure}) | |
| message(SEND_ERROR \"\${msg}\") | |
| if (\${log_merged}) | |
| read_up_to_max_size(\"\${stderr_log}\" error_log_contents) | |
| message(STATUS \"Log output is:\\n\${error_log_contents}\") | |
| else() | |
| read_up_to_max_size(\"\${stdout_log}\" out_log_contents) | |
| read_up_to_max_size(\"\${stderr_log}\" err_log_contents) | |
| message(STATUS \"stdout output is:\\n\${out_log_contents}\") | |
| message(STATUS \"stderr output is:\\n\${err_log_contents}\") | |
| endif() | |
| message(FATAL_ERROR \"Stopping after outputting logs.\") | |
| else() | |
| message(FATAL_ERROR \"\${msg}\") | |
| endif() | |
| else() | |
| if(NOT \"${CMAKE_GENERATOR}\" MATCHES \"Ninja\") | |
| set(msg \"${name} ${step} command succeeded. See also ${logbase}-*.log\") | |
| message(STATUS \"\${msg}\") | |
| endif() | |
| endif() | |
| ") | |
| file(GENERATE OUTPUT "${script}" CONTENT "${code}") | |
| set(command ${CMAKE_COMMAND} ${make} ${config} -P ${script}) | |
| set(${cmd_var} "${command}" PARENT_SCOPE) | |
| endfunction() | |
| # This module used to use "/${CMAKE_CFG_INTDIR}" directly and produced | |
| # makefiles with "/./" in paths for custom command dependencies. Which | |
| # resulted in problems with parallel make -j invocations. | |
| # | |
| # This function was added so that the suffix (search below for ${cfgdir}) is | |
| # only set to "/${CMAKE_CFG_INTDIR}" when ${CMAKE_CFG_INTDIR} is not going to | |
| # be "." (multi-configuration build systems like Visual Studio and Xcode...) | |
| # | |
| function(_ep_get_configuration_subdir_suffix suffix_var) | |
| set(suffix "") | |
| get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) | |
| if(_isMultiConfig) | |
| set(suffix "/${CMAKE_CFG_INTDIR}") | |
| endif() | |
| set(${suffix_var} "${suffix}" PARENT_SCOPE) | |
| endfunction() | |
| function(_ep_get_step_stampfile name step stampfile_var) | |
| ExternalProject_Get_Property(${name} stamp_dir) | |
| _ep_get_configuration_subdir_suffix(cfgdir) | |
| set(stampfile "${stamp_dir}${cfgdir}/${name}-${step}") | |
| set(${stampfile_var} "${stampfile}" PARENT_SCOPE) | |
| endfunction() | |
| function(_ep_get_complete_stampfile name stampfile_var) | |
| set(cmf_dir ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles) | |
| _ep_get_configuration_subdir_suffix(cfgdir) | |
| set(stampfile "${cmf_dir}${cfgdir}/${name}-complete") | |
| set(${stampfile_var} ${stampfile} PARENT_SCOPE) | |
| endfunction() | |
| function(_ep_step_add_target name step no_deps) | |
| if(TARGET ${name}-${step}) | |
| return() | |
| endif() | |
| get_property(cmp0114 TARGET ${name} PROPERTY _EP_CMP0114) | |
| _ep_get_step_stampfile(${name} ${step} stamp_file) | |
| cmake_policy(PUSH) | |
| if(cmp0114 STREQUAL "NEW") | |
| # To implement CMP0114 NEW behavior with Makefile generators, | |
| # we need CMP0113 NEW behavior. | |
| cmake_policy(SET CMP0113 NEW) | |
| endif() | |
| add_custom_target(${name}-${step} | |
| DEPENDS ${stamp_file}) | |
| cmake_policy(POP) | |
| set_property(TARGET ${name}-${step} PROPERTY _EP_IS_EXTERNAL_PROJECT_STEP 1) | |
| set_property(TARGET ${name}-${step} PROPERTY LABELS ${name}) | |
| set_property(TARGET ${name}-${step} PROPERTY FOLDER "ExternalProjectTargets/${name}") | |
| if(cmp0114 STREQUAL "NEW") | |
| # Add target-level dependencies for the step. | |
| get_property(exclude_from_main TARGET ${name} PROPERTY _EP_${step}_EXCLUDE_FROM_MAIN) | |
| if(NOT exclude_from_main) | |
| add_dependencies(${name} ${name}-${step}) | |
| endif() | |
| _ep_step_add_target_dependencies(${name} ${step} ${step}) | |
| _ep_step_add_target_dependents(${name} ${step} ${step}) | |
| get_property(independent TARGET ${name} PROPERTY _EP_${step}_INDEPENDENT) | |
| else() | |
| if(no_deps AND "${step}" MATCHES "^(configure|build|install|test)$") | |
| message(AUTHOR_WARNING "Using NO_DEPENDS for \"${step}\" step might break parallel builds") | |
| endif() | |
| set(independent ${no_deps}) | |
| endif() | |
| # Depend on other external projects (target-level). | |
| if(NOT independent) | |
| get_property(deps TARGET ${name} PROPERTY _EP_DEPENDS) | |
| foreach(arg IN LISTS deps) | |
| add_dependencies(${name}-${step} ${arg}) | |
| endforeach() | |
| endif() | |
| endfunction() | |
| function(_ep_step_add_target_dependencies name step node) | |
| get_property(dependees TARGET ${name} PROPERTY _EP_${node}_INTERNAL_DEPENDEES) | |
| list(REMOVE_DUPLICATES dependees) | |
| foreach(dependee IN LISTS dependees) | |
| get_property(exclude_from_main TARGET ${name} PROPERTY _EP_${step}_EXCLUDE_FROM_MAIN) | |
| get_property(dependee_dependers TARGET ${name} PROPERTY _EP_${dependee}_INTERNAL_DEPENDERS) | |
| if(exclude_from_main OR dependee_dependers MATCHES ";") | |
| # The step on which our step target depends itself has | |
| # dependents in multiple targes. It needs a step target too | |
| # so that there is a unique place for its custom command. | |
| _ep_step_add_target("${name}" "${dependee}" "FALSE") | |
| endif() | |
| if(TARGET ${name}-${dependee}) | |
| add_dependencies(${name}-${step} ${name}-${dependee}) | |
| else() | |
| _ep_step_add_target_dependencies(${name} ${step} ${dependee}) | |
| endif() | |
| endforeach() | |
| endfunction() | |
| function(_ep_step_add_target_dependents name step node) | |
| get_property(dependers TARGET ${name} PROPERTY _EP_${node}_INTERNAL_DEPENDERS) | |
| list(REMOVE_DUPLICATES dependers) | |
| foreach(depender IN LISTS dependers) | |
| if(TARGET ${name}-${depender}) | |
| add_dependencies(${name}-${depender} ${name}-${step}) | |
| else() | |
| _ep_step_add_target_dependents(${name} ${step} ${depender}) | |
| endif() | |
| endforeach() | |
| endfunction() | |
| function(ExternalProject_Add_StepTargets name) | |
| get_property(cmp0114 TARGET ${name} PROPERTY _EP_CMP0114) | |
| set(steps ${ARGN}) | |
| if(ARGC GREATER 1 AND "${ARGV1}" STREQUAL "NO_DEPENDS") | |
| set(no_deps 1) | |
| list(REMOVE_AT steps 0) | |
| else() | |
| set(no_deps 0) | |
| endif() | |
| if(cmp0114 STREQUAL "NEW") | |
| if(no_deps) | |
| message(FATAL_ERROR | |
| "The 'NO_DEPENDS' option is no longer allowed. " | |
| "It has been superseded by the per-step 'INDEPENDENT' option. " | |
| "See policy CMP0114." | |
| ) | |
| endif() | |
| elseif(cmp0114 STREQUAL "") | |
| cmake_policy(GET_WARNING CMP0114 _cmp0114_warning) | |
| string(APPEND _cmp0114_warning "\n" | |
| "ExternalProject target '${name}' would depend on the targets for " | |
| "step(s) '${steps}' under policy CMP0114, but this is being left out " | |
| "for compatibility since the policy is not set." | |
| ) | |
| if(no_deps) | |
| string(APPEND _cmp0114_warning | |
| " Also, the NO_DEPENDS option is deprecated in favor of policy CMP0114." | |
| ) | |
| endif() | |
| message(AUTHOR_WARNING "${_cmp0114_warning}") | |
| endif() | |
| foreach(step ${steps}) | |
| _ep_step_add_target("${name}" "${step}" "${no_deps}") | |
| endforeach() | |
| endfunction() | |
| function(ExternalProject_Add_Step name step) | |
| get_property(cmp0114 TARGET ${name} PROPERTY _EP_CMP0114) | |
| _ep_get_complete_stampfile(${name} complete_stamp_file) | |
| _ep_get_step_stampfile(${name} ${step} stamp_file) | |
| set(keywords | |
| COMMAND | |
| COMMENT | |
| DEPENDEES | |
| DEPENDERS | |
| DEPENDS | |
| INDEPENDENT | |
| BYPRODUCTS | |
| ALWAYS | |
| EXCLUDE_FROM_MAIN | |
| WORKING_DIRECTORY | |
| LOG | |
| USES_TERMINAL | |
| ) | |
| _ep_parse_arguments(ExternalProject_Add_Step "${keywords}" | |
| ${name} _EP_${step}_ "${ARGN}") | |
| get_property(independent TARGET ${name} PROPERTY _EP_${step}_INDEPENDENT) | |
| if(independent STREQUAL "") | |
| set(independent FALSE) | |
| set_property(TARGET ${name} PROPERTY _EP_${step}_INDEPENDENT "${independent}") | |
| endif() | |
| get_property(exclude_from_main TARGET ${name} PROPERTY _EP_${step}_EXCLUDE_FROM_MAIN) | |
| if(NOT exclude_from_main) | |
| add_custom_command(APPEND | |
| OUTPUT ${complete_stamp_file} | |
| DEPENDS ${stamp_file} | |
| ) | |
| endif() | |
| # Steps depending on this step. | |
| get_property(dependers TARGET ${name} PROPERTY _EP_${step}_DEPENDERS) | |
| set_property(TARGET ${name} APPEND PROPERTY _EP_${step}_INTERNAL_DEPENDERS ${dependers}) | |
| foreach(depender IN LISTS dependers) | |
| set_property(TARGET ${name} APPEND PROPERTY _EP_${depender}_INTERNAL_DEPENDEES ${step}) | |
| _ep_get_step_stampfile(${name} ${depender} depender_stamp_file) | |
| add_custom_command(APPEND | |
| OUTPUT ${depender_stamp_file} | |
| DEPENDS ${stamp_file} | |
| ) | |
| if(cmp0114 STREQUAL "NEW" AND NOT independent) | |
| get_property(dep_independent TARGET ${name} PROPERTY _EP_${depender}_INDEPENDENT) | |
| if(dep_independent) | |
| message(FATAL_ERROR "ExternalProject '${name}' step '${depender}' is marked INDEPENDENT " | |
| "but depends on step '${step}' that is not marked INDEPENDENT.") | |
| endif() | |
| endif() | |
| endforeach() | |
| # Dependencies on files. | |
| get_property(depends TARGET ${name} PROPERTY _EP_${step}_DEPENDS) | |
| # Byproducts of the step. | |
| get_property(byproducts TARGET ${name} PROPERTY _EP_${step}_BYPRODUCTS) | |
| # Dependencies on steps. | |
| get_property(dependees TARGET ${name} PROPERTY _EP_${step}_DEPENDEES) | |
| set_property(TARGET ${name} APPEND PROPERTY _EP_${step}_INTERNAL_DEPENDEES ${dependees}) | |
| foreach(dependee IN LISTS dependees) | |
| set_property(TARGET ${name} APPEND PROPERTY _EP_${dependee}_INTERNAL_DEPENDERS ${step}) | |
| _ep_get_step_stampfile(${name} ${dependee} dependee_stamp_file) | |
| list(APPEND depends ${dependee_stamp_file}) | |
| if(cmp0114 STREQUAL "NEW" AND independent) | |
| get_property(dep_independent TARGET ${name} PROPERTY _EP_${dependee}_INDEPENDENT) | |
| if(NOT dep_independent) | |
| message(FATAL_ERROR "ExternalProject '${name}' step '${step}' is marked INDEPENDENT " | |
| "but depends on step '${dependee}' that is not marked INDEPENDENT.") | |
| endif() | |
| endif() | |
| endforeach() | |
| # The command to run. | |
| get_property(command TARGET ${name} PROPERTY _EP_${step}_COMMAND) | |
| if(command) | |
| set(comment "Performing ${step} step for '${name}'") | |
| else() | |
| set(comment "No ${step} step for '${name}'") | |
| endif() | |
| get_property(work_dir TARGET ${name} PROPERTY _EP_${step}_WORKING_DIRECTORY) | |
| # Replace list separators. | |
| get_property(sep TARGET ${name} PROPERTY _EP_LIST_SEPARATOR) | |
| if(sep AND command) | |
| string(REPLACE "${sep}" "\\;" command "${command}") | |
| endif() | |
| # Replace location tags. | |
| _ep_replace_location_tags(${name} comment command work_dir byproducts) | |
| # Custom comment? | |
| get_property(comment_set TARGET ${name} PROPERTY _EP_${step}_COMMENT SET) | |
| if(comment_set) | |
| get_property(comment TARGET ${name} PROPERTY _EP_${step}_COMMENT) | |
| endif() | |
| # Uses terminal? | |
| get_property(uses_terminal TARGET ${name} PROPERTY _EP_${step}_USES_TERMINAL) | |
| if(uses_terminal) | |
| set(uses_terminal USES_TERMINAL) | |
| else() | |
| set(uses_terminal "") | |
| endif() | |
| # Run every time? | |
| get_property(always TARGET ${name} PROPERTY _EP_${step}_ALWAYS) | |
| if(always) | |
| set_property(SOURCE ${stamp_file} PROPERTY SYMBOLIC 1) | |
| set(touch) | |
| # Remove any existing stamp in case the option changed in an existing tree. | |
| get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) | |
| if(_isMultiConfig) | |
| foreach(cfg ${CMAKE_CONFIGURATION_TYPES}) | |
| string(REPLACE "/${CMAKE_CFG_INTDIR}" "/${cfg}" stamp_file_config "${stamp_file}") | |
| file(REMOVE ${stamp_file_config}) | |
| endforeach() | |
| else() | |
| file(REMOVE ${stamp_file}) | |
| endif() | |
| else() | |
| set(touch ${CMAKE_COMMAND} -E touch ${stamp_file}) | |
| endif() | |
| # Wrap with log script? | |
| get_property(log TARGET ${name} PROPERTY _EP_${step}_LOG) | |
| if(command AND log) | |
| _ep_write_log_script(${name} ${step} command) | |
| endif() | |
| if("${command}" STREQUAL "") | |
| # Some generators (i.e. Xcode) will not generate a file level target | |
| # if no command is set, and therefore the dependencies on this | |
| # target will be broken. | |
| # The empty command is replaced by an echo command here in order to | |
| # avoid this issue. | |
| set(command ${CMAKE_COMMAND} -E echo_append) | |
| endif() | |
| set(__cmdQuoted) | |
| foreach(__item IN LISTS command) | |
| string(APPEND __cmdQuoted " [==[${__item}]==]") | |
| endforeach() | |
| cmake_language(EVAL CODE " | |
| add_custom_command( | |
| OUTPUT \${stamp_file} | |
| BYPRODUCTS \${byproducts} | |
| COMMENT \${comment} | |
| COMMAND ${__cmdQuoted} | |
| COMMAND \${touch} | |
| DEPENDS \${depends} | |
| WORKING_DIRECTORY \${work_dir} | |
| VERBATIM | |
| ${uses_terminal} | |
| )" | |
| ) | |
| set_property(TARGET ${name} APPEND PROPERTY _EP_STEPS ${step}) | |
| # Add custom "step target"? | |
| get_property(step_targets TARGET ${name} PROPERTY _EP_STEP_TARGETS) | |
| if(NOT step_targets) | |
| get_property(step_targets DIRECTORY PROPERTY EP_STEP_TARGETS) | |
| endif() | |
| foreach(st ${step_targets}) | |
| if("${st}" STREQUAL "${step}") | |
| _ep_step_add_target("${name}" "${step}" "FALSE") | |
| break() | |
| endif() | |
| endforeach() | |
| get_property(independent_step_targets TARGET ${name} PROPERTY _EP_INDEPENDENT_STEP_TARGETS) | |
| if(NOT independent_step_targets) | |
| get_property(independent_step_targets DIRECTORY PROPERTY EP_INDEPENDENT_STEP_TARGETS) | |
| endif() | |
| if(cmp0114 STREQUAL "NEW") | |
| if(independent_step_targets) | |
| message(FATAL_ERROR | |
| "ExternalProject '${name}' option 'INDEPENDENT_STEP_TARGETS' is set to\n" | |
| " ${independent_step_targets}\n" | |
| "but the option is no longer allowed. " | |
| "It has been superseded by the per-step 'INDEPENDENT' option. " | |
| "See policy CMP0114." | |
| ) | |
| endif() | |
| else() | |
| if(independent_step_targets AND cmp0114 STREQUAL "") | |
| get_property(warned TARGET ${name} PROPERTY _EP_CMP0114_WARNED_INDEPENDENT_STEP_TARGETS) | |
| if(NOT warned) | |
| set_property(TARGET ${name} PROPERTY _EP_CMP0114_WARNED_INDEPENDENT_STEP_TARGETS 1) | |
| cmake_policy(GET_WARNING CMP0114 _cmp0114_warning) | |
| string(APPEND _cmp0114_warning "\n" | |
| "ExternalProject '${name}' option INDEPENDENT_STEP_TARGETS is set to\n" | |
| " ${independent_step_targets}\n" | |
| "but the option is deprecated in favor of policy CMP0114." | |
| ) | |
| message(AUTHOR_WARNING "${_cmp0114_warning}") | |
| endif() | |
| endif() | |
| foreach(st ${independent_step_targets}) | |
| if("${st}" STREQUAL "${step}") | |
| _ep_step_add_target("${name}" "${step}" "TRUE") | |
| break() | |
| endif() | |
| endforeach() | |
| endif() | |
| endfunction() | |
| function(ExternalProject_Add_StepDependencies name step) | |
| set(dependencies ${ARGN}) | |
| # Sanity checks on "name" and "step". | |
| if(NOT TARGET ${name}) | |
| message(FATAL_ERROR "Cannot find target \"${name}\". Perhaps it has not yet been created using ExternalProject_Add.") | |
| endif() | |
| get_property(type TARGET ${name} PROPERTY TYPE) | |
| if(NOT type STREQUAL "UTILITY") | |
| message(FATAL_ERROR "Target \"${name}\" was not generated by ExternalProject_Add.") | |
| endif() | |
| get_property(is_ep TARGET ${name} PROPERTY _EP_IS_EXTERNAL_PROJECT) | |
| if(NOT is_ep) | |
| message(FATAL_ERROR "Target \"${name}\" was not generated by ExternalProject_Add.") | |
| endif() | |
| get_property(steps TARGET ${name} PROPERTY _EP_STEPS) | |
| list(FIND steps ${step} is_step) | |
| if(is_step LESS 0) | |
| message(FATAL_ERROR "External project \"${name}\" does not have a step \"${step}\".") | |
| endif() | |
| if(TARGET ${name}-${step}) | |
| get_property(type TARGET ${name}-${step} PROPERTY TYPE) | |
| if(NOT type STREQUAL "UTILITY") | |
| message(FATAL_ERROR "Target \"${name}-${step}\" was not generated by ExternalProject_Add_StepTargets.") | |
| endif() | |
| get_property(is_ep_step TARGET ${name}-${step} PROPERTY _EP_IS_EXTERNAL_PROJECT_STEP) | |
| if(NOT is_ep_step) | |
| message(FATAL_ERROR "Target \"${name}-${step}\" was not generated by ExternalProject_Add_StepTargets.") | |
| endif() | |
| endif() | |
| # Always add file-level dependency, but add target-level dependency | |
| # only if the target exists for that step. | |
| _ep_get_step_stampfile(${name} ${step} stamp_file) | |
| foreach(dep ${dependencies}) | |
| add_custom_command(APPEND | |
| OUTPUT ${stamp_file} | |
| DEPENDS ${dep}) | |
| if(TARGET ${name}-${step}) | |
| foreach(dep ${dependencies}) | |
| add_dependencies(${name}-${step} ${dep}) | |
| endforeach() | |
| endif() | |
| endforeach() | |
| endfunction() | |
| function(_ep_add_mkdir_command name) | |
| ExternalProject_Get_Property(${name} tmp_dir) | |
| set(script_filename "${tmp_dir}/${name}-mkdirs.cmake") | |
| ExternalProject_Add_Step(${name} mkdir | |
| INDEPENDENT TRUE | |
| COMMENT "Creating directories for '${name}'" | |
| COMMAND ${CMAKE_COMMAND} -P ${script_filename} | |
| ) | |
| endfunction() | |
| function(_ep_is_dir_empty dir empty_var) | |
| file(GLOB gr "${dir}/*") | |
| if("${gr}" STREQUAL "") | |
| set(${empty_var} 1 PARENT_SCOPE) | |
| else() | |
| set(${empty_var} 0 PARENT_SCOPE) | |
| endif() | |
| endfunction() | |
| function(_ep_get_git_submodules_recurse git_submodules_recurse) | |
| # Checks for GIT_SUBMODULES_RECURSE property | |
| # Default is ON, which sets git_submodules_recurse output variable to "--recursive" | |
| # Otherwise, the output variable is set to an empty value "" | |
| get_property(git_submodules_recurse_set TARGET ${name} PROPERTY _EP_GIT_SUBMODULES_RECURSE SET) | |
| if(NOT git_submodules_recurse_set) | |
| set(recurseFlag "--recursive") | |
| else() | |
| get_property(git_submodules_recurse_value TARGET ${name} PROPERTY _EP_GIT_SUBMODULES_RECURSE) | |
| if(git_submodules_recurse_value) | |
| set(recurseFlag "--recursive") | |
| else() | |
| set(recurseFlag "") | |
| endif() | |
| endif() | |
| set(${git_submodules_recurse} "${recurseFlag}" PARENT_SCOPE) | |
| # The git submodule update '--recursive' flag requires git >= v1.6.5 | |
| if(recurseFlag AND GIT_VERSION_STRING VERSION_LESS 1.6.5) | |
| message(FATAL_ERROR "error: git version 1.6.5 or later required for --recursive flag with 'git submodule ...': GIT_VERSION_STRING='${GIT_VERSION_STRING}'") | |
| endif() | |
| endfunction() | |
| function(_ep_add_download_command name) | |
| ExternalProject_Get_Property(${name} source_dir stamp_dir download_dir tmp_dir) | |
| get_property(cmd_set TARGET ${name} PROPERTY _EP_DOWNLOAD_COMMAND SET) | |
| get_property(cmd TARGET ${name} PROPERTY _EP_DOWNLOAD_COMMAND) | |
| get_property(cvs_repository TARGET ${name} PROPERTY _EP_CVS_REPOSITORY) | |
| get_property(svn_repository TARGET ${name} PROPERTY _EP_SVN_REPOSITORY) | |
| get_property(git_repository TARGET ${name} PROPERTY _EP_GIT_REPOSITORY) | |
| get_property(hg_repository TARGET ${name} PROPERTY _EP_HG_REPOSITORY ) | |
| get_property(url TARGET ${name} PROPERTY _EP_URL) | |
| get_property(fname TARGET ${name} PROPERTY _EP_DOWNLOAD_NAME) | |
| # TODO: Perhaps file:// should be copied to download dir before extraction. | |
| string(REGEX REPLACE "file://" "" url "${url}") | |
| set(depends) | |
| set(comment) | |
| set(work_dir) | |
| set(extra_repo_info) | |
| if(cmd_set) | |
| set(work_dir ${download_dir}) | |
| set(method custom) | |
| elseif(cvs_repository) | |
| set(method cvs) | |
| find_package(CVS QUIET) | |
| if(NOT CVS_EXECUTABLE) | |
| message(FATAL_ERROR "error: could not find cvs for checkout of ${name}") | |
| endif() | |
| get_target_property(cvs_module ${name} _EP_CVS_MODULE) | |
| if(NOT cvs_module) | |
| message(FATAL_ERROR "error: no CVS_MODULE") | |
| endif() | |
| get_property(cvs_tag TARGET ${name} PROPERTY _EP_CVS_TAG) | |
| get_filename_component(src_name "${source_dir}" NAME) | |
| get_filename_component(work_dir "${source_dir}" PATH) | |
| set(comment "Performing download step (CVS checkout) for '${name}'") | |
| set(cmd ${CVS_EXECUTABLE} -d ${cvs_repository} -q co ${cvs_tag} -d ${src_name} ${cvs_module}) | |
| elseif(svn_repository) | |
| set(method svn) | |
| find_package(Subversion QUIET) | |
| if(NOT Subversion_SVN_EXECUTABLE) | |
| message(FATAL_ERROR "error: could not find svn for checkout of ${name}") | |
| endif() | |
| get_property(svn_revision TARGET ${name} PROPERTY _EP_SVN_REVISION) | |
| get_property(svn_username TARGET ${name} PROPERTY _EP_SVN_USERNAME) | |
| get_property(svn_password TARGET ${name} PROPERTY _EP_SVN_PASSWORD) | |
| get_property(svn_trust_cert TARGET ${name} PROPERTY _EP_SVN_TRUST_CERT) | |
| get_filename_component(src_name "${source_dir}" NAME) | |
| get_filename_component(work_dir "${source_dir}" PATH) | |
| set(comment "Performing download step (SVN checkout) for '${name}'") | |
| set(svn_user_pw_args "") | |
| if(DEFINED svn_username) | |
| set(svn_user_pw_args ${svn_user_pw_args} "--username=${svn_username}") | |
| endif() | |
| if(DEFINED svn_password) | |
| set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}") | |
| endif() | |
| if(svn_trust_cert) | |
| set(svn_trust_cert_args --trust-server-cert) | |
| endif() | |
| set(cmd ${Subversion_SVN_EXECUTABLE} co ${svn_repository} ${svn_revision} | |
| --non-interactive ${svn_trust_cert_args} ${svn_user_pw_args} ${src_name}) | |
| elseif(git_repository) | |
| set(method git) | |
| # FetchContent gives us these directly, so don't try to recompute them | |
| if(NOT GIT_EXECUTABLE OR NOT GIT_VERSION_STRING) | |
| unset(CMAKE_MODULE_PATH) # Use CMake builtin find module | |
| find_package(Git QUIET) | |
| if(NOT GIT_EXECUTABLE) | |
| message(FATAL_ERROR "error: could not find git for clone of ${name}") | |
| endif() | |
| endif() | |
| _ep_get_git_submodules_recurse(git_submodules_recurse) | |
| get_property(git_tag TARGET ${name} PROPERTY _EP_GIT_TAG) | |
| if(NOT git_tag) | |
| set(git_tag "master") | |
| endif() | |
| set(git_init_submodules TRUE) | |
| get_property(git_submodules_set TARGET ${name} PROPERTY _EP_GIT_SUBMODULES SET) | |
| if(git_submodules_set) | |
| get_property(git_submodules TARGET ${name} PROPERTY _EP_GIT_SUBMODULES) | |
| if(git_submodules STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW") | |
| set(git_init_submodules FALSE) | |
| endif() | |
| endif() | |
| get_property(git_remote_name TARGET ${name} PROPERTY _EP_GIT_REMOTE_NAME) | |
| if(NOT git_remote_name) | |
| set(git_remote_name "origin") | |
| endif() | |
| get_property(tls_verify TARGET ${name} PROPERTY _EP_TLS_VERIFY) | |
| if("x${tls_verify}" STREQUAL "x" AND DEFINED CMAKE_TLS_VERIFY) | |
| set(tls_verify "${CMAKE_TLS_VERIFY}") | |
| endif() | |
| get_property(git_shallow TARGET ${name} PROPERTY _EP_GIT_SHALLOW) | |
| get_property(git_progress TARGET ${name} PROPERTY _EP_GIT_PROGRESS) | |
| get_property(git_config TARGET ${name} PROPERTY _EP_GIT_CONFIG) | |
| # If git supports it, make checkouts quiet when checking out a git hash. | |
| # This avoids the very noisy detached head message. | |
| if(GIT_VERSION_STRING VERSION_GREATER_EQUAL 1.7.7) | |
| list(PREPEND git_config advice.detachedHead=false) | |
| endif() | |
| # The command doesn't expose any details, so we need to record additional | |
| # information in the RepositoryInfo.txt file. For the download step, only | |
| # the things specifically affecting the clone operation should be recorded. | |
| # If the repo changes, the clone script should be run again. | |
| # But if only the tag changes, avoid running the clone script again. | |
| # Let the 'always' running update step checkout the new tag. | |
| # | |
| set(extra_repo_info | |
| "repository=${git_repository} | |
| remote=${git_remote_name} | |
| init_submodules=${git_init_submodules} | |
| recurse_submodules=${git_submodules_recurse} | |
| submodules=${git_submodules} | |
| CMP0097=${_EP_CMP0097} | |
| ") | |
| get_filename_component(src_name "${source_dir}" NAME) | |
| get_filename_component(work_dir "${source_dir}" PATH) | |
| # Since git clone doesn't succeed if the non-empty source_dir exists, | |
| # create a cmake script to invoke as download command. | |
| # The script will delete the source directory and then call git clone. | |
| # | |
| _ep_write_gitclone_script(${tmp_dir}/${name}-gitclone.cmake ${source_dir} | |
| ${GIT_EXECUTABLE} ${git_repository} ${git_tag} ${git_remote_name} ${git_init_submodules} "${git_submodules_recurse}" "${git_submodules}" "${git_shallow}" "${git_progress}" "${git_config}" ${src_name} ${work_dir} | |
| ${stamp_dir}/${name}-gitinfo.txt ${stamp_dir}/${name}-gitclone-lastrun.txt "${tls_verify}" | |
| ) | |
| set(comment "Performing download step (git clone) for '${name}'") | |
| set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-gitclone.cmake) | |
| elseif(hg_repository) | |
| set(method hg) | |
| find_package(Hg QUIET) | |
| if(NOT HG_EXECUTABLE) | |
| message(FATAL_ERROR "error: could not find hg for clone of ${name}") | |
| endif() | |
| get_property(hg_tag TARGET ${name} PROPERTY _EP_HG_TAG) | |
| if(NOT hg_tag) | |
| set(hg_tag "tip") | |
| endif() | |
| # The command doesn't expose any details, so we need to record additional | |
| # information in the RepositoryInfo.txt file. For the download step, only | |
| # the things specifically affecting the clone operation should be recorded. | |
| # If the repo changes, the clone script should be run again. | |
| # But if only the tag changes, avoid running the clone script again. | |
| # Let the 'always' running update step checkout the new tag. | |
| # | |
| set(extra_repo_info "repository=${hg_repository}") | |
| get_filename_component(src_name "${source_dir}" NAME) | |
| get_filename_component(work_dir "${source_dir}" PATH) | |
| # Since hg clone doesn't succeed if the non-empty source_dir exists, | |
| # create a cmake script to invoke as download command. | |
| # The script will delete the source directory and then call hg clone. | |
| # | |
| _ep_write_hgclone_script(${tmp_dir}/${name}-hgclone.cmake ${source_dir} | |
| ${HG_EXECUTABLE} ${hg_repository} ${hg_tag} ${src_name} ${work_dir} | |
| ${stamp_dir}/${name}-hginfo.txt ${stamp_dir}/${name}-hgclone-lastrun.txt | |
| ) | |
| set(comment "Performing download step (hg clone) for '${name}'") | |
| set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-hgclone.cmake) | |
| elseif(url) | |
| set(method url) | |
| get_filename_component(work_dir "${source_dir}" PATH) | |
| get_property(hash TARGET ${name} PROPERTY _EP_URL_HASH) | |
| _ep_get_hash_regex(_ep_hash_regex) | |
| if(hash AND NOT "${hash}" MATCHES "${_ep_hash_regex}") | |
| _ep_get_hash_algos(_ep_hash_algos) | |
| list(JOIN _ep_hash_algos "|" _ep_hash_algos) | |
| message(FATAL_ERROR "URL_HASH is set to\n ${hash}\n" | |
| "but must be ALGO=value where ALGO is\n ${_ep_hash_algos}\n" | |
| "and value is a hex string.") | |
| endif() | |
| get_property(md5 TARGET ${name} PROPERTY _EP_URL_MD5) | |
| if(md5 AND NOT "MD5=${md5}" MATCHES "${_ep_hash_regex}") | |
| message(FATAL_ERROR "URL_MD5 is set to\n ${md5}\nbut must be a hex string.") | |
| endif() | |
| if(md5 AND NOT hash) | |
| set(hash "MD5=${md5}") | |
| endif() | |
| set(extra_repo_info | |
| "url(s)=${url} | |
| hash=${hash} | |
| ") | |
| list(LENGTH url url_list_length) | |
| if(NOT "${url_list_length}" STREQUAL "1") | |
| foreach(entry ${url}) | |
| if(NOT "${entry}" MATCHES "^[a-z]+://") | |
| message(FATAL_ERROR "At least one entry of URL is a path (invalid in a list)") | |
| endif() | |
| endforeach() | |
| if("x${fname}" STREQUAL "x") | |
| list(GET url 0 fname) | |
| endif() | |
| endif() | |
| if(IS_DIRECTORY "${url}") | |
| get_filename_component(abs_dir "${url}" ABSOLUTE) | |
| set(comment "Performing download step (DIR copy) for '${name}'") | |
| set(cmd ${CMAKE_COMMAND} -E rm -rf ${source_dir} | |
| COMMAND ${CMAKE_COMMAND} -E copy_directory ${abs_dir} ${source_dir}) | |
| else() | |
| get_property(no_extract TARGET "${name}" PROPERTY _EP_DOWNLOAD_NO_EXTRACT) | |
| string(APPEND extra_repo_info "no_extract=${no_extract}\n") | |
| if("${url}" MATCHES "^[a-z]+://") | |
| # TODO: Should download and extraction be different steps? | |
| if("x${fname}" STREQUAL "x") | |
| set(fname "${url}") | |
| endif() | |
| if("${fname}" MATCHES [[([^/\?#]+(\.|=)(7z|tar|tar\.bz2|tar\.gz|tar\.xz|tbz2|tgz|txz|zip))([/?#].*)?$]]) | |
| set(fname "${CMAKE_MATCH_1}") | |
| elseif(no_extract) | |
| get_filename_component(fname "${fname}" NAME) | |
| else() | |
| # Fall back to a default file name. The actual file name does not | |
| # matter because it is used only internally and our extraction tool | |
| # inspects the file content directly. If it turns out the wrong URL | |
| # was given that will be revealed during the build which is an easier | |
| # place for users to diagnose than an error here anyway. | |
| set(fname "archive.tar") | |
| endif() | |
| string(REPLACE ";" "-" fname "${fname}") | |
| set(file ${download_dir}/${fname}) | |
| get_property(timeout TARGET ${name} PROPERTY _EP_TIMEOUT) | |
| get_property(inactivity_timeout TARGET ${name} PROPERTY _EP_INACTIVITY_TIMEOUT) | |
| get_property(no_progress TARGET ${name} PROPERTY _EP_DOWNLOAD_NO_PROGRESS) | |
| get_property(tls_verify TARGET ${name} PROPERTY _EP_TLS_VERIFY) | |
| get_property(tls_cainfo TARGET ${name} PROPERTY _EP_TLS_CAINFO) | |
| get_property(netrc TARGET ${name} PROPERTY _EP_NETRC) | |
| get_property(netrc_file TARGET ${name} PROPERTY _EP_NETRC_FILE) | |
| get_property(http_username TARGET ${name} PROPERTY _EP_HTTP_USERNAME) | |
| get_property(http_password TARGET ${name} PROPERTY _EP_HTTP_PASSWORD) | |
| get_property(http_headers TARGET ${name} PROPERTY _EP_HTTP_HEADER) | |
| set(download_script "${stamp_dir}/download-${name}.cmake") | |
| _ep_write_downloadfile_script( | |
| "${download_script}" | |
| "${url}" | |
| "${file}" | |
| "${timeout}" | |
| "${inactivity_timeout}" | |
| "${no_progress}" | |
| "${hash}" | |
| "${tls_verify}" | |
| "${tls_cainfo}" | |
| "${http_username}:${http_password}" | |
| "${http_headers}" | |
| "${netrc}" | |
| "${netrc_file}" | |
| ) | |
| set(cmd ${CMAKE_COMMAND} -P "${download_script}" | |
| COMMAND) | |
| if (no_extract) | |
| set(steps "download and verify") | |
| else () | |
| set(steps "download, verify and extract") | |
| endif () | |
| set(comment "Performing download step (${steps}) for '${name}'") | |
| file(WRITE "${stamp_dir}/verify-${name}.cmake" "") # already verified by 'download_script' | |
| # Rather than adding everything to the RepositoryInfo.txt file, it is | |
| # more robust to just depend on the download script. That way, we will | |
| # re-download if any aspect of the download changes. | |
| list(APPEND depends "${download_script}") | |
| else() | |
| set(file "${url}") | |
| if (no_extract) | |
| set(steps "verify") | |
| else () | |
| set(steps "verify and extract") | |
| endif () | |
| set(comment "Performing download step (${steps}) for '${name}'") | |
| _ep_write_verifyfile_script( | |
| "${stamp_dir}/verify-${name}.cmake" | |
| "${file}" | |
| "${hash}" | |
| ) | |
| endif() | |
| list(APPEND cmd ${CMAKE_COMMAND} -P ${stamp_dir}/verify-${name}.cmake) | |
| if (NOT no_extract) | |
| _ep_write_extractfile_script( | |
| "${stamp_dir}/extract-${name}.cmake" | |
| "${name}" | |
| "${file}" | |
| "${source_dir}" | |
| ) | |
| list(APPEND cmd COMMAND ${CMAKE_COMMAND} -P ${stamp_dir}/extract-${name}.cmake) | |
| else () | |
| set_property(TARGET ${name} PROPERTY _EP_DOWNLOADED_FILE ${file}) | |
| endif () | |
| endif() | |
| else() | |
| set(method source_dir) | |
| _ep_is_dir_empty("${source_dir}" empty) | |
| if(${empty}) | |
| message(SEND_ERROR | |
| "No download info given for '${name}' and its source directory:\n" | |
| " ${source_dir}\n" | |
| "is not an existing non-empty directory. Please specify one of:\n" | |
| " * SOURCE_DIR with an existing non-empty directory\n" | |
| " * DOWNLOAD_COMMAND\n" | |
| " * URL\n" | |
| " * GIT_REPOSITORY\n" | |
| " * SVN_REPOSITORY\n" | |
| " * HG_REPOSITORY\n" | |
| " * CVS_REPOSITORY and CVS_MODULE" | |
| ) | |
| endif() | |
| endif() | |
| # We use configure_file() to write the repo_info_file so that the file's | |
| # timestamp is not updated if we don't change the contents | |
| set(repo_info_file ${stamp_dir}/${name}-${method}info.txt) | |
| list(APPEND depends ${repo_info_file}) | |
| configure_file( | |
| "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in" | |
| "${repo_info_file}" | |
| @ONLY | |
| ) | |
| get_property(log TARGET ${name} PROPERTY _EP_LOG_DOWNLOAD) | |
| if(log) | |
| set(log LOG 1) | |
| else() | |
| set(log "") | |
| endif() | |
| get_property(uses_terminal TARGET ${name} PROPERTY | |
| _EP_USES_TERMINAL_DOWNLOAD) | |
| if(uses_terminal) | |
| set(uses_terminal USES_TERMINAL 1) | |
| else() | |
| set(uses_terminal "") | |
| endif() | |
| set(__cmdQuoted) | |
| foreach(__item IN LISTS cmd) | |
| string(APPEND __cmdQuoted " [==[${__item}]==]") | |
| endforeach() | |
| cmake_language(EVAL CODE " | |
| ExternalProject_Add_Step(\${name} download | |
| INDEPENDENT TRUE | |
| COMMENT \${comment} | |
| COMMAND ${__cmdQuoted} | |
| WORKING_DIRECTORY \${work_dir} | |
| DEPENDS \${depends} | |
| DEPENDEES mkdir | |
| ${log} | |
| ${uses_terminal} | |
| )" | |
| ) | |
| endfunction() | |
| function(_ep_get_update_disconnected var name) | |
| get_property(update_disconnected_set TARGET ${name} PROPERTY _EP_UPDATE_DISCONNECTED SET) | |
| if(update_disconnected_set) | |
| get_property(update_disconnected TARGET ${name} PROPERTY _EP_UPDATE_DISCONNECTED) | |
| else() | |
| get_property(update_disconnected DIRECTORY PROPERTY EP_UPDATE_DISCONNECTED) | |
| endif() | |
| set(${var} "${update_disconnected}" PARENT_SCOPE) | |
| endfunction() | |
| function(_ep_add_update_command name) | |
| ExternalProject_Get_Property(${name} source_dir tmp_dir) | |
| get_property(cmd_set TARGET ${name} PROPERTY _EP_UPDATE_COMMAND SET) | |
| get_property(cmd TARGET ${name} PROPERTY _EP_UPDATE_COMMAND) | |
| get_property(cvs_repository TARGET ${name} PROPERTY _EP_CVS_REPOSITORY) | |
| get_property(svn_repository TARGET ${name} PROPERTY _EP_SVN_REPOSITORY) | |
| get_property(git_repository TARGET ${name} PROPERTY _EP_GIT_REPOSITORY) | |
| get_property(hg_repository TARGET ${name} PROPERTY _EP_HG_REPOSITORY ) | |
| _ep_get_update_disconnected(update_disconnected ${name}) | |
| set(work_dir) | |
| set(comment) | |
| set(always) | |
| if(cmd_set) | |
| set(work_dir ${source_dir}) | |
| if(NOT "x${cmd}" STREQUAL "x") | |
| set(always 1) | |
| endif() | |
| elseif(cvs_repository) | |
| if(NOT CVS_EXECUTABLE) | |
| message(FATAL_ERROR "error: could not find cvs for update of ${name}") | |
| endif() | |
| set(work_dir ${source_dir}) | |
| set(comment "Performing update step (CVS update) for '${name}'") | |
| get_property(cvs_tag TARGET ${name} PROPERTY _EP_CVS_TAG) | |
| set(cmd ${CVS_EXECUTABLE} -d ${cvs_repository} -q up -dP ${cvs_tag}) | |
| set(always 1) | |
| elseif(svn_repository) | |
| if(NOT Subversion_SVN_EXECUTABLE) | |
| message(FATAL_ERROR "error: could not find svn for update of ${name}") | |
| endif() | |
| set(work_dir ${source_dir}) | |
| set(comment "Performing update step (SVN update) for '${name}'") | |
| get_property(svn_revision TARGET ${name} PROPERTY _EP_SVN_REVISION) | |
| get_property(svn_username TARGET ${name} PROPERTY _EP_SVN_USERNAME) | |
| get_property(svn_password TARGET ${name} PROPERTY _EP_SVN_PASSWORD) | |
| get_property(svn_trust_cert TARGET ${name} PROPERTY _EP_SVN_TRUST_CERT) | |
| set(svn_user_pw_args "") | |
| if(DEFINED svn_username) | |
| set(svn_user_pw_args ${svn_user_pw_args} "--username=${svn_username}") | |
| endif() | |
| if(DEFINED svn_password) | |
| set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}") | |
| endif() | |
| if(svn_trust_cert) | |
| set(svn_trust_cert_args --trust-server-cert) | |
| endif() | |
| set(cmd ${Subversion_SVN_EXECUTABLE} up ${svn_revision} | |
| --non-interactive ${svn_trust_cert_args} ${svn_user_pw_args}) | |
| set(always 1) | |
| elseif(git_repository) | |
| # FetchContent gives us these directly, so don't try to recompute them | |
| if(NOT GIT_EXECUTABLE OR NOT GIT_VERSION_STRING) | |
| unset(CMAKE_MODULE_PATH) # Use CMake builtin find module | |
| find_package(Git QUIET) | |
| if(NOT GIT_EXECUTABLE) | |
| message(FATAL_ERROR "error: could not find git for fetch of ${name}") | |
| endif() | |
| endif() | |
| set(work_dir ${source_dir}) | |
| set(comment "Performing update step for '${name}'") | |
| get_property(git_tag TARGET ${name} PROPERTY _EP_GIT_TAG) | |
| if(NOT git_tag) | |
| set(git_tag "master") | |
| endif() | |
| get_property(git_remote_name TARGET ${name} PROPERTY _EP_GIT_REMOTE_NAME) | |
| if(NOT git_remote_name) | |
| set(git_remote_name "origin") | |
| endif() | |
| set(git_init_submodules TRUE) | |
| get_property(git_submodules_set TARGET ${name} PROPERTY _EP_GIT_SUBMODULES SET) | |
| if(git_submodules_set) | |
| get_property(git_submodules TARGET ${name} PROPERTY _EP_GIT_SUBMODULES) | |
| if(git_submodules STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW") | |
| set(git_init_submodules FALSE) | |
| endif() | |
| endif() | |
| get_property(git_update_strategy TARGET ${name} PROPERTY _EP_GIT_REMOTE_UPDATE_STRATEGY) | |
| if(NOT git_update_strategy) | |
| set(git_update_strategy "${CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY}") | |
| endif() | |
| if(NOT git_update_strategy) | |
| set(git_update_strategy REBASE) | |
| endif() | |
| set(strategies CHECKOUT REBASE REBASE_CHECKOUT) | |
| if(NOT git_update_strategy IN_LIST strategies) | |
| message(FATAL_ERROR "'${git_update_strategy}' is not one of the supported strategies: ${strategies}") | |
| endif() | |
| _ep_get_git_submodules_recurse(git_submodules_recurse) | |
| _ep_write_gitupdate_script( | |
| "${tmp_dir}/${name}-gitupdate.cmake" | |
| "${GIT_EXECUTABLE}" | |
| "${git_tag}" | |
| "${git_remote_name}" | |
| "${git_init_submodules}" | |
| "${git_submodules_recurse}" | |
| "${git_submodules}" | |
| "${git_repository}" | |
| "${work_dir}" | |
| "${git_update_strategy}" | |
| ) | |
| set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-gitupdate.cmake) | |
| set(always 1) | |
| elseif(hg_repository) | |
| if(NOT HG_EXECUTABLE) | |
| message(FATAL_ERROR "error: could not find hg for pull of ${name}") | |
| endif() | |
| set(work_dir ${source_dir}) | |
| set(comment "Performing update step (hg pull) for '${name}'") | |
| get_property(hg_tag TARGET ${name} PROPERTY _EP_HG_TAG) | |
| if(NOT hg_tag) | |
| set(hg_tag "tip") | |
| endif() | |
| if("${HG_VERSION_STRING}" STREQUAL "2.1") | |
| message(WARNING "Mercurial 2.1 does not distinguish an empty pull from a failed pull: | |
| http://mercurial.selenic.com/wiki/UpgradeNotes#A2.1.1:_revert_pull_return_code_change.2C_compile_issue_on_OS_X | |
| http://thread.gmane.org/gmane.comp.version-control.mercurial.devel/47656 | |
| Update to Mercurial >= 2.1.1. | |
| ") | |
| endif() | |
| set(cmd ${HG_EXECUTABLE} pull | |
| COMMAND ${HG_EXECUTABLE} update ${hg_tag} | |
| ) | |
| set(always 1) | |
| endif() | |
| get_property(log TARGET ${name} PROPERTY _EP_LOG_UPDATE) | |
| if(log) | |
| set(log LOG 1) | |
| else() | |
| set(log "") | |
| endif() | |
| get_property(uses_terminal TARGET ${name} PROPERTY | |
| _EP_USES_TERMINAL_UPDATE) | |
| if(uses_terminal) | |
| set(uses_terminal USES_TERMINAL 1) | |
| else() | |
| set(uses_terminal "") | |
| endif() | |
| set(__cmdQuoted) | |
| foreach(__item IN LISTS cmd) | |
| string(APPEND __cmdQuoted " [==[${__item}]==]") | |
| endforeach() | |
| cmake_language(EVAL CODE " | |
| ExternalProject_Add_Step(${name} update | |
| INDEPENDENT TRUE | |
| COMMENT \${comment} | |
| COMMAND ${__cmdQuoted} | |
| ALWAYS \${always} | |
| EXCLUDE_FROM_MAIN \${update_disconnected} | |
| WORKING_DIRECTORY \${work_dir} | |
| DEPENDEES download | |
| ${log} | |
| ${uses_terminal} | |
| )" | |
| ) | |
| endfunction() | |
| function(_ep_add_patch_command name) | |
| ExternalProject_Get_Property(${name} source_dir) | |
| get_property(cmd_set TARGET ${name} PROPERTY _EP_PATCH_COMMAND SET) | |
| get_property(cmd TARGET ${name} PROPERTY _EP_PATCH_COMMAND) | |
| set(work_dir) | |
| if(cmd_set) | |
| set(work_dir ${source_dir}) | |
| endif() | |
| get_property(log TARGET ${name} PROPERTY _EP_LOG_PATCH) | |
| if(log) | |
| set(log LOG 1) | |
| else() | |
| set(log "") | |
| endif() | |
| get_property(uses_terminal TARGET ${name} PROPERTY _EP_USES_TERMINAL_PATCH) | |
| if(uses_terminal) | |
| set(uses_terminal USES_TERMINAL 1) | |
| else() | |
| set(uses_terminal "") | |
| endif() | |
| _ep_get_update_disconnected(update_disconnected ${name}) | |
| if(update_disconnected) | |
| set(patch_dep download) | |
| else() | |
| set(patch_dep update) | |
| endif() | |
| set(__cmdQuoted) | |
| foreach(__item IN LISTS cmd) | |
| string(APPEND __cmdQuoted " [==[${__item}]==]") | |
| endforeach() | |
| cmake_language(EVAL CODE " | |
| ExternalProject_Add_Step(${name} patch | |
| INDEPENDENT TRUE | |
| COMMAND ${__cmdQuoted} | |
| WORKING_DIRECTORY \${work_dir} | |
| DEPENDEES \${patch_dep} | |
| ${log} | |
| ${uses_terminal} | |
| )" | |
| ) | |
| 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) | |
| if(cmd_set) | |
| get_property(cmd TARGET ${name} PROPERTY _EP_CONFIGURE_COMMAND) | |
| else() | |
| get_target_property(cmake_command ${name} _EP_CMAKE_COMMAND) | |
| if(cmake_command) | |
| set(cmd "${cmake_command}") | |
| else() | |
| set(cmd "${CMAKE_COMMAND}") | |
| endif() | |
| get_property(cmake_args TARGET ${name} PROPERTY _EP_CMAKE_ARGS) | |
| list(APPEND cmd ${cmake_args}) | |
| # If there are any CMAKE_CACHE_ARGS or CMAKE_CACHE_DEFAULT_ARGS, | |
| # write an initial cache and use it | |
| get_property(cmake_cache_args TARGET ${name} PROPERTY _EP_CMAKE_CACHE_ARGS) | |
| get_property(cmake_cache_default_args TARGET ${name} PROPERTY _EP_CMAKE_CACHE_DEFAULT_ARGS) | |
| set(has_cmake_cache_args 0) | |
| if(NOT "${cmake_cache_args}" STREQUAL "") | |
| set(has_cmake_cache_args 1) | |
| endif() | |
| set(has_cmake_cache_default_args 0) | |
| if(NOT "${cmake_cache_default_args}" STREQUAL "") | |
| set(has_cmake_cache_default_args 1) | |
| endif() | |
| get_target_property(cmake_generator ${name} _EP_CMAKE_GENERATOR) | |
| get_target_property(cmake_generator_instance ${name} _EP_CMAKE_GENERATOR_INSTANCE) | |
| get_target_property(cmake_generator_platform ${name} _EP_CMAKE_GENERATOR_PLATFORM) | |
| get_target_property(cmake_generator_toolset ${name} _EP_CMAKE_GENERATOR_TOOLSET) | |
| if(cmake_generator) | |
| list(APPEND cmd "-G${cmake_generator}") | |
| if(cmake_generator_platform) | |
| list(APPEND cmd "-A${cmake_generator_platform}") | |
| endif() | |
| if(cmake_generator_toolset) | |
| list(APPEND cmd "-T${cmake_generator_toolset}") | |
| endif() | |
| if(cmake_generator_instance) | |
| list(APPEND cmd "-DCMAKE_GENERATOR_INSTANCE:INTERNAL=${cmake_generator_instance}") | |
| endif() | |
| else() | |
| if(CMAKE_EXTRA_GENERATOR) | |
| list(APPEND cmd "-G${CMAKE_EXTRA_GENERATOR} - ${CMAKE_GENERATOR}") | |
| else() | |
| list(APPEND cmd "-G${CMAKE_GENERATOR}") | |
| if("${CMAKE_GENERATOR}" MATCHES "Green Hills MULTI") | |
| set(has_cmake_cache_default_args 1) | |
| set(cmake_cache_default_args ${cmake_cache_default_args} | |
| "-DGHS_TARGET_PLATFORM:STRING=${GHS_TARGET_PLATFORM}" | |
| "-DGHS_PRIMARY_TARGET:STRING=${GHS_PRIMARY_TARGET}" | |
| "-DGHS_TOOLSET_ROOT:STRING=${GHS_TOOLSET_ROOT}" | |
| "-DGHS_OS_ROOT:STRING=${GHS_OS_ROOT}" | |
| "-DGHS_OS_DIR:STRING=${GHS_OS_DIR}" | |
| "-DGHS_BSP_NAME:STRING=${GHS_BSP_NAME}") | |
| endif() | |
| endif() | |
| if(cmake_generator_platform) | |
| message(FATAL_ERROR "Option CMAKE_GENERATOR_PLATFORM not allowed without CMAKE_GENERATOR.") | |
| endif() | |
| if(CMAKE_GENERATOR_PLATFORM) | |
| list(APPEND cmd "-A${CMAKE_GENERATOR_PLATFORM}") | |
| endif() | |
| if(cmake_generator_toolset) | |
| message(FATAL_ERROR "Option CMAKE_GENERATOR_TOOLSET not allowed without CMAKE_GENERATOR.") | |
| endif() | |
| if(CMAKE_GENERATOR_TOOLSET) | |
| list(APPEND cmd "-T${CMAKE_GENERATOR_TOOLSET}") | |
| endif() | |
| if(cmake_generator_instance) | |
| message(FATAL_ERROR "Option CMAKE_GENERATOR_INSTANCE not allowed without CMAKE_GENERATOR.") | |
| endif() | |
| if(CMAKE_GENERATOR_INSTANCE) | |
| list(APPEND cmd "-DCMAKE_GENERATOR_INSTANCE:INTERNAL=${CMAKE_GENERATOR_INSTANCE}") | |
| endif() | |
| endif() | |
| if(has_cmake_cache_args OR has_cmake_cache_default_args) | |
| set(_ep_cache_args_script "<TMP_DIR>/${name}-cache-$<CONFIG>.cmake") | |
| if(has_cmake_cache_args) | |
| _ep_command_line_to_initial_cache(script_initial_cache_force "${cmake_cache_args}" 1) | |
| endif() | |
| if(has_cmake_cache_default_args) | |
| _ep_command_line_to_initial_cache(script_initial_cache_default "${cmake_cache_default_args}" 0) | |
| endif() | |
| _ep_write_initial_cache(${name} "${_ep_cache_args_script}" "${script_initial_cache_force}${script_initial_cache_default}") | |
| list(APPEND cmd "-C${_ep_cache_args_script}") | |
| _ep_replace_location_tags(${name} _ep_cache_args_script) | |
| set(_ep_cache_args_script | |
| "${_ep_cache_args_script}" | |
| PARENT_SCOPE) | |
| endif() | |
| list(APPEND cmd "<SOURCE_DIR><SOURCE_SUBDIR>") | |
| endif() | |
| set("${var}" "${cmd}" PARENT_SCOPE) | |
| endfunction() | |
| # TODO: Make sure external projects use the proper compiler | |
| function(_ep_add_configure_command name) | |
| ExternalProject_Get_Property(${name} binary_dir tmp_dir) | |
| set(file_deps) | |
| 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}) | |
| # If anything about the configure command changes, (command itself, cmake | |
| # used, cmake args or cmake generator) then re-run the configure step. | |
| # Fixes issue https://gitlab.kitware.com/cmake/cmake/-/issues/10258 | |
| # | |
| configure_file( | |
| ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/cfgcmd.txt.in | |
| ${tmp_dir}/${name}-cfgcmd.txt | |
| @ONLY | |
| ) | |
| list(APPEND file_deps ${tmp_dir}/${name}-cfgcmd.txt) | |
| list(APPEND file_deps ${_ep_cache_args_script}) | |
| get_property(log TARGET ${name} PROPERTY _EP_LOG_CONFIGURE) | |
| if(log) | |
| set(log LOG 1) | |
| else() | |
| set(log "") | |
| endif() | |
| get_property(uses_terminal TARGET ${name} PROPERTY | |
| _EP_USES_TERMINAL_CONFIGURE) | |
| if(uses_terminal) | |
| set(uses_terminal USES_TERMINAL 1) | |
| else() | |
| set(uses_terminal "") | |
| endif() | |
| set(__cmdQuoted) | |
| foreach(__item IN LISTS cmd) | |
| string(APPEND __cmdQuoted " [==[${__item}]==]") | |
| endforeach() | |
| cmake_language(EVAL CODE " | |
| ExternalProject_Add_Step(${name} configure | |
| INDEPENDENT FALSE | |
| COMMAND ${__cmdQuoted} | |
| WORKING_DIRECTORY \${binary_dir} | |
| DEPENDEES patch | |
| DEPENDS \${file_deps} | |
| ${log} | |
| ${uses_terminal} | |
| )" | |
| ) | |
| 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) | |
| else() | |
| _ep_get_build_command(${name} BUILD cmd) | |
| endif() | |
| get_property(log TARGET ${name} PROPERTY _EP_LOG_BUILD) | |
| if(log) | |
| set(log LOG 1) | |
| else() | |
| set(log "") | |
| endif() | |
| get_property(uses_terminal TARGET ${name} PROPERTY | |
| _EP_USES_TERMINAL_BUILD) | |
| if(uses_terminal) | |
| set(uses_terminal USES_TERMINAL 1) | |
| else() | |
| set(uses_terminal "") | |
| endif() | |
| get_property(build_always TARGET ${name} PROPERTY _EP_BUILD_ALWAYS) | |
| if(build_always) | |
| set(always 1) | |
| else() | |
| set(always 0) | |
| endif() | |
| get_property(build_byproducts TARGET ${name} PROPERTY _EP_BUILD_BYPRODUCTS) | |
| set(__cmdQuoted) | |
| foreach(__item IN LISTS cmd) | |
| string(APPEND __cmdQuoted " [==[${__item}]==]") | |
| endforeach() | |
| cmake_language(EVAL CODE " | |
| ExternalProject_Add_Step(${name} build | |
| INDEPENDENT FALSE | |
| COMMAND ${__cmdQuoted} | |
| BYPRODUCTS \${build_byproducts} | |
| WORKING_DIRECTORY \${binary_dir} | |
| DEPENDEES configure | |
| DEPENDS \${file_deps} | |
| ALWAYS \${always} | |
| ${log} | |
| ${uses_terminal} | |
| )" | |
| ) | |
| endfunction() | |
| function(_ep_add_install_command name) | |
| ExternalProject_Get_Property(${name} binary_dir) | |
| get_property(cmd_set TARGET ${name} PROPERTY _EP_INSTALL_COMMAND SET) | |
| if(cmd_set) | |
| get_property(cmd TARGET ${name} PROPERTY _EP_INSTALL_COMMAND) | |
| else() | |
| _ep_get_build_command(${name} INSTALL cmd) | |
| endif() | |
| get_property(log TARGET ${name} PROPERTY _EP_LOG_INSTALL) | |
| if(log) | |
| set(log LOG 1) | |
| else() | |
| set(log "") | |
| endif() | |
| get_property(uses_terminal TARGET ${name} PROPERTY | |
| _EP_USES_TERMINAL_INSTALL) | |
| if(uses_terminal) | |
| set(uses_terminal USES_TERMINAL 1) | |
| else() | |
| set(uses_terminal "") | |
| endif() | |
| set(__cmdQuoted) | |
| foreach(__item IN LISTS cmd) | |
| string(APPEND __cmdQuoted " [==[${__item}]==]") | |
| endforeach() | |
| cmake_language(EVAL CODE " | |
| ExternalProject_Add_Step(${name} install | |
| INDEPENDENT FALSE | |
| COMMAND ${__cmdQuoted} | |
| WORKING_DIRECTORY \${binary_dir} | |
| DEPENDEES build | |
| ${log} | |
| ${uses_terminal} | |
| )" | |
| ) | |
| endfunction() | |
| function(_ep_add_test_command name) | |
| ExternalProject_Get_Property(${name} binary_dir) | |
| get_property(before TARGET ${name} PROPERTY _EP_TEST_BEFORE_INSTALL) | |
| get_property(after TARGET ${name} PROPERTY _EP_TEST_AFTER_INSTALL) | |
| get_property(exclude TARGET ${name} PROPERTY _EP_TEST_EXCLUDE_FROM_MAIN) | |
| get_property(cmd_set TARGET ${name} PROPERTY _EP_TEST_COMMAND SET) | |
| # Only actually add the test step if one of the test related properties is | |
| # explicitly set. (i.e. the test step is omitted unless requested...) | |
| # | |
| if(cmd_set OR before OR after OR exclude) | |
| if(cmd_set) | |
| get_property(cmd TARGET ${name} PROPERTY _EP_TEST_COMMAND) | |
| else() | |
| _ep_get_build_command(${name} TEST cmd) | |
| endif() | |
| if(before) | |
| set(dependees_args DEPENDEES build) | |
| else() | |
| set(dependees_args DEPENDEES install) | |
| endif() | |
| if(exclude) | |
| set(dependers_args "") | |
| set(exclude_args EXCLUDE_FROM_MAIN 1) | |
| else() | |
| if(before) | |
| set(dependers_args DEPENDERS install) | |
| else() | |
| set(dependers_args "") | |
| endif() | |
| set(exclude_args "") | |
| endif() | |
| get_property(log TARGET ${name} PROPERTY _EP_LOG_TEST) | |
| if(log) | |
| set(log LOG 1) | |
| else() | |
| set(log "") | |
| endif() | |
| get_property(uses_terminal TARGET ${name} PROPERTY | |
| _EP_USES_TERMINAL_TEST) | |
| if(uses_terminal) | |
| set(uses_terminal USES_TERMINAL 1) | |
| else() | |
| set(uses_terminal "") | |
| endif() | |
| set(__cmdQuoted) | |
| foreach(__item IN LISTS cmd) | |
| string(APPEND __cmdQuoted " [==[${__item}]==]") | |
| endforeach() | |
| cmake_language(EVAL CODE " | |
| ExternalProject_Add_Step(${name} test | |
| INDEPENDENT FALSE | |
| COMMAND ${__cmdQuoted} | |
| WORKING_DIRECTORY \${binary_dir} | |
| ${dependees_args} | |
| ${dependers_args} | |
| ${exclude_args} | |
| ${log} | |
| ${uses_terminal} | |
| )" | |
| ) | |
| endif() | |
| endfunction() | |
| function(ExternalProject_Add name) | |
| cmake_policy(GET CMP0097 _EP_CMP0097 | |
| PARENT_SCOPE # undocumented, do not use outside of CMake | |
| ) | |
| cmake_policy(GET CMP0114 cmp0114 | |
| PARENT_SCOPE # undocumented, do not use outside of CMake | |
| ) | |
| if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12 AND NOT cmp0114 STREQUAL "NEW") | |
| message(AUTHOR_WARNING | |
| "Policy CMP0114 is not set to NEW. " | |
| "In order to support the Xcode \"new build system\", " | |
| "this project must be updated to set policy CMP0114 to NEW." | |
| "\n" | |
| "Since CMake is generating for the Xcode \"new build system\", " | |
| "ExternalProject_Add will use policy CMP0114's NEW behavior anyway, " | |
| "but the generated build system may not match what the project intends." | |
| ) | |
| set(cmp0114 "NEW") | |
| endif() | |
| _ep_get_configuration_subdir_suffix(cfgdir) | |
| # Add a custom target for the external project. | |
| set(cmf_dir ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles) | |
| _ep_get_complete_stampfile(${name} complete_stamp_file) | |
| cmake_policy(PUSH) | |
| if(cmp0114 STREQUAL "NEW") | |
| # To implement CMP0114 NEW behavior with Makefile generators, | |
| # we need CMP0113 NEW behavior. | |
| cmake_policy(SET CMP0113 NEW) | |
| endif() | |
| # The "ALL" option to add_custom_target just tells it to not set the | |
| # EXCLUDE_FROM_ALL target property. Later, if the EXCLUDE_FROM_ALL | |
| # argument was passed, we explicitly set it for the target. | |
| add_custom_target(${name} ALL DEPENDS ${complete_stamp_file}) | |
| cmake_policy(POP) | |
| set_property(TARGET ${name} PROPERTY _EP_IS_EXTERNAL_PROJECT 1) | |
| set_property(TARGET ${name} PROPERTY LABELS ${name}) | |
| set_property(TARGET ${name} PROPERTY FOLDER "ExternalProjectTargets/${name}") | |
| set_property(TARGET ${name} PROPERTY _EP_CMP0114 "${cmp0114}") | |
| set(keywords | |
| # | |
| # Directory options | |
| # | |
| PREFIX | |
| TMP_DIR | |
| STAMP_DIR | |
| LOG_DIR | |
| DOWNLOAD_DIR | |
| SOURCE_DIR | |
| BINARY_DIR | |
| INSTALL_DIR | |
| # | |
| # Download step options | |
| # | |
| DOWNLOAD_COMMAND | |
| # | |
| URL | |
| URL_HASH | |
| URL_MD5 | |
| DOWNLOAD_NAME | |
| DOWNLOAD_NO_EXTRACT | |
| DOWNLOAD_NO_PROGRESS | |
| TIMEOUT | |
| INACTIVITY_TIMEOUT | |
| HTTP_USERNAME | |
| HTTP_PASSWORD | |
| HTTP_HEADER | |
| TLS_VERIFY # Also used for git clone operations | |
| TLS_CAINFO | |
| NETRC | |
| NETRC_FILE | |
| # | |
| GIT_REPOSITORY | |
| GIT_TAG | |
| GIT_REMOTE_NAME | |
| GIT_SUBMODULES | |
| GIT_SUBMODULES_RECURSE | |
| GIT_SHALLOW | |
| GIT_PROGRESS | |
| GIT_CONFIG | |
| GIT_REMOTE_UPDATE_STRATEGY | |
| # | |
| SVN_REPOSITORY | |
| SVN_REVISION | |
| SVN_USERNAME | |
| SVN_PASSWORD | |
| SVN_TRUST_CERT | |
| # | |
| HG_REPOSITORY | |
| HG_TAG | |
| # | |
| CVS_REPOSITORY | |
| CVS_MODULE | |
| CVS_TAG | |
| # | |
| # Update step options | |
| # | |
| UPDATE_COMMAND | |
| UPDATE_DISCONNECTED | |
| # | |
| # Patch step options | |
| # | |
| PATCH_COMMAND | |
| # | |
| # Configure step options | |
| # | |
| CONFIGURE_COMMAND | |
| CMAKE_COMMAND | |
| CMAKE_GENERATOR | |
| CMAKE_GENERATOR_PLATFORM | |
| CMAKE_GENERATOR_TOOLSET | |
| CMAKE_GENERATOR_INSTANCE | |
| CMAKE_ARGS | |
| CMAKE_CACHE_ARGS | |
| CMAKE_CACHE_DEFAULT_ARGS | |
| SOURCE_SUBDIR | |
| CONFIGURE_HANDLED_BY_BUILD | |
| # | |
| # Build step options | |
| # | |
| BUILD_COMMAND | |
| BUILD_IN_SOURCE | |
| BUILD_ALWAYS | |
| BUILD_BYPRODUCTS | |
| # | |
| # Install step options | |
| # | |
| INSTALL_COMMAND | |
| # | |
| # Test step options | |
| # | |
| TEST_COMMAND | |
| TEST_BEFORE_INSTALL | |
| TEST_AFTER_INSTALL | |
| TEST_EXCLUDE_FROM_MAIN | |
| # | |
| # Logging options | |
| # | |
| LOG_DOWNLOAD | |
| LOG_UPDATE | |
| LOG_PATCH | |
| LOG_CONFIGURE | |
| LOG_BUILD | |
| LOG_INSTALL | |
| LOG_TEST | |
| LOG_MERGED_STDOUTERR | |
| LOG_OUTPUT_ON_FAILURE | |
| # | |
| # Terminal access options | |
| # | |
| USES_TERMINAL_DOWNLOAD | |
| USES_TERMINAL_UPDATE | |
| USES_TERMINAL_PATCH | |
| USES_TERMINAL_CONFIGURE | |
| USES_TERMINAL_BUILD | |
| USES_TERMINAL_INSTALL | |
| USES_TERMINAL_TEST | |
| # | |
| # Target options | |
| # | |
| DEPENDS | |
| EXCLUDE_FROM_ALL | |
| STEP_TARGETS | |
| INDEPENDENT_STEP_TARGETS | |
| # | |
| # Miscellaneous options | |
| # | |
| LIST_SEPARATOR | |
| ) | |
| _ep_parse_arguments(ExternalProject_Add "${keywords}" ${name} _EP_ "${ARGN}") | |
| _ep_set_directories(${name}) | |
| _ep_get_step_stampfile(${name} "done" done_stamp_file) | |
| _ep_get_step_stampfile(${name} "install" install_stamp_file) | |
| # Set the EXCLUDE_FROM_ALL target property if required. | |
| get_property(exclude_from_all TARGET ${name} PROPERTY _EP_EXCLUDE_FROM_ALL) | |
| if(exclude_from_all) | |
| set_property(TARGET ${name} PROPERTY EXCLUDE_FROM_ALL TRUE) | |
| endif() | |
| # The 'complete' step depends on all other steps and creates a | |
| # 'done' mark. A dependent external project's 'configure' step | |
| # depends on the 'done' mark so that it rebuilds when this project | |
| # rebuilds. It is important that 'done' is not the output of any | |
| # custom command so that CMake does not propagate build rules to | |
| # other external project targets, which may cause problems during | |
| # parallel builds. However, the Ninja generator needs to see the entire | |
| # dependency graph, and can cope with custom commands belonging to | |
| # multiple targets, so we add the 'done' mark as an output for Ninja only. | |
| set(complete_outputs ${complete_stamp_file}) | |
| if(${CMAKE_GENERATOR} MATCHES "Ninja") | |
| set(complete_outputs ${complete_outputs} ${done_stamp_file}) | |
| endif() | |
| add_custom_command( | |
| OUTPUT ${complete_outputs} | |
| COMMENT "Completed '${name}'" | |
| COMMAND ${CMAKE_COMMAND} -E make_directory ${cmf_dir}${cfgdir} | |
| COMMAND ${CMAKE_COMMAND} -E touch ${complete_stamp_file} | |
| COMMAND ${CMAKE_COMMAND} -E touch ${done_stamp_file} | |
| DEPENDS ${install_stamp_file} | |
| VERBATIM | |
| ) | |
| # Depend on other external projects (target-level). | |
| get_property(deps TARGET ${name} PROPERTY _EP_DEPENDS) | |
| foreach(arg IN LISTS deps) | |
| add_dependencies(${name} ${arg}) | |
| endforeach() | |
| # Set up custom build steps based on the target properties. | |
| # Each step depends on the previous one. | |
| # | |
| # The target depends on the output of the final step. | |
| # (Already set up above in the DEPENDS of the add_custom_target command.) | |
| # | |
| _ep_add_mkdir_command(${name}) | |
| _ep_add_download_command(${name}) | |
| _ep_add_update_command(${name}) | |
| _ep_add_patch_command(${name}) | |
| _ep_add_configure_command(${name}) | |
| _ep_add_build_command(${name}) | |
| _ep_add_install_command(${name}) | |
| # Test is special in that it might depend on build, or it might depend | |
| # on install. | |
| # | |
| _ep_add_test_command(${name}) | |
| endfunction() | |
| cmake_policy(POP) |