1# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2# file Copyright.txt or https://cmake.org/licensing for details.
3
4include_guard(GLOBAL)
5
6#[=======================================================================[.rst:
7ExternalProject
8---------------
9
10.. only:: html
11
12   .. contents::
13
14Commands
15^^^^^^^^
16
17External Project Definition
18"""""""""""""""""""""""""""
19
20.. command:: ExternalProject_Add
21
22  The ``ExternalProject_Add()`` function creates a custom target to drive
23  download, update/patch, configure, build, install and test steps of an
24  external project:
25
26  .. code-block:: cmake
27
28    ExternalProject_Add(<name> [<option>...])
29
30  The individual steps within the process can be driven independently if
31  required (e.g. for CDash submission) and extra custom steps can be defined,
32  along with the ability to control the step dependencies. The directory
33  structure used for the management of the external project can also be
34  customized. The function supports a large number of options which can be used
35  to tailor the external project behavior.
36
37  **Directory Options:**
38    Most of the time, the default directory layout is sufficient. It is largely
39    an implementation detail that the main project usually doesn't need to
40    change. In some circumstances, however, control over the directory layout
41    can be useful or necessary. The directory options are potentially more
42    useful from the point of view that the main build can use the
43    :command:`ExternalProject_Get_Property` command to retrieve their values,
44    thereby allowing the main project to refer to build artifacts of the
45    external project.
46
47    ``PREFIX <dir>``
48      Root directory for the external project. Unless otherwise noted below,
49      all other directories associated with the external project will be
50      created under here.
51
52    ``TMP_DIR <dir>``
53      Directory in which to store temporary files.
54
55    ``STAMP_DIR <dir>``
56      Directory in which to store the timestamps of each step. Log files from
57      individual steps are also created in here unless overridden by LOG_DIR
58      (see *Logging Options* below).
59
60    ``LOG_DIR <dir>``
61      .. versionadded:: 3.14
62
63      Directory in which to store the logs of each step.
64
65    ``DOWNLOAD_DIR <dir>``
66      Directory in which to store downloaded files before unpacking them. This
67      directory is only used by the URL download method, all other download
68      methods use ``SOURCE_DIR`` directly instead.
69
70    ``SOURCE_DIR <dir>``
71      Source directory into which downloaded contents will be unpacked, or for
72      non-URL download methods, the directory in which the repository should be
73      checked out, cloned, etc. If no download method is specified, this must
74      point to an existing directory where the external project has already
75      been unpacked or cloned/checked out.
76
77      .. note::
78         If a download method is specified, any existing contents of the source
79         directory may be deleted. Only the URL download method checks whether
80         this directory is either missing or empty before initiating the
81         download, stopping with an error if it is not empty. All other
82         download methods silently discard any previous contents of the source
83         directory.
84
85    ``BINARY_DIR <dir>``
86      Specify the build directory location. This option is ignored if
87      ``BUILD_IN_SOURCE`` is enabled.
88
89    ``INSTALL_DIR <dir>``
90      Installation prefix to be placed in the ``<INSTALL_DIR>`` placeholder.
91      This does not actually configure the external project to install to
92      the given prefix. That must be done by passing appropriate arguments
93      to the external project configuration step, e.g. using ``<INSTALL_DIR>``.
94
95    If any of the above ``..._DIR`` options are not specified, their defaults
96    are computed as follows. If the ``PREFIX`` option is given or the
97    ``EP_PREFIX`` directory property is set, then an external project is built
98    and installed under the specified prefix::
99
100      TMP_DIR      = <prefix>/tmp
101      STAMP_DIR    = <prefix>/src/<name>-stamp
102      DOWNLOAD_DIR = <prefix>/src
103      SOURCE_DIR   = <prefix>/src/<name>
104      BINARY_DIR   = <prefix>/src/<name>-build
105      INSTALL_DIR  = <prefix>
106      LOG_DIR      = <STAMP_DIR>
107
108    Otherwise, if the ``EP_BASE`` directory property is set then components
109    of an external project are stored under the specified base::
110
111      TMP_DIR      = <base>/tmp/<name>
112      STAMP_DIR    = <base>/Stamp/<name>
113      DOWNLOAD_DIR = <base>/Download/<name>
114      SOURCE_DIR   = <base>/Source/<name>
115      BINARY_DIR   = <base>/Build/<name>
116      INSTALL_DIR  = <base>/Install/<name>
117      LOG_DIR      = <STAMP_DIR>
118
119    If no ``PREFIX``, ``EP_PREFIX``, or ``EP_BASE`` is specified, then the
120    default is to set ``PREFIX`` to ``<name>-prefix``. Relative paths are
121    interpreted with respect to :variable:`CMAKE_CURRENT_BINARY_DIR` at the
122    point where ``ExternalProject_Add()`` is called.
123
124  **Download Step Options:**
125    A download method can be omitted if the ``SOURCE_DIR`` option is used to
126    point to an existing non-empty directory. Otherwise, one of the download
127    methods below must be specified (multiple download methods should not be
128    given) or a custom ``DOWNLOAD_COMMAND`` provided.
129
130    ``DOWNLOAD_COMMAND <cmd>...``
131      Overrides the command used for the download step
132      (:manual:`generator expressions <cmake-generator-expressions(7)>` are
133      supported). If this option is specified, all other download options will
134      be ignored. Providing an empty string for ``<cmd>`` effectively disables
135      the download step.
136
137    *URL Download*
138      ``URL <url1> [<url2>...]``
139        List of paths and/or URL(s) of the external project's source. When more
140        than one URL is given, they are tried in turn until one succeeds. A URL
141        may be an ordinary path in the local file system (in which case it
142        must be the only URL provided) or any downloadable URL supported by the
143        :command:`file(DOWNLOAD)` command. A local filesystem path may refer to
144        either an existing directory or to an archive file, whereas a URL is
145        expected to point to a file which can be treated as an archive. When an
146        archive is used, it will be unpacked automatically unless the
147        ``DOWNLOAD_NO_EXTRACT`` option is set to prevent it. The archive type
148        is determined by inspecting the actual content rather than using logic
149        based on the file extension.
150
151        .. versionchanged:: 3.7
152          Multiple URLs are allowed.
153
154      ``URL_HASH <algo>=<hashValue>``
155        Hash of the archive file to be downloaded. The argument should be of
156        the form ``<algo>=<hashValue>`` where ``algo`` can be any of the hashing
157        algorithms supported by the :command:`file()` command. Specifying this
158        option is strongly recommended for URL downloads, as it ensures the
159        integrity of the downloaded content. It is also used as a check for a
160        previously downloaded file, allowing connection to the remote location
161        to be avoided altogether if the local directory already has a file from
162        an earlier download that matches the specified hash.
163
164      ``URL_MD5 <md5>``
165        Equivalent to ``URL_HASH MD5=<md5>``.
166
167      ``DOWNLOAD_NAME <fname>``
168        File name to use for the downloaded file. If not given, the end of the
169        URL is used to determine the file name. This option is rarely needed,
170        the default name is generally suitable and is not normally used outside
171        of code internal to the ``ExternalProject`` module.
172
173      ``DOWNLOAD_NO_EXTRACT <bool>``
174        .. versionadded:: 3.6
175
176        Allows the extraction part of the download step to be disabled by
177        passing a boolean true value for this option. If this option is not
178        given, the downloaded contents will be unpacked automatically if
179        required. If extraction has been disabled, the full path to the
180        downloaded file is available as ``<DOWNLOADED_FILE>`` in subsequent
181        steps or as the property ``DOWNLOADED_FILE`` with the
182        :command:`ExternalProject_Get_Property` command.
183
184      ``DOWNLOAD_NO_PROGRESS <bool>``
185        Can be used to disable logging the download progress. If this option is
186        not given, download progress messages will be logged.
187
188      ``TIMEOUT <seconds>``
189        Maximum time allowed for file download operations.
190
191      ``INACTIVITY_TIMEOUT <seconds>``
192        .. versionadded:: 3.19
193
194        Terminate the operation after a period of inactivity.
195
196      ``HTTP_USERNAME <username>``
197        .. versionadded:: 3.7
198
199        Username for the download operation if authentication is required.
200
201      ``HTTP_PASSWORD <password>``
202        .. versionadded:: 3.7
203
204        Password for the download operation if authentication is required.
205
206      ``HTTP_HEADER <header1> [<header2>...]``
207        .. versionadded:: 3.7
208
209        Provides an arbitrary list of HTTP headers for the download operation.
210        This can be useful for accessing content in systems like AWS, etc.
211
212      ``TLS_VERIFY <bool>``
213        Specifies whether certificate verification should be performed for
214        https URLs. If this option is not provided, the default behavior is
215        determined by the :variable:`CMAKE_TLS_VERIFY` variable (see
216        :command:`file(DOWNLOAD)`). If that is also not set, certificate
217        verification will not be performed. In situations where ``URL_HASH``
218        cannot be provided, this option can be an alternative verification
219        measure.
220
221        .. versionchanged:: 3.6
222          This option also applies to ``git clone`` invocations.
223
224      ``TLS_CAINFO <file>``
225        Specify a custom certificate authority file to use if ``TLS_VERIFY``
226        is enabled. If this option is not specified, the value of the
227        :variable:`CMAKE_TLS_CAINFO` variable will be used instead (see
228        :command:`file(DOWNLOAD)`)
229
230      ``NETRC <level>``
231        .. versionadded:: 3.11
232
233        Specify whether the ``.netrc`` file is to be used for operation.
234        If this option is not specified, the value of the :variable:`CMAKE_NETRC`
235        variable will be used instead (see :command:`file(DOWNLOAD)`)
236        Valid levels are:
237
238        ``IGNORED``
239          The ``.netrc`` file is ignored.
240          This is the default.
241        ``OPTIONAL``
242          The ``.netrc`` file is optional, and information in the URL
243          is preferred.  The file will be scanned to find which ever
244          information is not specified in the URL.
245        ``REQUIRED``
246          The ``.netrc`` file is required, and information in the URL
247          is ignored.
248
249      ``NETRC_FILE <file>``
250        .. versionadded:: 3.11
251
252        Specify an alternative ``.netrc`` file to the one in your home directory
253        if the ``NETRC`` level is ``OPTIONAL`` or ``REQUIRED``. If this option
254        is not specified, the value of the :variable:`CMAKE_NETRC_FILE` variable
255        will be used instead (see :command:`file(DOWNLOAD)`)
256
257      .. versionadded:: 3.1
258        Added support for `tbz2`, `.tar.xz`, `.txz`, and `.7z` extensions.
259
260    *Git*
261      NOTE: A git version of 1.6.5 or later is required if this download method
262      is used.
263
264      ``GIT_REPOSITORY <url>``
265        URL of the git repository. Any URL understood by the ``git`` command
266        may be used.
267
268      ``GIT_TAG <tag>``
269        Git branch name, tag or commit hash. Note that branch names and tags
270        should generally be specified as remote names (i.e. ``origin/myBranch``
271        rather than simply ``myBranch``). This ensures that if the remote end
272        has its tag moved or branch rebased or history rewritten, the local
273        clone will still be updated correctly. In general, however, specifying
274        a commit hash should be preferred for a number of reasons:
275
276        - If the local clone already has the commit corresponding to the hash,
277          no ``git fetch`` needs to be performed to check for changes each time
278          CMake is re-run. This can result in a significant speed up if many
279          external projects are being used.
280        - Using a specific git hash ensures that the main project's own history
281          is fully traceable to a specific point in the external project's
282          evolution. If a branch or tag name is used instead, then checking out
283          a specific commit of the main project doesn't necessarily pin the
284          whole build to a specific point in the life of the external project.
285          The lack of such deterministic behavior makes the main project lose
286          traceability and repeatability.
287
288        If ``GIT_SHALLOW`` is enabled then ``GIT_TAG`` works only with
289        branch names and tags.  A commit hash is not allowed.
290
291      ``GIT_REMOTE_NAME <name>``
292        The optional name of the remote. If this option is not specified, it
293        defaults to ``origin``.
294
295      ``GIT_SUBMODULES <module>...``
296        Specific git submodules that should also be updated. If this option is
297        not provided, all git submodules will be updated.
298
299        .. versionchanged:: 3.16
300          When :policy:`CMP0097` is set to ``NEW``, if this value is set
301          to an empty string then no submodules are initialized or updated.
302
303      ``GIT_SUBMODULES_RECURSE <bool>``
304        .. versionadded:: 3.17
305
306        Specify whether git submodules (if any) should update recursively by
307        passing the ``--recursive`` flag to ``git submodule update``.
308        If not specified, the default is on.
309
310      ``GIT_SHALLOW <bool>``
311        .. versionadded:: 3.6
312
313        When this option is enabled, the ``git clone`` operation will be given
314        the ``--depth 1`` option. This performs a shallow clone, which avoids
315        downloading the whole history and instead retrieves just the commit
316        denoted by the ``GIT_TAG`` option.
317
318      ``GIT_PROGRESS <bool>``
319        .. versionadded:: 3.8
320
321        When enabled, this option instructs the ``git clone`` operation to
322        report its progress by passing it the ``--progress`` option. Without
323        this option, the clone step for large projects may appear to make the
324        build stall, since nothing will be logged until the clone operation
325        finishes. While this option can be used to provide progress to prevent
326        the appearance of the build having stalled, it may also make the build
327        overly noisy if lots of external projects are used.
328
329      ``GIT_CONFIG <option1> [<option2>...]``
330        .. versionadded:: 3.8
331
332        Specify a list of config options to pass to ``git clone``. Each option
333        listed will be transformed into its own ``--config <option>`` on the
334        ``git clone`` command line, with each option required to be in the
335        form ``key=value``.
336
337      ``GIT_REMOTE_UPDATE_STRATEGY <strategy>``
338        .. versionadded:: 3.18
339
340        When ``GIT_TAG`` refers to a remote branch, this option can be used to
341        specify how the update step behaves.  The ``<strategy>`` must be one of
342        the following:
343
344        ``CHECKOUT``
345          Ignore the local branch and always checkout the branch specified by
346          ``GIT_TAG``.
347
348        ``REBASE``
349          Try to rebase the current branch to the one specified by ``GIT_TAG``.
350          If there are local uncommitted changes, they will be stashed first
351          and popped again after rebasing.  If rebasing or popping stashed
352          changes fail, abort the rebase and halt with an error.
353          When ``GIT_REMOTE_UPDATE_STRATEGY`` is not present, this is the
354          default strategy unless the default has been overridden with
355          ``CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY`` (see below).
356
357        ``REBASE_CHECKOUT``
358          Same as ``REBASE`` except if the rebase fails, an annotated tag will
359          be created at the original ``HEAD`` position from before the rebase
360          and then checkout ``GIT_TAG`` just like the ``CHECKOUT`` strategy.
361          The message stored on the annotated tag will give information about
362          what was attempted and the tag name will include a timestamp so that
363          each failed run will add a new tag.  This strategy ensures no changes
364          will be lost, but updates should always succeed if ``GIT_TAG`` refers
365          to a valid ref unless there are uncommitted changes that cannot be
366          popped successfully.
367
368        The variable ``CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY`` can be set to
369        override the default strategy.  This variable should not be set by a
370        project, it is intended for the user to set.  It is primarily intended
371        for use in continuous integration scripts to ensure that when history
372        is rewritten on a remote branch, the build doesn't end up with unintended
373        changes or failed builds resulting from conflicts during rebase operations.
374
375    *Subversion*
376      ``SVN_REPOSITORY <url>``
377        URL of the Subversion repository.
378
379      ``SVN_REVISION -r<rev>``
380        Revision to checkout from the Subversion repository.
381
382      ``SVN_USERNAME <username>``
383        Username for the Subversion checkout and update.
384
385      ``SVN_PASSWORD <password>``
386        Password for the Subversion checkout and update.
387
388      ``SVN_TRUST_CERT <bool>``
389        Specifies whether to trust the Subversion server site certificate. If
390        enabled, the ``--trust-server-cert`` option is passed to the ``svn``
391        checkout and update commands.
392
393    *Mercurial*
394      ``HG_REPOSITORY <url>``
395        URL of the mercurial repository.
396
397      ``HG_TAG <tag>``
398        Mercurial branch name, tag or commit id.
399
400    *CVS*
401      ``CVS_REPOSITORY <cvsroot>``
402        CVSROOT of the CVS repository.
403
404      ``CVS_MODULE <mod>``
405        Module to checkout from the CVS repository.
406
407      ``CVS_TAG <tag>``
408        Tag to checkout from the CVS repository.
409
410  **Update/Patch Step Options:**
411    Whenever CMake is re-run, by default the external project's sources will be
412    updated if the download method supports updates (e.g. a git repository
413    would be checked if the ``GIT_TAG`` does not refer to a specific commit).
414
415    ``UPDATE_COMMAND <cmd>...``
416      Overrides the download method's update step with a custom command.
417      The command may use
418      :manual:`generator expressions <cmake-generator-expressions(7)>`.
419
420    ``UPDATE_DISCONNECTED <bool>``
421      .. versionadded:: 3.2
422
423      When enabled, this option causes the update step to be skipped. It does
424      not, however, prevent the download step. The update step can still be
425      added as a step target (see :command:`ExternalProject_Add_StepTargets`)
426      and called manually. This is useful if you want to allow developers to
427      build the project when disconnected from the network (the network may
428      still be needed for the download step though).
429
430      When this option is present, it is generally advisable to make the value
431      a cache variable under the developer's control rather than hard-coding
432      it. If this option is not present, the default value is taken from the
433      ``EP_UPDATE_DISCONNECTED`` directory property. If that is also not
434      defined, updates are performed as normal. The ``EP_UPDATE_DISCONNECTED``
435      directory property is intended as a convenience for controlling the
436      ``UPDATE_DISCONNECTED`` behavior for an entire section of a project's
437      directory hierarchy and may be a more convenient method of giving
438      developers control over whether or not to perform updates (assuming the
439      project also provides a cache variable or some other convenient method
440      for setting the directory property).
441
442      This may cause a step target to be created automatically for the
443      ``download`` step.  See policy :policy:`CMP0114`.
444
445    ``PATCH_COMMAND <cmd>...``
446      Specifies a custom command to patch the sources after an update. By
447      default, no patch command is defined. Note that it can be quite difficult
448      to define an appropriate patch command that performs robustly, especially
449      for download methods such as git where changing the ``GIT_TAG`` will not
450      discard changes from a previous patch, but the patch command will be
451      called again after updating to the new tag.
452
453  **Configure Step Options:**
454    The configure step is run after the download and update steps. By default,
455    the external project is assumed to be a CMake project, but this can be
456    overridden if required.
457
458    ``CONFIGURE_COMMAND <cmd>...``
459      The default configure command runs CMake with a few options based on
460      the main project.  The options added are typically only those needed to
461      use the same generator as the main project, but the ``CMAKE_GENERATOR``
462      option can be given to override this.  The project is responsible for
463      adding any toolchain details, flags or other settings it wants to
464      re-use from the main project or otherwise specify (see ``CMAKE_ARGS``,
465      ``CMAKE_CACHE_ARGS`` and ``CMAKE_CACHE_DEFAULT_ARGS`` below).
466
467      For non-CMake external projects, the ``CONFIGURE_COMMAND`` option must
468      be used to override the default configure command
469      (:manual:`generator expressions <cmake-generator-expressions(7)>` are
470      supported). For projects that require no configure step, specify this
471      option with an empty string as the command to execute.
472
473    ``CMAKE_COMMAND /.../cmake``
474      Specify an alternative cmake executable for the configure step (use an
475      absolute path). This is generally not recommended, since it is
476      usually desirable to use the same CMake version throughout the whole
477      build. This option is ignored if a custom configure command has been
478      specified with ``CONFIGURE_COMMAND``.
479
480    ``CMAKE_GENERATOR <gen>``
481      Override the CMake generator used for the configure step. Without this
482      option, the same generator as the main build will be used. This option is
483      ignored if a custom configure command has been specified with the
484      ``CONFIGURE_COMMAND`` option.
485
486    ``CMAKE_GENERATOR_PLATFORM <platform>``
487      .. versionadded:: 3.1
488
489      Pass a generator-specific platform name to the CMake command (see
490      :variable:`CMAKE_GENERATOR_PLATFORM`). It is an error to provide this
491      option without the ``CMAKE_GENERATOR`` option.
492
493    ``CMAKE_GENERATOR_TOOLSET <toolset>``
494      Pass a generator-specific toolset name to the CMake command (see
495      :variable:`CMAKE_GENERATOR_TOOLSET`). It is an error to provide this
496      option without the ``CMAKE_GENERATOR`` option.
497
498    ``CMAKE_GENERATOR_INSTANCE <instance>``
499      .. versionadded:: 3.11
500
501      Pass a generator-specific instance selection to the CMake command (see
502      :variable:`CMAKE_GENERATOR_INSTANCE`). It is an error to provide this
503      option without the ``CMAKE_GENERATOR`` option.
504
505    ``CMAKE_ARGS <arg>...``
506      The specified arguments are passed to the ``cmake`` command line. They
507      can be any argument the ``cmake`` command understands, not just cache
508      values defined by ``-D...`` arguments (see also
509      :manual:`CMake Options <cmake(1)>`).
510
511      .. versionadded:: 3.3
512        Arguments may use :manual:`generator expressions <cmake-generator-expressions(7)>`.
513
514    ``CMAKE_CACHE_ARGS <arg>...``
515      This is an alternate way of specifying cache variables where command line
516      length issues may become a problem. The arguments are expected to be in
517      the form ``-Dvar:STRING=value``, which are then transformed into
518      CMake :command:`set` commands with the ``FORCE`` option used. These
519      ``set()`` commands are written to a pre-load script which is then applied
520      using the :manual:`cmake -C <cmake(1)>` command line option.
521
522      .. versionadded:: 3.3
523        Arguments may use :manual:`generator expressions <cmake-generator-expressions(7)>`.
524
525    ``CMAKE_CACHE_DEFAULT_ARGS <arg>...``
526      .. versionadded:: 3.2
527
528      This is the same as the ``CMAKE_CACHE_ARGS`` option except the ``set()``
529      commands do not include the ``FORCE`` keyword. This means the values act
530      as initial defaults only and will not override any variables already set
531      from a previous run. Use this option with care, as it can lead to
532      different behavior depending on whether the build starts from a fresh
533      build directory or re-uses previous build contents.
534
535      .. versionadded:: 3.15
536        If the CMake generator is the ``Green Hills MULTI`` and not overridden then
537        the original project's settings for the GHS toolset and target system
538        customization cache variables are propagated into the external project.
539
540    ``SOURCE_SUBDIR <dir>``
541      .. versionadded:: 3.7
542
543      When no ``CONFIGURE_COMMAND`` option is specified, the configure step
544      assumes the external project has a ``CMakeLists.txt`` file at the top of
545      its source tree (i.e. in ``SOURCE_DIR``). The ``SOURCE_SUBDIR`` option
546      can be used to point to an alternative directory within the source tree
547      to use as the top of the CMake source tree instead. This must be a
548      relative path and it will be interpreted as being relative to
549      ``SOURCE_DIR``.
550
551      .. versionadded:: 3.14
552        When ``BUILD_IN_SOURCE`` option is enabled, the ``BUILD_COMMAND``
553        is used to point to an alternative directory within the source tree.
554
555    ``CONFIGURE_HANDLED_BY_BUILD <bool>``
556      .. versionadded:: 3.20
557
558      Enabling this option relaxes the dependencies of the configure step on
559      other external projects to order-only. This means the configure step will
560      be executed after its external project dependencies are built but it will
561      not be marked dirty when one of its external project dependencies is
562      rebuilt. This option can be enabled when the build step is smart enough
563      to figure out if the configure step needs to be rerun. CMake and Meson are
564      examples of build systems whose build step is smart enough to know if the
565      configure step needs to be rerun.
566
567  **Build Step Options:**
568    If the configure step assumed the external project uses CMake as its build
569    system, the build step will also. Otherwise, the build step will assume a
570    Makefile-based build and simply run ``make`` with no arguments as the
571    default build step. This can be overridden with custom build commands if
572    required.
573
574    If both the main project and the external project use make as their build
575    tool, the build step of the external project is invoked as a recursive
576    make using ``$(MAKE)``.  This will communicate some build tool settings
577    from the main project to the external project.  If either the main project
578    or external project is not using make, no build tool settings will be
579    passed to the external project other than those established by the
580    configure step (i.e. running ``ninja -v`` in the main project will not
581    pass ``-v`` to the external project's build step, even if it also uses
582    ``ninja`` as its build tool).
583
584    ``BUILD_COMMAND <cmd>...``
585      Overrides the default build command
586      (:manual:`generator expressions <cmake-generator-expressions(7)>` are
587      supported). If this option is not given, the default build command will
588      be chosen to integrate with the main build in the most appropriate way
589      (e.g. using recursive ``make`` for Makefile generators or
590      ``cmake --build`` if the project uses a CMake build). This option can be
591      specified with an empty string as the command to make the build step do
592      nothing.
593
594    ``BUILD_IN_SOURCE <bool>``
595      When this option is enabled, the build will be done directly within the
596      external project's source tree. This should generally be avoided, the use
597      of a separate build directory is usually preferred, but it can be useful
598      when the external project assumes an in-source build. The ``BINARY_DIR``
599      option should not be specified if building in-source.
600
601    ``BUILD_ALWAYS <bool>``
602      Enabling this option forces the build step to always be run. This can be
603      the easiest way to robustly ensure that the external project's own build
604      dependencies are evaluated rather than relying on the default
605      success timestamp-based method. This option is not normally needed unless
606      developers are expected to modify something the external project's build
607      depends on in a way that is not detectable via the step target
608      dependencies (e.g. ``SOURCE_DIR`` is used without a download method and
609      developers might modify the sources in ``SOURCE_DIR``).
610
611    ``BUILD_BYPRODUCTS <file>...``
612      .. versionadded:: 3.2
613
614      Specifies files that will be generated by the build command but which
615      might or might not have their modification time updated by subsequent
616      builds. These ultimately get passed through as ``BYPRODUCTS`` to the
617      build step's own underlying call to :command:`add_custom_command`.
618
619  **Install Step Options:**
620    If the configure step assumed the external project uses CMake as its build
621    system, the install step will also. Otherwise, the install step will assume
622    a Makefile-based build and simply run ``make install`` as the default build
623    step. This can be overridden with custom install commands if required.
624
625    ``INSTALL_COMMAND <cmd>...``
626      The external project's own install step is invoked as part of the main
627      project's *build*. It is done after the external project's build step
628      and may be before or after the external project's test step (see the
629      ``TEST_BEFORE_INSTALL`` option below). The external project's install
630      rules are not part of the main project's install rules, so if anything
631      from the external project should be installed as part of the main build,
632      these need to be specified in the main build as additional
633      :command:`install` commands. The default install step builds the
634      ``install`` target of the external project, but this can be overridden
635      with a custom command using this option
636      (:manual:`generator expressions <cmake-generator-expressions(7)>` are
637      supported). Passing an empty string as the ``<cmd>`` makes the install
638      step do nothing.
639
640    .. note::
641      If the :envvar:`CMAKE_INSTALL_MODE` environment variable is set when the
642      main project is built, it will only have an effect if the following
643      conditions are met:
644
645      * The main project's configure step assumed the external project uses
646        CMake as its build system.
647      * The external project's install command actually runs. Note that due
648        to the way ``ExternalProject`` may use timestamps internally, if
649        nothing the install step depends on needs to be re-executed, the
650        install command might also not need to run.
651
652      Note also that ``ExternalProject`` does not check whether the
653      :envvar:`CMAKE_INSTALL_MODE` environment variable changes from one run
654      to another.
655
656  **Test Step Options:**
657    The test step is only defined if at least one of the following ``TEST_...``
658    options are provided.
659
660    ``TEST_COMMAND <cmd>...``
661      Overrides the default test command
662      (:manual:`generator expressions <cmake-generator-expressions(7)>` are
663      supported). If this option is not given, the default behavior of the test
664      step is to build the external project's own ``test`` target. This option
665      can be specified with ``<cmd>`` as an empty string, which allows the test
666      step to still be defined, but it will do nothing. Do not specify any of
667      the other ``TEST_...`` options if providing an empty string as the test
668      command, but prefer to omit all ``TEST_...`` options altogether if the
669      test step target is not needed.
670
671    ``TEST_BEFORE_INSTALL <bool>``
672      When this option is enabled, the test step will be executed before the
673      install step. The default behavior is for the test step to run after the
674      install step.
675
676    ``TEST_AFTER_INSTALL <bool>``
677      This option is mainly useful as a way to indicate that the test step is
678      desired but all default behavior is sufficient. Specifying this option
679      with a boolean true value ensures the test step is defined and that it
680      comes after the install step. If both ``TEST_BEFORE_INSTALL`` and
681      ``TEST_AFTER_INSTALL`` are enabled, the latter is silently ignored.
682
683    ``TEST_EXCLUDE_FROM_MAIN <bool>``
684      .. versionadded:: 3.2
685
686      If enabled, the main build's default ALL target will not depend on the
687      test step. This can be a useful way of ensuring the test step is defined
688      but only gets invoked when manually requested.
689      This may cause a step target to be created automatically for either
690      the ``install`` or ``build`` step.  See policy :policy:`CMP0114`.
691
692  **Output Logging Options:**
693    Each of the following ``LOG_...`` options can be used to wrap the relevant
694    step in a script to capture its output to files. The log files will be
695    created in ``LOG_DIR`` if supplied or otherwise the ``STAMP_DIR``
696    directory with step-specific file names.
697
698    ``LOG_DOWNLOAD <bool>``
699      When enabled, the output of the download step is logged to files.
700
701    ``LOG_UPDATE <bool>``
702      When enabled, the output of the update step is logged to files.
703
704    ``LOG_PATCH <bool>``
705      .. versionadded:: 3.14
706
707      When enabled, the output of the patch step is logged to files.
708
709    ``LOG_CONFIGURE <bool>``
710      When enabled, the output of the configure step is logged to files.
711
712    ``LOG_BUILD <bool>``
713      When enabled, the output of the build step is logged to files.
714
715    ``LOG_INSTALL <bool>``
716      When enabled, the output of the install step is logged to files.
717
718    ``LOG_TEST <bool>``
719      When enabled, the output of the test step is logged to files.
720
721    ``LOG_MERGED_STDOUTERR <bool>``
722      .. versionadded:: 3.14
723
724      When enabled, stdout and stderr will be merged for any step whose
725      output is being logged to files.
726
727    ``LOG_OUTPUT_ON_FAILURE <bool>``
728      .. versionadded:: 3.14
729
730      This option only has an effect if at least one of the other ``LOG_<step>``
731      options is enabled.  If an error occurs for a step which has logging to
732      file enabled, that step's output will be printed to the console if
733      ``LOG_OUTPUT_ON_FAILURE`` is set to true.  For cases where a large amount
734      of output is recorded, just the end of that output may be printed to the
735      console.
736
737  **Terminal Access Options:**
738    .. versionadded:: 3.4
739
740    Steps can be given direct access to the terminal in some cases. Giving a
741    step access to the terminal may allow it to receive terminal input if
742    required, such as for authentication details not provided by other options.
743    With the :generator:`Ninja` generator, these options place the steps in the
744    ``console`` :prop_gbl:`job pool <JOB_POOLS>`. Each step can be given access
745    to the terminal individually via the following options:
746
747    ``USES_TERMINAL_DOWNLOAD <bool>``
748      Give the download step access to the terminal.
749
750    ``USES_TERMINAL_UPDATE <bool>``
751      Give the update step access to the terminal.
752
753    ``USES_TERMINAL_CONFIGURE <bool>``
754      Give the configure step access to the terminal.
755
756    ``USES_TERMINAL_BUILD <bool>``
757      Give the build step access to the terminal.
758
759    ``USES_TERMINAL_INSTALL <bool>``
760      Give the install step access to the terminal.
761
762    ``USES_TERMINAL_TEST <bool>``
763      Give the test step access to the terminal.
764
765  **Target Options:**
766    ``DEPENDS <targets>...``
767      Specify other targets on which the external project depends. The other
768      targets will be brought up to date before any of the external project's
769      steps are executed. Because the external project uses additional custom
770      targets internally for each step, the ``DEPENDS`` option is the most
771      convenient way to ensure all of those steps depend on the other targets.
772      Simply doing
773      :command:`add_dependencies(\<name\> \<targets\>) <add_dependencies>` will
774      not make any of the steps dependent on ``<targets>``.
775
776    ``EXCLUDE_FROM_ALL <bool>``
777      When enabled, this option excludes the external project from the default
778      ALL target of the main build.
779
780    ``STEP_TARGETS <step-target>...``
781      Generate custom targets for the specified steps. This is required if the
782      steps need to be triggered manually or if they need to be used as
783      dependencies of other targets. If this option is not specified, the
784      default value is taken from the ``EP_STEP_TARGETS`` directory property.
785      See :command:`ExternalProject_Add_StepTargets` below for further
786      discussion of the effects of this option.
787
788    ``INDEPENDENT_STEP_TARGETS <step-target>...``
789      .. deprecated:: 3.19
790        This is allowed only if policy :policy:`CMP0114` is not set to ``NEW``.
791
792      Generates custom targets for the specified steps and prevent these targets
793      from having the usual dependencies applied to them. If this option is not
794      specified, the default value is taken from the
795      ``EP_INDEPENDENT_STEP_TARGETS`` directory property. This option is mostly
796      useful for allowing individual steps to be driven independently, such as
797      for a CDash setup where each step should be initiated and reported
798      individually rather than as one whole build. See
799      :command:`ExternalProject_Add_StepTargets` below for further discussion
800      of the effects of this option.
801
802  **Miscellaneous Options:**
803    ``LIST_SEPARATOR <sep>``
804      For any of the various ``..._COMMAND`` options, replace ``;`` with
805      ``<sep>`` in the specified command lines. This can be useful where list
806      variables may be given in commands where they should end up as
807      space-separated arguments (``<sep>`` would be a single space character
808      string in this case).
809
810    ``COMMAND <cmd>...``
811      Any of the other ``..._COMMAND`` options can have additional commands
812      appended to them by following them with as many ``COMMAND ...`` options
813      as needed
814      (:manual:`generator expressions <cmake-generator-expressions(7)>` are
815      supported). For example:
816
817      .. code-block:: cmake
818
819        ExternalProject_Add(example
820          ... # Download options, etc.
821          BUILD_COMMAND ${CMAKE_COMMAND} -E echo "Starting $<CONFIG> build"
822          COMMAND       ${CMAKE_COMMAND} --build <BINARY_DIR> --config $<CONFIG>
823          COMMAND       ${CMAKE_COMMAND} -E echo "$<CONFIG> build complete"
824        )
825
826  It should also be noted that each build step is created via a call to
827  :command:`ExternalProject_Add_Step`. See that command's documentation for the
828  automatic substitutions that are supported for some options.
829
830Obtaining Project Properties
831""""""""""""""""""""""""""""
832
833.. command:: ExternalProject_Get_Property
834
835  The ``ExternalProject_Get_Property()`` function retrieves external project
836  target properties:
837
838  .. code-block:: cmake
839
840    ExternalProject_Get_Property(<name> <prop1> [<prop2>...])
841
842  The function stores property values in variables of the same name. Property
843  names correspond to the keyword argument names of ``ExternalProject_Add()``.
844  For example, the source directory might be retrieved like so:
845
846  .. code-block:: cmake
847
848    ExternalProject_Get_property(myExtProj SOURCE_DIR)
849    message("Source dir of myExtProj = ${SOURCE_DIR}")
850
851Explicit Step Management
852""""""""""""""""""""""""
853
854The ``ExternalProject_Add()`` function on its own is often sufficient for
855incorporating an external project into the main build. Certain scenarios
856require additional work to implement desired behavior, such as adding in a
857custom step or making steps available as manually triggerable targets. The
858``ExternalProject_Add_Step()``, ``ExternalProject_Add_StepTargets()`` and
859``ExternalProject_Add_StepDependencies`` functions provide the lower level
860control needed to implement such step-level capabilities.
861
862.. command:: ExternalProject_Add_Step
863
864  The ``ExternalProject_Add_Step()`` function specifies an additional custom
865  step for an external project defined by an earlier call to
866  :command:`ExternalProject_Add`:
867
868  .. code-block:: cmake
869
870    ExternalProject_Add_Step(<name> <step> [<option>...])
871
872  ``<name>`` is the same as the name passed to the original call to
873  :command:`ExternalProject_Add`. The specified ``<step>`` must not be one of
874  the pre-defined steps (``mkdir``, ``download``, ``update``,
875  ``patch``, ``configure``, ``build``, ``install`` or ``test``). The supported
876  options are:
877
878  ``COMMAND <cmd>...``
879    The command line to be executed by this custom step
880    (:manual:`generator expressions <cmake-generator-expressions(7)>` are
881    supported). This option can be repeated multiple times to specify multiple
882    commands to be executed in order.
883
884  ``COMMENT "<text>..."``
885    Text to be printed when the custom step executes.
886
887  ``DEPENDEES <step>...``
888    Other steps (custom or pre-defined) on which this step depends.
889
890  ``DEPENDERS <step>...``
891    Other steps (custom or pre-defined) that depend on this new custom step.
892
893  ``DEPENDS <file>...``
894    Files on which this custom step depends.
895
896  ``INDEPENDENT <bool>``
897    .. versionadded:: 3.19
898
899    Specifies whether this step is independent of the external dependencies
900    specified by the :command:`ExternalProject_Add`'s ``DEPENDS`` option.
901    The default is ``FALSE``.  Steps marked as independent may depend only
902    on other steps marked independent.  See policy :policy:`CMP0114`.
903
904    Note that this use of the term "independent" refers only to independence
905    from external targets specified by the ``DEPENDS`` option and is
906    orthogonal to a step's dependencies on other steps.
907
908    If a step target is created for an independent step by the
909    :command:`ExternalProject_Add` ``STEP_TARGETS`` option or by the
910    :command:`ExternalProject_Add_StepTargets` function, it will not depend
911    on the external targets, but may depend on targets for other steps.
912
913  ``BYPRODUCTS <file>...``
914    .. versionadded:: 3.2
915
916    Files that will be generated by this custom step but which might or might
917    not have their modification time updated by subsequent builds. This list of
918    files will ultimately be passed through as the ``BYPRODUCTS`` option to the
919    :command:`add_custom_command` used to implement the custom step internally.
920
921  ``ALWAYS <bool>``
922    When enabled, this option specifies that the custom step should always be
923    run (i.e. that it is always considered out of date).
924
925  ``EXCLUDE_FROM_MAIN <bool>``
926    When enabled, this option specifies that the external project's main target
927    does not depend on the custom step.
928    This may cause step targets to be created automatically for the steps on
929    which this step depends.  See policy :policy:`CMP0114`.
930
931  ``WORKING_DIRECTORY <dir>``
932    Specifies the working directory to set before running the custom step's
933    command. If this option is not specified, the directory will be the value
934    of the :variable:`CMAKE_CURRENT_BINARY_DIR` at the point where
935    ``ExternalProject_Add_Step()`` was called.
936
937  ``LOG <bool>``
938    If set, this causes the output from the custom step to be captured to files
939    in the external project's ``LOG_DIR`` if supplied or ``STAMP_DIR``.
940
941  ``USES_TERMINAL <bool>``
942    If enabled, this gives the custom step direct access to the terminal if
943    possible.
944
945  The command line, comment, working directory and byproducts of every
946  standard and custom step are processed to replace the tokens
947  ``<SOURCE_DIR>``, ``<SOURCE_SUBDIR>``, ``<BINARY_DIR>``, ``<INSTALL_DIR>``
948  ``<TMP_DIR>``, ``<DOWNLOAD_DIR>`` and ``<DOWNLOADED_FILE>`` with their
949  corresponding property values defined in the original call to
950  :command:`ExternalProject_Add`.
951
952  .. versionadded:: 3.3
953    Token replacement is extended to byproducts.
954
955  .. versionadded:: 3.11
956    The ``<DOWNLOAD_DIR>`` substitution token.
957
958.. command:: ExternalProject_Add_StepTargets
959
960  The ``ExternalProject_Add_StepTargets()`` function generates targets for the
961  steps listed. The name of each created target will be of the form
962  ``<name>-<step>``:
963
964  .. code-block:: cmake
965
966    ExternalProject_Add_StepTargets(<name> <step1> [<step2>...])
967
968  Creating a target for a step allows it to be used as a dependency of another
969  target or to be triggered manually. Having targets for specific steps also
970  allows them to be driven independently of each other by specifying targets on
971  build command lines. For example, you may be submitting to a sub-project
972  based dashboard where you want to drive the configure portion of the build,
973  then submit to the dashboard, followed by the build portion, followed
974  by tests. If you invoke a custom target that depends on a step halfway
975  through the step dependency chain, then all the previous steps will also run
976  to ensure everything is up to date.
977
978  Internally, :command:`ExternalProject_Add` calls
979  :command:`ExternalProject_Add_Step` to create each step. If any
980  ``STEP_TARGETS`` were specified, then ``ExternalProject_Add_StepTargets()``
981  will also be called after :command:`ExternalProject_Add_Step`.  Even if a
982  step is not mentioned in the ``STEP_TARGETS`` option,
983  ``ExternalProject_Add_StepTargets()`` can still be called later to manually
984  define a target for the step.
985
986  The ``STEP_TARGETS`` option for :command:`ExternalProject_Add` is generally
987  the easiest way to ensure targets are created for specific steps of interest.
988  For custom steps, ``ExternalProject_Add_StepTargets()`` must be called
989  explicitly if a target should also be created for that custom step.
990  An alternative to these two options is to populate the ``EP_STEP_TARGETS``
991  directory property.  It acts as a default for the step target options and
992  can save having to repeatedly specify the same set of step targets when
993  multiple external projects are being defined.
994
995  .. versionadded:: 3.19
996    If :policy:`CMP0114` is set to ``NEW``, step targets are fully responsible
997    for holding the custom commands implementing their steps.  The primary target
998    created by ``ExternalProject_Add`` depends on the step targets, and the
999    step targets depend on each other.  The target-level dependencies match
1000    the file-level dependencies used by the custom commands for each step.
1001    The targets for steps created with :command:`ExternalProject_Add_Step`'s
1002    ``INDEPENDENT`` option do not depend on the external targets specified
1003    by :command:`ExternalProject_Add`'s ``DEPENDS`` option.  The predefined
1004    steps ``mkdir``, ``download``, ``update``, and ``patch`` are independent.
1005
1006  If :policy:`CMP0114` is not ``NEW``, the following deprecated behavior
1007  is available:
1008
1009  * A deprecated ``NO_DEPENDS`` option may be specified immediately after the
1010    ``<name>`` and before the first step.
1011    If the ``NO_DEPENDS`` option is specified, the step target will not depend on
1012    the dependencies of the external project (i.e. on any dependencies of the
1013    ``<name>`` custom target created by :command:`ExternalProject_Add`). This is
1014    usually safe for the ``download``, ``update`` and ``patch`` steps, since they
1015    do not typically require that the dependencies are updated and built. Using
1016    ``NO_DEPENDS`` for any of the other pre-defined steps, however, may break
1017    parallel builds. Only use ``NO_DEPENDS`` where it is certain that the named
1018    steps genuinely do not have dependencies. For custom steps, consider whether
1019    or not the custom commands require the dependencies to be configured, built
1020    and installed.
1021
1022  * The ``INDEPENDENT_STEP_TARGETS`` option for :command:`ExternalProject_Add`,
1023    or the ``EP_INDEPENDENT_STEP_TARGETS`` directory property, tells the
1024    function to call ``ExternalProject_Add_StepTargets()`` internally
1025    using the ``NO_DEPENDS`` option for the specified steps.
1026
1027.. command:: ExternalProject_Add_StepDependencies
1028
1029  .. versionadded:: 3.2
1030
1031  The ``ExternalProject_Add_StepDependencies()`` function can be used to add
1032  dependencies to a step. The dependencies added must be targets CMake already
1033  knows about (these can be ordinary executable or library targets, custom
1034  targets or even step targets of another external project):
1035
1036  .. code-block:: cmake
1037
1038    ExternalProject_Add_StepDependencies(<name> <step> <target1> [<target2>...])
1039
1040  This function takes care to set both target and file level dependencies and
1041  will ensure that parallel builds will not break. It should be used instead of
1042  :command:`add_dependencies` whenever adding a dependency for some of the step
1043  targets generated by the ``ExternalProject`` module.
1044
1045Examples
1046^^^^^^^^
1047
1048The following example shows how to download and build a hypothetical project
1049called *FooBar* from github:
1050
1051.. code-block:: cmake
1052
1053  include(ExternalProject)
1054  ExternalProject_Add(foobar
1055    GIT_REPOSITORY    git@github.com:FooCo/FooBar.git
1056    GIT_TAG           origin/release/1.2.3
1057  )
1058
1059For the sake of the example, also define a second hypothetical external project
1060called *SecretSauce*, which is downloaded from a web server. Two URLs are given
1061to take advantage of a faster internal network if available, with a fallback to
1062a slower external server. The project is a typical ``Makefile`` project with no
1063configure step, so some of the default commands are overridden. The build is
1064only required to build the *sauce* target:
1065
1066.. code-block:: cmake
1067
1068  find_program(MAKE_EXE NAMES gmake nmake make)
1069  ExternalProject_Add(secretsauce
1070    URL               http://intranet.somecompany.com/artifacts/sauce-2.7.tgz
1071                      https://www.somecompany.com/downloads/sauce-2.7.zip
1072    URL_HASH          MD5=d41d8cd98f00b204e9800998ecf8427e
1073    CONFIGURE_COMMAND ""
1074    BUILD_COMMAND     ${MAKE_EXE} sauce
1075  )
1076
1077Suppose the build step of ``secretsauce`` requires that ``foobar`` must already
1078be built. This could be enforced like so:
1079
1080.. code-block:: cmake
1081
1082  ExternalProject_Add_StepDependencies(secretsauce build foobar)
1083
1084Another alternative would be to create a custom target for ``foobar``'s build
1085step and make ``secretsauce`` depend on that rather than the whole ``foobar``
1086project. This would mean ``foobar`` only needs to be built, it doesn't need to
1087run its install or test steps before ``secretsauce`` can be built. The
1088dependency can also be defined along with the ``secretsauce`` project:
1089
1090.. code-block:: cmake
1091
1092  ExternalProject_Add_StepTargets(foobar build)
1093  ExternalProject_Add(secretsauce
1094    URL               http://intranet.somecompany.com/artifacts/sauce-2.7.tgz
1095                      https://www.somecompany.com/downloads/sauce-2.7.zip
1096    URL_HASH          MD5=d41d8cd98f00b204e9800998ecf8427e
1097    CONFIGURE_COMMAND ""
1098    BUILD_COMMAND     ${MAKE_EXE} sauce
1099    DEPENDS           foobar-build
1100  )
1101
1102Instead of calling :command:`ExternalProject_Add_StepTargets`, the target could
1103be defined along with the ``foobar`` project itself:
1104
1105.. code-block:: cmake
1106
1107  ExternalProject_Add(foobar
1108    GIT_REPOSITORY git@github.com:FooCo/FooBar.git
1109    GIT_TAG        origin/release/1.2.3
1110    STEP_TARGETS   build
1111  )
1112
1113If many external projects should have the same set of step targets, setting a
1114directory property may be more convenient. The ``build`` step target could be
1115created automatically by setting the ``EP_STEP_TARGETS`` directory property
1116before creating the external projects with :command:`ExternalProject_Add`:
1117
1118.. code-block:: cmake
1119
1120  set_property(DIRECTORY PROPERTY EP_STEP_TARGETS build)
1121
1122Lastly, suppose that ``secretsauce`` provides a script called ``makedoc`` which
1123can be used to generate its own documentation. Further suppose that the script
1124expects the output directory to be provided as the only parameter and that it
1125should be run from the ``secretsauce`` source directory. A custom step and a
1126custom target to trigger the script can be defined like so:
1127
1128.. code-block:: cmake
1129
1130  ExternalProject_Add_Step(secretsauce docs
1131    COMMAND           <SOURCE_DIR>/makedoc <BINARY_DIR>
1132    WORKING_DIRECTORY <SOURCE_DIR>
1133    COMMENT           "Building secretsauce docs"
1134    ALWAYS            TRUE
1135    EXCLUDE_FROM_MAIN TRUE
1136  )
1137  ExternalProject_Add_StepTargets(secretsauce docs)
1138
1139The custom step could then be triggered from the main build like so::
1140
1141  cmake --build . --target secretsauce-docs
1142
1143#]=======================================================================]
1144
1145cmake_policy(PUSH)
1146cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced
1147cmake_policy(SET CMP0057 NEW) # if() supports IN_LIST
1148
1149macro(_ep_get_hash_algos out_var)
1150  set(${out_var}
1151    MD5
1152    SHA1
1153    SHA224
1154    SHA256
1155    SHA384
1156    SHA512
1157    SHA3_224
1158    SHA3_256
1159    SHA3_384
1160    SHA3_512
1161  )
1162endmacro()
1163
1164macro(_ep_get_hash_regex out_var)
1165  _ep_get_hash_algos(${out_var})
1166  list(JOIN ${out_var} "|" ${out_var})
1167  set(${out_var} "^(${${out_var}})=([0-9A-Fa-f]+)$")
1168endmacro()
1169
1170function(_ep_parse_arguments f keywords name ns args)
1171  # Transfer the arguments to this function into target properties for the
1172  # new custom target we just added so that we can set up all the build steps
1173  # correctly based on target properties.
1174  #
1175  # Because some keywords can be repeated, we can't use cmake_parse_arguments().
1176  # Instead, we loop through ARGN and consider the namespace starting with an
1177  # upper-case letter followed by at least two more upper-case letters,
1178  # numbers or underscores to be keywords.
1179
1180  set(key)
1181
1182  foreach(arg IN LISTS args)
1183    set(is_value 1)
1184
1185    if(arg MATCHES "^[A-Z][A-Z0-9_][A-Z0-9_]+$" AND
1186        NOT (("x${arg}x" STREQUAL "x${key}x") AND ("x${key}x" STREQUAL "xCOMMANDx")) AND
1187        NOT arg MATCHES "^(TRUE|FALSE)$")
1188      if(arg IN_LIST keywords)
1189        set(is_value 0)
1190      endif()
1191    endif()
1192
1193    if(is_value)
1194      if(key)
1195        # Value
1196        if(NOT arg STREQUAL "")
1197          set_property(TARGET ${name} APPEND PROPERTY ${ns}${key} "${arg}")
1198        else()
1199          get_property(have_key TARGET ${name} PROPERTY ${ns}${key} SET)
1200          if(have_key)
1201            get_property(value TARGET ${name} PROPERTY ${ns}${key})
1202            set_property(TARGET ${name} PROPERTY ${ns}${key} "${value};${arg}")
1203          else()
1204            set_property(TARGET ${name} PROPERTY ${ns}${key} "${arg}")
1205          endif()
1206        endif()
1207      else()
1208        # Missing Keyword
1209        message(AUTHOR_WARNING "value '${arg}' with no previous keyword in ${f}")
1210      endif()
1211    else()
1212      set(key "${arg}")
1213    endif()
1214  endforeach()
1215endfunction()
1216
1217
1218define_property(DIRECTORY PROPERTY "EP_BASE" INHERITED
1219  BRIEF_DOCS "Base directory for External Project storage."
1220  FULL_DOCS
1221  "See documentation of the ExternalProject_Add() function in the "
1222  "ExternalProject module."
1223  )
1224
1225define_property(DIRECTORY PROPERTY "EP_PREFIX" INHERITED
1226  BRIEF_DOCS "Top prefix for External Project storage."
1227  FULL_DOCS
1228  "See documentation of the ExternalProject_Add() function in the "
1229  "ExternalProject module."
1230  )
1231
1232define_property(DIRECTORY PROPERTY "EP_STEP_TARGETS" INHERITED
1233  BRIEF_DOCS
1234  "List of ExternalProject steps that automatically get corresponding targets"
1235  FULL_DOCS
1236  "These targets will be dependent on the main target dependencies. "
1237  "See documentation of the ExternalProject_Add_StepTargets() function in the "
1238  "ExternalProject module."
1239  )
1240
1241define_property(DIRECTORY PROPERTY "EP_INDEPENDENT_STEP_TARGETS" INHERITED
1242  BRIEF_DOCS
1243  "List of ExternalProject steps that automatically get corresponding targets"
1244  FULL_DOCS
1245  "These targets will not be dependent on the main target dependencies. "
1246  "See documentation of the ExternalProject_Add_StepTargets() function in the "
1247  "ExternalProject module."
1248  )
1249
1250define_property(DIRECTORY PROPERTY "EP_UPDATE_DISCONNECTED" INHERITED
1251  BRIEF_DOCS "Never update automatically from the remote repo."
1252  FULL_DOCS
1253  "See documentation of the ExternalProject_Add() function in the "
1254  "ExternalProject module."
1255  )
1256
1257function(_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)
1258  if(NOT GIT_VERSION_STRING VERSION_LESS 1.8.5)
1259    # Use `git checkout <tree-ish> --` to avoid ambiguity with a local path.
1260    set(git_checkout_explicit-- "--")
1261  else()
1262    # Use `git checkout <branch>` even though this risks ambiguity with a
1263    # local path.  Unfortunately we cannot use `git checkout <tree-ish> --`
1264    # because that will not search for remote branch names, a common use case.
1265    set(git_checkout_explicit-- "")
1266  endif()
1267  if("${git_tag}" STREQUAL "")
1268    message(FATAL_ERROR "Tag for git checkout should not be empty.")
1269  endif()
1270
1271  if(GIT_VERSION_STRING VERSION_LESS 2.20 OR 2.21 VERSION_LESS_EQUAL GIT_VERSION_STRING)
1272    set(git_clone_options "--no-checkout")
1273  else()
1274    set(git_clone_options)
1275  endif()
1276  if(git_shallow)
1277    if(NOT GIT_VERSION_STRING VERSION_LESS 1.7.10)
1278      list(APPEND git_clone_options "--depth 1 --no-single-branch")
1279    else()
1280      list(APPEND git_clone_options "--depth 1")
1281    endif()
1282  endif()
1283  if(git_progress)
1284    list(APPEND git_clone_options --progress)
1285  endif()
1286  foreach(config IN LISTS git_config)
1287    list(APPEND git_clone_options --config \"${config}\")
1288  endforeach()
1289  if(NOT ${git_remote_name} STREQUAL "origin")
1290    list(APPEND git_clone_options --origin \"${git_remote_name}\")
1291  endif()
1292
1293  string (REPLACE ";" " " git_clone_options "${git_clone_options}")
1294
1295  set(git_options)
1296  # disable cert checking if explicitly told not to do it
1297  if(NOT "x${tls_verify}" STREQUAL "x" AND NOT tls_verify)
1298    set(git_options
1299      -c http.sslVerify=false)
1300  endif()
1301  string (REPLACE ";" " " git_options "${git_options}")
1302
1303  file(WRITE ${script_filename}
1304"
1305if(NOT \"${gitclone_infofile}\" IS_NEWER_THAN \"${gitclone_stampfile}\")
1306  message(STATUS \"Avoiding repeated git clone, stamp file is up to date: '${gitclone_stampfile}'\")
1307  return()
1308endif()
1309
1310execute_process(
1311  COMMAND \${CMAKE_COMMAND} -E rm -rf \"${source_dir}\"
1312  RESULT_VARIABLE error_code
1313  )
1314if(error_code)
1315  message(FATAL_ERROR \"Failed to remove directory: '${source_dir}'\")
1316endif()
1317
1318# try the clone 3 times in case there is an odd git clone issue
1319set(error_code 1)
1320set(number_of_tries 0)
1321while(error_code AND number_of_tries LESS 3)
1322  execute_process(
1323    COMMAND \"${git_EXECUTABLE}\" ${git_options} clone ${git_clone_options} \"${git_repository}\" \"${src_name}\"
1324    WORKING_DIRECTORY \"${work_dir}\"
1325    RESULT_VARIABLE error_code
1326    )
1327  math(EXPR number_of_tries \"\${number_of_tries} + 1\")
1328endwhile()
1329if(number_of_tries GREATER 1)
1330  message(STATUS \"Had to git clone more than once:
1331          \${number_of_tries} times.\")
1332endif()
1333if(error_code)
1334  message(FATAL_ERROR \"Failed to clone repository: '${git_repository}'\")
1335endif()
1336
1337execute_process(
1338  COMMAND \"${git_EXECUTABLE}\" ${git_options} checkout ${git_tag} ${git_checkout_explicit--}
1339  WORKING_DIRECTORY \"${work_dir}/${src_name}\"
1340  RESULT_VARIABLE error_code
1341  )
1342if(error_code)
1343  message(FATAL_ERROR \"Failed to checkout tag: '${git_tag}'\")
1344endif()
1345
1346set(init_submodules ${init_submodules})
1347if(init_submodules)
1348  execute_process(
1349    COMMAND \"${git_EXECUTABLE}\" ${git_options} submodule update ${git_submodules_recurse} --init ${git_submodules}
1350    WORKING_DIRECTORY \"${work_dir}/${src_name}\"
1351    RESULT_VARIABLE error_code
1352    )
1353endif()
1354if(error_code)
1355  message(FATAL_ERROR \"Failed to update submodules in: '${work_dir}/${src_name}'\")
1356endif()
1357
1358# Complete success, update the script-last-run stamp file:
1359#
1360execute_process(
1361  COMMAND \${CMAKE_COMMAND} -E copy
1362    \"${gitclone_infofile}\"
1363    \"${gitclone_stampfile}\"
1364  RESULT_VARIABLE error_code
1365  )
1366if(error_code)
1367  message(FATAL_ERROR \"Failed to copy script-last-run stamp file: '${gitclone_stampfile}'\")
1368endif()
1369
1370"
1371)
1372
1373endfunction()
1374
1375function(_ep_write_hgclone_script script_filename source_dir hg_EXECUTABLE hg_repository hg_tag src_name work_dir hgclone_infofile hgclone_stampfile)
1376  if("${hg_tag}" STREQUAL "")
1377    message(FATAL_ERROR "Tag for hg checkout should not be empty.")
1378  endif()
1379  file(WRITE ${script_filename}
1380"
1381if(NOT \"${hgclone_infofile}\" IS_NEWER_THAN \"${hgclone_stampfile}\")
1382  message(STATUS \"Avoiding repeated hg clone, stamp file is up to date: '${hgclone_stampfile}'\")
1383  return()
1384endif()
1385
1386execute_process(
1387  COMMAND \${CMAKE_COMMAND} -E rm -rf \"${source_dir}\"
1388  RESULT_VARIABLE error_code
1389  )
1390if(error_code)
1391  message(FATAL_ERROR \"Failed to remove directory: '${source_dir}'\")
1392endif()
1393
1394execute_process(
1395  COMMAND \"${hg_EXECUTABLE}\" clone -U \"${hg_repository}\" \"${src_name}\"
1396  WORKING_DIRECTORY \"${work_dir}\"
1397  RESULT_VARIABLE error_code
1398  )
1399if(error_code)
1400  message(FATAL_ERROR \"Failed to clone repository: '${hg_repository}'\")
1401endif()
1402
1403execute_process(
1404  COMMAND \"${hg_EXECUTABLE}\" update ${hg_tag}
1405  WORKING_DIRECTORY \"${work_dir}/${src_name}\"
1406  RESULT_VARIABLE error_code
1407  )
1408if(error_code)
1409  message(FATAL_ERROR \"Failed to checkout tag: '${hg_tag}'\")
1410endif()
1411
1412# Complete success, update the script-last-run stamp file:
1413#
1414execute_process(
1415  COMMAND \${CMAKE_COMMAND} -E copy
1416    \"${hgclone_infofile}\"
1417    \"${hgclone_stampfile}\"
1418  RESULT_VARIABLE error_code
1419  )
1420if(error_code)
1421  message(FATAL_ERROR \"Failed to copy script-last-run stamp file: '${hgclone_stampfile}'\")
1422endif()
1423
1424"
1425)
1426
1427endfunction()
1428
1429
1430function(_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)
1431  if("${git_tag}" STREQUAL "")
1432    message(FATAL_ERROR "Tag for git checkout should not be empty.")
1433  endif()
1434  set(git_stash_save_options --quiet)
1435  if(GIT_VERSION_STRING VERSION_GREATER_EQUAL 1.7.7)
1436    # This avoids stashing files covered by .gitignore
1437    list(APPEND git_stash_save_options --include-untracked)
1438  elseif(GIT_VERSION_STRING VERSION_GREATER_EQUAL 1.7.6)
1439    # Untracked files, but also ignored files, so potentially slower
1440    list(APPEND git_stash_save_options --all)
1441  endif()
1442
1443  configure_file(
1444      "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject-gitupdate.cmake.in"
1445      "${script_filename}"
1446      @ONLY
1447  )
1448endfunction()
1449
1450function(_ep_write_downloadfile_script script_filename REMOTE LOCAL timeout inactivity_timeout no_progress hash tls_verify tls_cainfo userpwd http_headers netrc netrc_file)
1451  if(timeout)
1452    set(TIMEOUT_ARGS TIMEOUT ${timeout})
1453    set(TIMEOUT_MSG "${timeout} seconds")
1454  else()
1455    set(TIMEOUT_ARGS "# no TIMEOUT")
1456    set(TIMEOUT_MSG "none")
1457  endif()
1458  if(inactivity_timeout)
1459    set(INACTIVITY_TIMEOUT_ARGS INACTIVITY_TIMEOUT ${inactivity_timeout})
1460    set(INACTIVITY_TIMEOUT_MSG "${inactivity_timeout} seconds")
1461  else()
1462    set(INACTIVITY_TIMEOUT_ARGS "# no INACTIVITY_TIMEOUT")
1463    set(INACTIVITY_TIMEOUT_MSG "none")
1464  endif()
1465
1466
1467  if(no_progress)
1468    set(SHOW_PROGRESS "")
1469  else()
1470    set(SHOW_PROGRESS "SHOW_PROGRESS")
1471  endif()
1472
1473  _ep_get_hash_regex(_ep_hash_regex)
1474  if("${hash}" MATCHES "${_ep_hash_regex}")
1475    set(ALGO "${CMAKE_MATCH_1}")
1476    string(TOLOWER "${CMAKE_MATCH_2}" EXPECT_VALUE)
1477  else()
1478    set(ALGO "")
1479    set(EXPECT_VALUE "")
1480  endif()
1481
1482  set(TLS_VERIFY_CODE "")
1483  set(TLS_CAINFO_CODE "")
1484  set(NETRC_CODE "")
1485  set(NETRC_FILE_CODE "")
1486
1487  # check for curl globals in the project
1488  if(DEFINED CMAKE_TLS_VERIFY)
1489    set(TLS_VERIFY_CODE "set(CMAKE_TLS_VERIFY ${CMAKE_TLS_VERIFY})")
1490  endif()
1491  if(DEFINED CMAKE_TLS_CAINFO)
1492    set(TLS_CAINFO_CODE "set(CMAKE_TLS_CAINFO \"${CMAKE_TLS_CAINFO}\")")
1493  endif()
1494  if(DEFINED CMAKE_NETRC)
1495    set(NETRC_CODE "set(CMAKE_NETRC \"${CMAKE_NETRC}\")")
1496  endif()
1497  if(DEFINED CMAKE_NETRC_FILE)
1498    set(NETRC_FILE_CODE "set(CMAKE_NETRC_FILE \"${CMAKE_NETRC_FILE}\")")
1499  endif()
1500
1501  # now check for curl locals so that the local values
1502  # will override the globals
1503
1504  # check for tls_verify argument
1505  string(LENGTH "${tls_verify}" tls_verify_len)
1506  if(tls_verify_len GREATER 0)
1507    set(TLS_VERIFY_CODE "set(CMAKE_TLS_VERIFY ${tls_verify})")
1508  endif()
1509  # check for tls_cainfo argument
1510  string(LENGTH "${tls_cainfo}" tls_cainfo_len)
1511  if(tls_cainfo_len GREATER 0)
1512    set(TLS_CAINFO_CODE "set(CMAKE_TLS_CAINFO \"${tls_cainfo}\")")
1513  endif()
1514  # check for netrc argument
1515  string(LENGTH "${netrc}" netrc_len)
1516  if(netrc_len GREATER 0)
1517    set(NETRC_CODE "set(CMAKE_NETRC \"${netrc}\")")
1518  endif()
1519  # check for netrc_file argument
1520  string(LENGTH "${netrc_file}" netrc_file_len)
1521  if(netrc_file_len GREATER 0)
1522    set(NETRC_FILE_CODE "set(CMAKE_NETRC_FILE \"${netrc_file}\")")
1523  endif()
1524
1525  if(userpwd STREQUAL ":")
1526    set(USERPWD_ARGS)
1527  else()
1528    set(USERPWD_ARGS USERPWD "${userpwd}")
1529  endif()
1530
1531  set(HTTP_HEADERS_ARGS "")
1532  if(NOT http_headers STREQUAL "")
1533    foreach(header ${http_headers})
1534      set(
1535          HTTP_HEADERS_ARGS
1536          "HTTPHEADER \"${header}\"\n        ${HTTP_HEADERS_ARGS}"
1537      )
1538    endforeach()
1539  endif()
1540
1541  # Used variables:
1542  # * TLS_VERIFY_CODE
1543  # * TLS_CAINFO_CODE
1544  # * ALGO
1545  # * EXPECT_VALUE
1546  # * REMOTE
1547  # * LOCAL
1548  # * SHOW_PROGRESS
1549  # * TIMEOUT_ARGS
1550  # * TIMEOUT_MSG
1551  # * USERPWD_ARGS
1552  # * HTTP_HEADERS_ARGS
1553  configure_file(
1554      "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject-download.cmake.in"
1555      "${script_filename}"
1556      @ONLY
1557  )
1558endfunction()
1559
1560function(_ep_write_verifyfile_script script_filename LOCAL hash)
1561  _ep_get_hash_regex(_ep_hash_regex)
1562  if("${hash}" MATCHES "${_ep_hash_regex}")
1563    set(ALGO "${CMAKE_MATCH_1}")
1564    string(TOLOWER "${CMAKE_MATCH_2}" EXPECT_VALUE)
1565  else()
1566    set(ALGO "")
1567    set(EXPECT_VALUE "")
1568  endif()
1569
1570  # Used variables:
1571  # * ALGO
1572  # * EXPECT_VALUE
1573  # * LOCAL
1574  configure_file(
1575      "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject-verify.cmake.in"
1576      "${script_filename}"
1577      @ONLY
1578  )
1579endfunction()
1580
1581
1582function(_ep_write_extractfile_script script_filename name filename directory)
1583  set(args "")
1584
1585  if(filename MATCHES "(\\.|=)(7z|tar\\.bz2|tar\\.gz|tar\\.xz|tbz2|tgz|txz|zip)$")
1586    set(args xfz)
1587  endif()
1588
1589  if(filename MATCHES "(\\.|=)tar$")
1590    set(args xf)
1591  endif()
1592
1593  if(args STREQUAL "")
1594    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")
1595    return()
1596  endif()
1597
1598  file(WRITE ${script_filename}
1599"# Make file names absolute:
1600#
1601get_filename_component(filename \"${filename}\" ABSOLUTE)
1602get_filename_component(directory \"${directory}\" ABSOLUTE)
1603
1604message(STATUS \"extracting...
1605     src='\${filename}'
1606     dst='\${directory}'\")
1607
1608if(NOT EXISTS \"\${filename}\")
1609  message(FATAL_ERROR \"error: file to extract does not exist: '\${filename}'\")
1610endif()
1611
1612# Prepare a space for extracting:
1613#
1614set(i 1234)
1615while(EXISTS \"\${directory}/../ex-${name}\${i}\")
1616  math(EXPR i \"\${i} + 1\")
1617endwhile()
1618set(ut_dir \"\${directory}/../ex-${name}\${i}\")
1619file(MAKE_DIRECTORY \"\${ut_dir}\")
1620
1621# Extract it:
1622#
1623message(STATUS \"extracting... [tar ${args}]\")
1624execute_process(COMMAND \${CMAKE_COMMAND} -E tar ${args} \${filename}
1625  WORKING_DIRECTORY \${ut_dir}
1626  RESULT_VARIABLE rv)
1627
1628if(NOT rv EQUAL 0)
1629  message(STATUS \"extracting... [error clean up]\")
1630  file(REMOVE_RECURSE \"\${ut_dir}\")
1631  message(FATAL_ERROR \"error: extract of '\${filename}' failed\")
1632endif()
1633
1634# Analyze what came out of the tar file:
1635#
1636message(STATUS \"extracting... [analysis]\")
1637file(GLOB contents \"\${ut_dir}/*\")
1638list(REMOVE_ITEM contents \"\${ut_dir}/.DS_Store\")
1639list(LENGTH contents n)
1640if(NOT n EQUAL 1 OR NOT IS_DIRECTORY \"\${contents}\")
1641  set(contents \"\${ut_dir}\")
1642endif()
1643
1644# Move \"the one\" directory to the final directory:
1645#
1646message(STATUS \"extracting... [rename]\")
1647file(REMOVE_RECURSE \${directory})
1648get_filename_component(contents \${contents} ABSOLUTE)
1649file(RENAME \${contents} \${directory})
1650
1651# Clean up:
1652#
1653message(STATUS \"extracting... [clean up]\")
1654file(REMOVE_RECURSE \"\${ut_dir}\")
1655
1656message(STATUS \"extracting... done\")
1657"
1658)
1659
1660endfunction()
1661
1662
1663function(_ep_set_directories name)
1664  get_property(prefix TARGET ${name} PROPERTY _EP_PREFIX)
1665  if(NOT prefix)
1666    get_property(prefix DIRECTORY PROPERTY EP_PREFIX)
1667    if(NOT prefix)
1668      get_property(base DIRECTORY PROPERTY EP_BASE)
1669      if(NOT base)
1670        set(prefix "${name}-prefix")
1671      endif()
1672    endif()
1673  endif()
1674  if(prefix)
1675    set(tmp_default "${prefix}/tmp")
1676    set(download_default "${prefix}/src")
1677    set(source_default "${prefix}/src/${name}")
1678    set(binary_default "${prefix}/src/${name}-build")
1679    set(stamp_default "${prefix}/src/${name}-stamp")
1680    set(install_default "${prefix}")
1681  else()
1682    set(tmp_default "${base}/tmp/${name}")
1683    set(download_default "${base}/Download/${name}")
1684    set(source_default "${base}/Source/${name}")
1685    set(binary_default "${base}/Build/${name}")
1686    set(stamp_default "${base}/Stamp/${name}")
1687    set(install_default "${base}/Install/${name}")
1688  endif()
1689  get_property(build_in_source TARGET ${name} PROPERTY _EP_BUILD_IN_SOURCE)
1690  if(build_in_source)
1691    get_property(have_binary_dir TARGET ${name} PROPERTY _EP_BINARY_DIR SET)
1692    if(have_binary_dir)
1693      message(FATAL_ERROR
1694        "External project ${name} has both BINARY_DIR and BUILD_IN_SOURCE!")
1695    endif()
1696  endif()
1697  set(top "${CMAKE_CURRENT_BINARY_DIR}")
1698
1699  # Apply defaults and convert to absolute paths.
1700  set(places stamp download source binary install tmp)
1701  foreach(var ${places})
1702    string(TOUPPER "${var}" VAR)
1703    get_property(${var}_dir TARGET ${name} PROPERTY _EP_${VAR}_DIR)
1704    if(NOT ${var}_dir)
1705      set(${var}_dir "${${var}_default}")
1706    endif()
1707    if(NOT IS_ABSOLUTE "${${var}_dir}")
1708      get_filename_component(${var}_dir "${top}/${${var}_dir}" ABSOLUTE)
1709    endif()
1710    set_property(TARGET ${name} PROPERTY _EP_${VAR}_DIR "${${var}_dir}")
1711  endforeach()
1712
1713  # Special case for default log directory based on stamp directory.
1714  get_property(log_dir TARGET ${name} PROPERTY _EP_LOG_DIR)
1715  if(NOT log_dir)
1716    get_property(log_dir TARGET ${name} PROPERTY _EP_STAMP_DIR)
1717  endif()
1718  if(NOT IS_ABSOLUTE "${log_dir}")
1719    get_filename_component(log_dir "${top}/${log_dir}" ABSOLUTE)
1720  endif()
1721  set_property(TARGET ${name} PROPERTY _EP_LOG_DIR "${log_dir}")
1722
1723  get_property(source_subdir TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR)
1724  if(NOT source_subdir)
1725    set_property(TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR "")
1726  elseif(IS_ABSOLUTE "${source_subdir}")
1727    message(FATAL_ERROR
1728      "External project ${name} has non-relative SOURCE_SUBDIR!")
1729  else()
1730    # Prefix with a slash so that when appended to the source directory, it
1731    # behaves as expected.
1732    set_property(TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR "/${source_subdir}")
1733  endif()
1734  if(build_in_source)
1735    get_property(source_dir TARGET ${name} PROPERTY _EP_SOURCE_DIR)
1736    if(source_subdir)
1737      set_property(TARGET ${name} PROPERTY _EP_BINARY_DIR "${source_dir}/${source_subdir}")
1738    else()
1739      set_property(TARGET ${name} PROPERTY _EP_BINARY_DIR "${source_dir}")
1740    endif()
1741  endif()
1742
1743  # Make the directories at CMake configure time *and* add a custom command
1744  # to make them at build time. They need to exist at makefile generation
1745  # time for Borland make and wmake so that CMake may generate makefiles
1746  # with "cd C:\short\paths\with\no\spaces" commands in them.
1747  #
1748  # Additionally, the add_custom_command is still used in case somebody
1749  # removes one of the necessary directories and tries to rebuild without
1750  # re-running cmake.
1751  foreach(var ${places})
1752    string(TOUPPER "${var}" VAR)
1753    get_property(dir TARGET ${name} PROPERTY _EP_${VAR}_DIR)
1754    file(MAKE_DIRECTORY "${dir}")
1755    if(NOT EXISTS "${dir}")
1756      message(FATAL_ERROR "dir '${dir}' does not exist after file(MAKE_DIRECTORY)")
1757    endif()
1758  endforeach()
1759endfunction()
1760
1761
1762# IMPORTANT: this MUST be a macro and not a function because of the
1763# in-place replacements that occur in each ${var}
1764#
1765macro(_ep_replace_location_tags target_name)
1766  set(vars ${ARGN})
1767  foreach(var ${vars})
1768    if(${var})
1769      foreach(dir SOURCE_DIR SOURCE_SUBDIR BINARY_DIR INSTALL_DIR TMP_DIR DOWNLOAD_DIR DOWNLOADED_FILE LOG_DIR)
1770        get_property(val TARGET ${target_name} PROPERTY _EP_${dir})
1771        string(REPLACE "<${dir}>" "${val}" ${var} "${${var}}")
1772      endforeach()
1773    endif()
1774  endforeach()
1775endmacro()
1776
1777
1778function(_ep_command_line_to_initial_cache var args force)
1779  set(script_initial_cache "")
1780  set(regex "^([^:]+):([^=]+)=(.*)$")
1781  set(setArg "")
1782  set(forceArg "")
1783  if(force)
1784    set(forceArg "FORCE")
1785  endif()
1786  foreach(line ${args})
1787    if("${line}" MATCHES "^-D(.*)")
1788      set(line "${CMAKE_MATCH_1}")
1789      if(NOT "${setArg}" STREQUAL "")
1790        # This is required to build up lists in variables, or complete an entry
1791        string(APPEND setArg "${accumulator}\" CACHE ${type} \"Initial cache\" ${forceArg})")
1792        string(APPEND script_initial_cache "\n${setArg}")
1793        set(accumulator "")
1794        set(setArg "")
1795      endif()
1796      if("${line}" MATCHES "${regex}")
1797        set(name "${CMAKE_MATCH_1}")
1798        set(type "${CMAKE_MATCH_2}")
1799        set(value "${CMAKE_MATCH_3}")
1800        set(setArg "set(${name} \"${value}")
1801      else()
1802        message(WARNING "Line '${line}' does not match regex. Ignoring.")
1803      endif()
1804    else()
1805      # Assume this is a list to append to the last var
1806      string(APPEND accumulator ";${line}")
1807    endif()
1808  endforeach()
1809  # Catch the final line of the args
1810  if(NOT "${setArg}" STREQUAL "")
1811    string(APPEND setArg "${accumulator}\" CACHE ${type} \"Initial cache\" ${forceArg})")
1812    string(APPEND script_initial_cache "\n${setArg}")
1813  endif()
1814  set(${var} ${script_initial_cache} PARENT_SCOPE)
1815endfunction()
1816
1817
1818function(_ep_write_initial_cache target_name script_filename script_initial_cache)
1819  # Write out values into an initial cache, that will be passed to CMake with -C
1820  # Replace location tags.
1821  _ep_replace_location_tags(${target_name} script_initial_cache)
1822  _ep_replace_location_tags(${target_name} script_filename)
1823  # Replace list separators.
1824  get_property(sep TARGET ${target_name} PROPERTY _EP_LIST_SEPARATOR)
1825  if(sep AND script_initial_cache)
1826    string(REPLACE "${sep}" ";" script_initial_cache "${script_initial_cache}")
1827  endif()
1828  # Write out the initial cache file to the location specified.
1829  file(GENERATE OUTPUT "${script_filename}" CONTENT "${script_initial_cache}")
1830endfunction()
1831
1832
1833function(ExternalProject_Get_Property name)
1834  foreach(var ${ARGN})
1835    string(TOUPPER "${var}" VAR)
1836    get_property(is_set TARGET ${name} PROPERTY _EP_${VAR} SET)
1837    if(NOT is_set)
1838      message(FATAL_ERROR "External project \"${name}\" has no ${var}")
1839    endif()
1840    get_property(${var} TARGET ${name} PROPERTY _EP_${VAR})
1841    set(${var} "${${var}}" PARENT_SCOPE)
1842  endforeach()
1843endfunction()
1844
1845
1846function(_ep_get_configure_command_id name cfg_cmd_id_var)
1847  get_target_property(cmd ${name} _EP_CONFIGURE_COMMAND)
1848
1849  if(cmd STREQUAL "")
1850    # Explicit empty string means no configure step for this project
1851    set(${cfg_cmd_id_var} "none" PARENT_SCOPE)
1852  else()
1853    if(NOT cmd)
1854      # Default is "use cmake":
1855      set(${cfg_cmd_id_var} "cmake" PARENT_SCOPE)
1856    else()
1857      # Otherwise we have to analyze the value:
1858      if(cmd MATCHES "^[^;]*/configure")
1859        set(${cfg_cmd_id_var} "configure" PARENT_SCOPE)
1860      elseif(cmd MATCHES "^[^;]*/cmake" AND NOT cmd MATCHES ";-[PE];")
1861        set(${cfg_cmd_id_var} "cmake" PARENT_SCOPE)
1862      elseif(cmd MATCHES "config")
1863        set(${cfg_cmd_id_var} "configure" PARENT_SCOPE)
1864      else()
1865        set(${cfg_cmd_id_var} "unknown:${cmd}" PARENT_SCOPE)
1866      endif()
1867    endif()
1868  endif()
1869endfunction()
1870
1871
1872function(_ep_get_build_command name step cmd_var)
1873  set(cmd "")
1874  set(args)
1875  _ep_get_configure_command_id(${name} cfg_cmd_id)
1876  if(cfg_cmd_id STREQUAL "cmake")
1877    # CMake project.  Select build command based on generator.
1878    get_target_property(cmake_generator ${name} _EP_CMAKE_GENERATOR)
1879    if("${CMAKE_GENERATOR}" MATCHES "Make" AND
1880       ("${cmake_generator}" MATCHES "Make" OR NOT cmake_generator))
1881      # The project uses the same Makefile generator.  Use recursive make.
1882      set(cmd "$(MAKE)")
1883      if(step STREQUAL "INSTALL")
1884        set(args install)
1885      endif()
1886      if("x${step}x" STREQUAL "xTESTx")
1887        set(args test)
1888      endif()
1889    else()
1890      # Drive the project with "cmake --build".
1891      get_target_property(cmake_command ${name} _EP_CMAKE_COMMAND)
1892      if(cmake_command)
1893        set(cmd "${cmake_command}")
1894      else()
1895        set(cmd "${CMAKE_COMMAND}")
1896      endif()
1897      set(args --build ".")
1898      get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
1899      if(_isMultiConfig)
1900        if (CMAKE_CFG_INTDIR AND
1901            NOT CMAKE_CFG_INTDIR STREQUAL "." AND
1902            NOT CMAKE_CFG_INTDIR MATCHES "\\$")
1903          # CMake 3.4 and below used the CMAKE_CFG_INTDIR placeholder value
1904          # provided by multi-configuration generators.  Some projects were
1905          # taking advantage of that undocumented implementation detail to
1906          # specify a specific configuration here.  They should use
1907          # BUILD_COMMAND to change the default command instead, but for
1908          # compatibility honor the value.
1909          set(config ${CMAKE_CFG_INTDIR})
1910          message(AUTHOR_WARNING "CMAKE_CFG_INTDIR should not be set by project code.\n"
1911            "To get a non-default build command, use the BUILD_COMMAND option.")
1912        else()
1913          set(config $<CONFIG>)
1914        endif()
1915        list(APPEND args --config ${config})
1916      endif()
1917      if(step STREQUAL "INSTALL")
1918        list(APPEND args --target install)
1919      endif()
1920      # But for "TEST" drive the project with corresponding "ctest".
1921      if("x${step}x" STREQUAL "xTESTx")
1922        string(REGEX REPLACE "^(.*/)cmake([^/]*)$" "\\1ctest\\2" cmd "${cmd}")
1923        set(args "")
1924        if(_isMultiConfig)
1925          list(APPEND args -C ${config})
1926        endif()
1927      endif()
1928    endif()
1929  else()
1930    # Non-CMake project.  Guess "make" and "make install" and "make test".
1931    if("${CMAKE_GENERATOR}" MATCHES "Makefiles")
1932      # Try to get the parallel arguments
1933      set(cmd "$(MAKE)")
1934    else()
1935      set(cmd "make")
1936    endif()
1937    if(step STREQUAL "INSTALL")
1938      set(args install)
1939    endif()
1940    if("x${step}x" STREQUAL "xTESTx")
1941      set(args test)
1942    endif()
1943  endif()
1944
1945  # Use user-specified arguments instead of default arguments, if any.
1946  get_property(have_args TARGET ${name} PROPERTY _EP_${step}_ARGS SET)
1947  if(have_args)
1948    get_target_property(args ${name} _EP_${step}_ARGS)
1949  endif()
1950
1951  if(NOT "${args}" STREQUAL "")
1952    # args could have empty items, so we must quote it to prevent them
1953    # from being silently removed
1954    list(APPEND cmd "${args}")
1955  endif()
1956  set(${cmd_var} "${cmd}" PARENT_SCOPE)
1957endfunction()
1958
1959function(_ep_write_log_script name step cmd_var)
1960  ExternalProject_Get_Property(${name} log_dir)
1961  ExternalProject_Get_Property(${name} stamp_dir)
1962  set(command "${${cmd_var}}")
1963
1964  set(make "")
1965  set(code_cygpath_make "")
1966  if(command MATCHES "^\\$\\(MAKE\\)")
1967    # GNU make recognizes the string "$(MAKE)" as recursive make, so
1968    # ensure that it appears directly in the makefile.
1969    string(REGEX REPLACE "^\\$\\(MAKE\\)" "\${make}" command "${command}")
1970    set(make "-Dmake=$(MAKE)")
1971
1972    if(WIN32 AND NOT CYGWIN)
1973      set(code_cygpath_make "
1974if(\${make} MATCHES \"^/\")
1975  execute_process(
1976    COMMAND cygpath -w \${make}
1977    OUTPUT_VARIABLE cygpath_make
1978    ERROR_VARIABLE cygpath_make
1979    RESULT_VARIABLE cygpath_error
1980    OUTPUT_STRIP_TRAILING_WHITESPACE
1981  )
1982  if(NOT cygpath_error)
1983    set(make \${cygpath_make})
1984  endif()
1985endif()
1986")
1987    endif()
1988  endif()
1989
1990  set(config "")
1991  if("${CMAKE_CFG_INTDIR}" MATCHES "^\\$")
1992    string(REPLACE "${CMAKE_CFG_INTDIR}" "\${config}" command "${command}")
1993    set(config "-Dconfig=${CMAKE_CFG_INTDIR}")
1994  endif()
1995
1996  # Wrap multiple 'COMMAND' lines up into a second-level wrapper
1997  # script so all output can be sent to one log file.
1998  if(command MATCHES "(^|;)COMMAND;")
1999    set(code_execute_process "
2000${code_cygpath_make}
2001execute_process(COMMAND \${command} RESULT_VARIABLE result)
2002if(result)
2003  set(msg \"Command failed (\${result}):\\n\")
2004  foreach(arg IN LISTS command)
2005    set(msg \"\${msg} '\${arg}'\")
2006  endforeach()
2007  message(FATAL_ERROR \"\${msg}\")
2008endif()
2009")
2010    set(code "")
2011    set(cmd "")
2012    set(sep "")
2013    foreach(arg IN LISTS command)
2014      if("x${arg}" STREQUAL "xCOMMAND")
2015        if(NOT "x${cmd}" STREQUAL "x")
2016          string(APPEND code "set(command \"${cmd}\")${code_execute_process}")
2017        endif()
2018        set(cmd "")
2019        set(sep "")
2020      else()
2021        string(APPEND cmd "${sep}${arg}")
2022        set(sep ";")
2023      endif()
2024    endforeach()
2025    string(APPEND code "set(command \"${cmd}\")${code_execute_process}")
2026    file(GENERATE OUTPUT "${stamp_dir}/${name}-${step}-$<CONFIG>-impl.cmake" CONTENT "${code}")
2027    set(command ${CMAKE_COMMAND} "-Dmake=\${make}" "-Dconfig=\${config}" -P ${stamp_dir}/${name}-${step}-$<CONFIG>-impl.cmake)
2028  endif()
2029
2030  # Wrap the command in a script to log output to files.
2031  set(script ${stamp_dir}/${name}-${step}-$<CONFIG>.cmake)
2032  set(logbase ${log_dir}/${name}-${step})
2033  get_property(log_merged TARGET ${name} PROPERTY _EP_LOG_MERGED_STDOUTERR)
2034  get_property(log_output_on_failure TARGET ${name} PROPERTY _EP_LOG_OUTPUT_ON_FAILURE)
2035  if (log_merged)
2036    set(stdout_log "${logbase}.log")
2037    set(stderr_log "${logbase}.log")
2038  else()
2039    set(stdout_log "${logbase}-out.log")
2040    set(stderr_log "${logbase}-err.log")
2041  endif()
2042  set(code "
2043cmake_minimum_required(VERSION 3.15)
2044${code_cygpath_make}
2045set(command \"${command}\")
2046set(log_merged \"${log_merged}\")
2047set(log_output_on_failure \"${log_output_on_failure}\")
2048set(stdout_log \"${stdout_log}\")
2049set(stderr_log \"${stderr_log}\")
2050execute_process(
2051  COMMAND \${command}
2052  RESULT_VARIABLE result
2053  OUTPUT_FILE \"\${stdout_log}\"
2054  ERROR_FILE \"\${stderr_log}\"
2055  )
2056macro(read_up_to_max_size log_file output_var)
2057  file(SIZE \${log_file} determined_size)
2058  set(max_size 10240)
2059  if (determined_size GREATER max_size)
2060    math(EXPR seek_position \"\${determined_size} - \${max_size}\")
2061    file(READ \${log_file} \${output_var} OFFSET \${seek_position})
2062    set(\${output_var} \"...skipping to end...\\n\${\${output_var}}\")
2063  else()
2064    file(READ \${log_file} \${output_var})
2065  endif()
2066endmacro()
2067if(result)
2068  set(msg \"Command failed: \${result}\\n\")
2069  foreach(arg IN LISTS command)
2070    set(msg \"\${msg} '\${arg}'\")
2071  endforeach()
2072  if (\${log_merged})
2073    set(msg \"\${msg}\\nSee also\\n  \${stderr_log}\")
2074  else()
2075    set(msg \"\${msg}\\nSee also\\n  ${logbase}-*.log\")
2076  endif()
2077  if (\${log_output_on_failure})
2078    message(SEND_ERROR \"\${msg}\")
2079    if (\${log_merged})
2080      read_up_to_max_size(\"\${stderr_log}\" error_log_contents)
2081      message(STATUS \"Log output is:\\n\${error_log_contents}\")
2082    else()
2083      read_up_to_max_size(\"\${stdout_log}\" out_log_contents)
2084      read_up_to_max_size(\"\${stderr_log}\" err_log_contents)
2085      message(STATUS \"stdout output is:\\n\${out_log_contents}\")
2086      message(STATUS \"stderr output is:\\n\${err_log_contents}\")
2087    endif()
2088    message(FATAL_ERROR \"Stopping after outputting logs.\")
2089  else()
2090    message(FATAL_ERROR \"\${msg}\")
2091  endif()
2092else()
2093  if(NOT \"${CMAKE_GENERATOR}\" MATCHES \"Ninja\")
2094    set(msg \"${name} ${step} command succeeded.  See also ${logbase}-*.log\")
2095    message(STATUS \"\${msg}\")
2096  endif()
2097endif()
2098")
2099  file(GENERATE OUTPUT "${script}" CONTENT "${code}")
2100  set(command ${CMAKE_COMMAND} ${make} ${config} -P ${script})
2101  set(${cmd_var} "${command}" PARENT_SCOPE)
2102endfunction()
2103
2104# This module used to use "/${CMAKE_CFG_INTDIR}" directly and produced
2105# makefiles with "/./" in paths for custom command dependencies. Which
2106# resulted in problems with parallel make -j invocations.
2107#
2108# This function was added so that the suffix (search below for ${cfgdir}) is
2109# only set to "/${CMAKE_CFG_INTDIR}" when ${CMAKE_CFG_INTDIR} is not going to
2110# be "." (multi-configuration build systems like Visual Studio and Xcode...)
2111#
2112function(_ep_get_configuration_subdir_suffix suffix_var)
2113  set(suffix "")
2114  get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
2115  if(_isMultiConfig)
2116    set(suffix "/${CMAKE_CFG_INTDIR}")
2117  endif()
2118  set(${suffix_var} "${suffix}" PARENT_SCOPE)
2119endfunction()
2120
2121
2122function(_ep_get_step_stampfile name step stampfile_var)
2123  ExternalProject_Get_Property(${name} stamp_dir)
2124
2125  _ep_get_configuration_subdir_suffix(cfgdir)
2126  set(stampfile "${stamp_dir}${cfgdir}/${name}-${step}")
2127
2128  set(${stampfile_var} "${stampfile}" PARENT_SCOPE)
2129endfunction()
2130
2131
2132function(_ep_get_complete_stampfile name stampfile_var)
2133  set(cmf_dir ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles)
2134  _ep_get_configuration_subdir_suffix(cfgdir)
2135  set(stampfile "${cmf_dir}${cfgdir}/${name}-complete")
2136
2137  set(${stampfile_var} ${stampfile} PARENT_SCOPE)
2138endfunction()
2139
2140
2141function(_ep_step_add_target name step no_deps)
2142  if(TARGET ${name}-${step})
2143    return()
2144  endif()
2145  get_property(cmp0114 TARGET ${name} PROPERTY _EP_CMP0114)
2146  _ep_get_step_stampfile(${name} ${step} stamp_file)
2147  cmake_policy(PUSH)
2148  if(cmp0114 STREQUAL "NEW")
2149    # To implement CMP0114 NEW behavior with Makefile generators,
2150    # we need CMP0113 NEW behavior.
2151    cmake_policy(SET CMP0113 NEW)
2152  endif()
2153  add_custom_target(${name}-${step}
2154    DEPENDS ${stamp_file})
2155  cmake_policy(POP)
2156  set_property(TARGET ${name}-${step} PROPERTY _EP_IS_EXTERNAL_PROJECT_STEP 1)
2157  set_property(TARGET ${name}-${step} PROPERTY LABELS ${name})
2158  set_property(TARGET ${name}-${step} PROPERTY FOLDER "ExternalProjectTargets/${name}")
2159
2160  if(cmp0114 STREQUAL "NEW")
2161    # Add target-level dependencies for the step.
2162    get_property(exclude_from_main TARGET ${name} PROPERTY _EP_${step}_EXCLUDE_FROM_MAIN)
2163    if(NOT exclude_from_main)
2164      add_dependencies(${name} ${name}-${step})
2165    endif()
2166    _ep_step_add_target_dependencies(${name} ${step} ${step})
2167    _ep_step_add_target_dependents(${name} ${step} ${step})
2168
2169    get_property(independent TARGET ${name} PROPERTY _EP_${step}_INDEPENDENT)
2170  else()
2171    if(no_deps AND "${step}" MATCHES "^(configure|build|install|test)$")
2172      message(AUTHOR_WARNING "Using NO_DEPENDS for \"${step}\" step  might break parallel builds")
2173    endif()
2174    set(independent ${no_deps})
2175  endif()
2176
2177  # Depend on other external projects (target-level).
2178  if(NOT independent)
2179    get_property(deps TARGET ${name} PROPERTY _EP_DEPENDS)
2180    foreach(arg IN LISTS deps)
2181      add_dependencies(${name}-${step} ${arg})
2182    endforeach()
2183  endif()
2184endfunction()
2185
2186
2187function(_ep_step_add_target_dependencies name step node)
2188  get_property(dependees TARGET ${name} PROPERTY _EP_${node}_INTERNAL_DEPENDEES)
2189  list(REMOVE_DUPLICATES dependees)
2190  foreach(dependee IN LISTS dependees)
2191    get_property(exclude_from_main TARGET ${name} PROPERTY _EP_${step}_EXCLUDE_FROM_MAIN)
2192    get_property(dependee_dependers TARGET ${name} PROPERTY _EP_${dependee}_INTERNAL_DEPENDERS)
2193    if(exclude_from_main OR dependee_dependers MATCHES ";")
2194      # The step on which our step target depends itself has
2195      # dependents in multiple targes.  It needs a step target too
2196      # so that there is a unique place for its custom command.
2197      _ep_step_add_target("${name}" "${dependee}" "FALSE")
2198    endif()
2199
2200    if(TARGET ${name}-${dependee})
2201      add_dependencies(${name}-${step} ${name}-${dependee})
2202    else()
2203      _ep_step_add_target_dependencies(${name} ${step} ${dependee})
2204    endif()
2205  endforeach()
2206endfunction()
2207
2208
2209function(_ep_step_add_target_dependents name step node)
2210  get_property(dependers TARGET ${name} PROPERTY _EP_${node}_INTERNAL_DEPENDERS)
2211  list(REMOVE_DUPLICATES dependers)
2212  foreach(depender IN LISTS dependers)
2213    if(TARGET ${name}-${depender})
2214      add_dependencies(${name}-${depender} ${name}-${step})
2215    else()
2216      _ep_step_add_target_dependents(${name} ${step} ${depender})
2217    endif()
2218  endforeach()
2219endfunction()
2220
2221
2222function(ExternalProject_Add_StepTargets name)
2223  get_property(cmp0114 TARGET ${name} PROPERTY _EP_CMP0114)
2224  set(steps ${ARGN})
2225  if(ARGC GREATER 1 AND "${ARGV1}" STREQUAL "NO_DEPENDS")
2226    set(no_deps 1)
2227    list(REMOVE_AT steps 0)
2228  else()
2229    set(no_deps 0)
2230  endif()
2231  if(cmp0114 STREQUAL "NEW")
2232    if(no_deps)
2233      message(FATAL_ERROR
2234        "The 'NO_DEPENDS' option is no longer allowed.  "
2235        "It has been superseded by the per-step 'INDEPENDENT' option.  "
2236        "See policy CMP0114."
2237        )
2238    endif()
2239  elseif(cmp0114 STREQUAL "")
2240    cmake_policy(GET_WARNING CMP0114 _cmp0114_warning)
2241    string(APPEND _cmp0114_warning "\n"
2242      "ExternalProject target '${name}' would depend on the targets for "
2243      "step(s) '${steps}' under policy CMP0114, but this is being left out "
2244      "for compatibility since the policy is not set."
2245      )
2246    if(no_deps)
2247      string(APPEND _cmp0114_warning
2248        "  Also, the NO_DEPENDS option is deprecated in favor of policy CMP0114."
2249        )
2250    endif()
2251    message(AUTHOR_WARNING "${_cmp0114_warning}")
2252  endif()
2253  foreach(step ${steps})
2254    _ep_step_add_target("${name}" "${step}" "${no_deps}")
2255  endforeach()
2256endfunction()
2257
2258
2259function(ExternalProject_Add_Step name step)
2260  get_property(cmp0114 TARGET ${name} PROPERTY _EP_CMP0114)
2261  _ep_get_complete_stampfile(${name} complete_stamp_file)
2262  _ep_get_step_stampfile(${name} ${step} stamp_file)
2263
2264  set(keywords
2265    COMMAND
2266    COMMENT
2267    DEPENDEES
2268    DEPENDERS
2269    DEPENDS
2270    INDEPENDENT
2271    BYPRODUCTS
2272    ALWAYS
2273    EXCLUDE_FROM_MAIN
2274    WORKING_DIRECTORY
2275    LOG
2276    USES_TERMINAL
2277  )
2278  _ep_parse_arguments(ExternalProject_Add_Step "${keywords}"
2279                      ${name} _EP_${step}_ "${ARGN}")
2280
2281  get_property(independent TARGET ${name} PROPERTY _EP_${step}_INDEPENDENT)
2282  if(independent STREQUAL "")
2283    set(independent FALSE)
2284    set_property(TARGET ${name} PROPERTY _EP_${step}_INDEPENDENT "${independent}")
2285  endif()
2286
2287  get_property(exclude_from_main TARGET ${name} PROPERTY _EP_${step}_EXCLUDE_FROM_MAIN)
2288  if(NOT exclude_from_main)
2289    add_custom_command(APPEND
2290      OUTPUT ${complete_stamp_file}
2291      DEPENDS ${stamp_file}
2292      )
2293  endif()
2294
2295  # Steps depending on this step.
2296  get_property(dependers TARGET ${name} PROPERTY _EP_${step}_DEPENDERS)
2297  set_property(TARGET ${name} APPEND PROPERTY _EP_${step}_INTERNAL_DEPENDERS ${dependers})
2298  foreach(depender IN LISTS dependers)
2299    set_property(TARGET ${name} APPEND PROPERTY _EP_${depender}_INTERNAL_DEPENDEES ${step})
2300    _ep_get_step_stampfile(${name} ${depender} depender_stamp_file)
2301    add_custom_command(APPEND
2302      OUTPUT ${depender_stamp_file}
2303      DEPENDS ${stamp_file}
2304      )
2305    if(cmp0114 STREQUAL "NEW" AND NOT independent)
2306      get_property(dep_independent TARGET ${name} PROPERTY _EP_${depender}_INDEPENDENT)
2307      if(dep_independent)
2308        message(FATAL_ERROR "ExternalProject '${name}' step '${depender}' is marked INDEPENDENT "
2309          "but depends on step '${step}' that is not marked INDEPENDENT.")
2310      endif()
2311    endif()
2312  endforeach()
2313
2314  # Dependencies on files.
2315  get_property(depends TARGET ${name} PROPERTY _EP_${step}_DEPENDS)
2316
2317  # Byproducts of the step.
2318  get_property(byproducts TARGET ${name} PROPERTY _EP_${step}_BYPRODUCTS)
2319
2320  # Dependencies on steps.
2321  get_property(dependees TARGET ${name} PROPERTY _EP_${step}_DEPENDEES)
2322  set_property(TARGET ${name} APPEND PROPERTY _EP_${step}_INTERNAL_DEPENDEES ${dependees})
2323  foreach(dependee IN LISTS dependees)
2324    set_property(TARGET ${name} APPEND PROPERTY _EP_${dependee}_INTERNAL_DEPENDERS ${step})
2325    _ep_get_step_stampfile(${name} ${dependee} dependee_stamp_file)
2326    list(APPEND depends ${dependee_stamp_file})
2327    if(cmp0114 STREQUAL "NEW" AND independent)
2328      get_property(dep_independent TARGET ${name} PROPERTY _EP_${dependee}_INDEPENDENT)
2329      if(NOT dep_independent)
2330        message(FATAL_ERROR "ExternalProject '${name}' step '${step}' is marked INDEPENDENT "
2331          "but depends on step '${dependee}' that is not marked INDEPENDENT.")
2332      endif()
2333    endif()
2334  endforeach()
2335
2336  # The command to run.
2337  get_property(command TARGET ${name} PROPERTY _EP_${step}_COMMAND)
2338  if(command)
2339    set(comment "Performing ${step} step for '${name}'")
2340  else()
2341    set(comment "No ${step} step for '${name}'")
2342  endif()
2343  get_property(work_dir TARGET ${name} PROPERTY _EP_${step}_WORKING_DIRECTORY)
2344
2345  # Replace list separators.
2346  get_property(sep TARGET ${name} PROPERTY _EP_LIST_SEPARATOR)
2347  if(sep AND command)
2348    string(REPLACE "${sep}" "\\;" command "${command}")
2349  endif()
2350
2351  # Replace location tags.
2352  _ep_replace_location_tags(${name} comment command work_dir byproducts)
2353
2354  # Custom comment?
2355  get_property(comment_set TARGET ${name} PROPERTY _EP_${step}_COMMENT SET)
2356  if(comment_set)
2357    get_property(comment TARGET ${name} PROPERTY _EP_${step}_COMMENT)
2358  endif()
2359
2360  # Uses terminal?
2361  get_property(uses_terminal TARGET ${name} PROPERTY _EP_${step}_USES_TERMINAL)
2362  if(uses_terminal)
2363    set(uses_terminal USES_TERMINAL)
2364  else()
2365    set(uses_terminal "")
2366  endif()
2367
2368  # Run every time?
2369  get_property(always TARGET ${name} PROPERTY _EP_${step}_ALWAYS)
2370  if(always)
2371    set_property(SOURCE ${stamp_file} PROPERTY SYMBOLIC 1)
2372    set(touch)
2373    # Remove any existing stamp in case the option changed in an existing tree.
2374    get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
2375    if(_isMultiConfig)
2376      foreach(cfg ${CMAKE_CONFIGURATION_TYPES})
2377        string(REPLACE "/${CMAKE_CFG_INTDIR}" "/${cfg}" stamp_file_config "${stamp_file}")
2378        file(REMOVE ${stamp_file_config})
2379      endforeach()
2380    else()
2381      file(REMOVE ${stamp_file})
2382    endif()
2383  else()
2384    set(touch ${CMAKE_COMMAND} -E touch ${stamp_file})
2385  endif()
2386
2387  # Wrap with log script?
2388  get_property(log TARGET ${name} PROPERTY _EP_${step}_LOG)
2389  if(command AND log)
2390    _ep_write_log_script(${name} ${step} command)
2391  endif()
2392
2393  if("${command}" STREQUAL "")
2394    # Some generators (i.e. Xcode) will not generate a file level target
2395    # if no command is set, and therefore the dependencies on this
2396    # target will be broken.
2397    # The empty command is replaced by an echo command here in order to
2398    # avoid this issue.
2399    set(command ${CMAKE_COMMAND} -E echo_append)
2400  endif()
2401
2402  set(__cmdQuoted)
2403  foreach(__item IN LISTS command)
2404    string(APPEND __cmdQuoted " [==[${__item}]==]")
2405  endforeach()
2406  cmake_language(EVAL CODE "
2407    add_custom_command(
2408      OUTPUT \${stamp_file}
2409      BYPRODUCTS \${byproducts}
2410      COMMENT \${comment}
2411      COMMAND ${__cmdQuoted}
2412      COMMAND \${touch}
2413      DEPENDS \${depends}
2414      WORKING_DIRECTORY \${work_dir}
2415      VERBATIM
2416      ${uses_terminal}
2417    )"
2418  )
2419  set_property(TARGET ${name} APPEND PROPERTY _EP_STEPS ${step})
2420
2421  # Add custom "step target"?
2422  get_property(step_targets TARGET ${name} PROPERTY _EP_STEP_TARGETS)
2423  if(NOT step_targets)
2424    get_property(step_targets DIRECTORY PROPERTY EP_STEP_TARGETS)
2425  endif()
2426  foreach(st ${step_targets})
2427    if("${st}" STREQUAL "${step}")
2428      _ep_step_add_target("${name}" "${step}" "FALSE")
2429      break()
2430    endif()
2431  endforeach()
2432
2433  get_property(independent_step_targets TARGET ${name} PROPERTY _EP_INDEPENDENT_STEP_TARGETS)
2434  if(NOT independent_step_targets)
2435    get_property(independent_step_targets DIRECTORY PROPERTY EP_INDEPENDENT_STEP_TARGETS)
2436  endif()
2437  if(cmp0114 STREQUAL "NEW")
2438    if(independent_step_targets)
2439      message(FATAL_ERROR
2440        "ExternalProject '${name}' option 'INDEPENDENT_STEP_TARGETS' is set to\n"
2441        "  ${independent_step_targets}\n"
2442        "but the option is no longer allowed.  "
2443        "It has been superseded by the per-step 'INDEPENDENT' option.  "
2444        "See policy CMP0114."
2445        )
2446    endif()
2447  else()
2448    if(independent_step_targets AND cmp0114 STREQUAL "")
2449      get_property(warned TARGET ${name} PROPERTY _EP_CMP0114_WARNED_INDEPENDENT_STEP_TARGETS)
2450      if(NOT warned)
2451        set_property(TARGET ${name} PROPERTY _EP_CMP0114_WARNED_INDEPENDENT_STEP_TARGETS 1)
2452        cmake_policy(GET_WARNING CMP0114 _cmp0114_warning)
2453        string(APPEND _cmp0114_warning "\n"
2454          "ExternalProject '${name}' option INDEPENDENT_STEP_TARGETS is set to\n"
2455          "  ${independent_step_targets}\n"
2456          "but the option is deprecated in favor of policy CMP0114."
2457          )
2458        message(AUTHOR_WARNING "${_cmp0114_warning}")
2459      endif()
2460    endif()
2461    foreach(st ${independent_step_targets})
2462      if("${st}" STREQUAL "${step}")
2463        _ep_step_add_target("${name}" "${step}" "TRUE")
2464        break()
2465      endif()
2466    endforeach()
2467  endif()
2468endfunction()
2469
2470
2471function(ExternalProject_Add_StepDependencies name step)
2472  set(dependencies ${ARGN})
2473
2474  # Sanity checks on "name" and "step".
2475  if(NOT TARGET ${name})
2476    message(FATAL_ERROR "Cannot find target \"${name}\". Perhaps it has not yet been created using ExternalProject_Add.")
2477  endif()
2478
2479  get_property(type TARGET ${name} PROPERTY TYPE)
2480  if(NOT type STREQUAL "UTILITY")
2481    message(FATAL_ERROR "Target \"${name}\" was not generated by ExternalProject_Add.")
2482  endif()
2483
2484  get_property(is_ep TARGET ${name} PROPERTY _EP_IS_EXTERNAL_PROJECT)
2485  if(NOT is_ep)
2486    message(FATAL_ERROR "Target \"${name}\" was not generated by ExternalProject_Add.")
2487  endif()
2488
2489  get_property(steps TARGET ${name} PROPERTY _EP_STEPS)
2490  list(FIND steps ${step} is_step)
2491  if(is_step LESS 0)
2492    message(FATAL_ERROR "External project \"${name}\" does not have a step \"${step}\".")
2493  endif()
2494
2495  if(TARGET ${name}-${step})
2496    get_property(type TARGET ${name}-${step} PROPERTY TYPE)
2497    if(NOT type STREQUAL "UTILITY")
2498      message(FATAL_ERROR "Target \"${name}-${step}\" was not generated by ExternalProject_Add_StepTargets.")
2499    endif()
2500    get_property(is_ep_step TARGET ${name}-${step} PROPERTY _EP_IS_EXTERNAL_PROJECT_STEP)
2501    if(NOT is_ep_step)
2502      message(FATAL_ERROR "Target \"${name}-${step}\" was not generated by ExternalProject_Add_StepTargets.")
2503    endif()
2504  endif()
2505
2506  # Always add file-level dependency, but add target-level dependency
2507  # only if the target exists for that step.
2508  _ep_get_step_stampfile(${name} ${step} stamp_file)
2509  foreach(dep ${dependencies})
2510    add_custom_command(APPEND
2511      OUTPUT ${stamp_file}
2512      DEPENDS ${dep})
2513    if(TARGET ${name}-${step})
2514      foreach(dep ${dependencies})
2515        add_dependencies(${name}-${step} ${dep})
2516      endforeach()
2517    endif()
2518  endforeach()
2519
2520endfunction()
2521
2522
2523function(_ep_add_mkdir_command name)
2524  ExternalProject_Get_Property(${name}
2525    source_dir binary_dir install_dir stamp_dir download_dir tmp_dir log_dir)
2526
2527  _ep_get_configuration_subdir_suffix(cfgdir)
2528
2529  ExternalProject_Add_Step(${name} mkdir
2530    INDEPENDENT TRUE
2531    COMMENT "Creating directories for '${name}'"
2532    COMMAND ${CMAKE_COMMAND} -E make_directory ${source_dir}
2533    COMMAND ${CMAKE_COMMAND} -E make_directory ${binary_dir}
2534    COMMAND ${CMAKE_COMMAND} -E make_directory ${install_dir}
2535    COMMAND ${CMAKE_COMMAND} -E make_directory ${tmp_dir}
2536    COMMAND ${CMAKE_COMMAND} -E make_directory ${stamp_dir}${cfgdir}
2537    COMMAND ${CMAKE_COMMAND} -E make_directory ${download_dir}
2538    COMMAND ${CMAKE_COMMAND} -E make_directory ${log_dir}
2539    )
2540endfunction()
2541
2542
2543function(_ep_is_dir_empty dir empty_var)
2544  file(GLOB gr "${dir}/*")
2545  if("${gr}" STREQUAL "")
2546    set(${empty_var} 1 PARENT_SCOPE)
2547  else()
2548    set(${empty_var} 0 PARENT_SCOPE)
2549  endif()
2550endfunction()
2551
2552function(_ep_get_git_submodules_recurse git_submodules_recurse)
2553  # Checks for GIT_SUBMODULES_RECURSE property
2554  # Default is ON, which sets git_submodules_recurse output variable to "--recursive"
2555  # Otherwise, the output variable is set to an empty value ""
2556  get_property(git_submodules_recurse_set TARGET ${name} PROPERTY _EP_GIT_SUBMODULES_RECURSE SET)
2557  if(NOT git_submodules_recurse_set)
2558    set(recurseFlag "--recursive")
2559  else()
2560    get_property(git_submodules_recurse_value TARGET ${name} PROPERTY _EP_GIT_SUBMODULES_RECURSE)
2561    if(git_submodules_recurse_value)
2562      set(recurseFlag "--recursive")
2563    else()
2564      set(recurseFlag "")
2565    endif()
2566  endif()
2567  set(${git_submodules_recurse} "${recurseFlag}" PARENT_SCOPE)
2568
2569  # The git submodule update '--recursive' flag requires git >= v1.6.5
2570  if(recurseFlag AND GIT_VERSION_STRING VERSION_LESS 1.6.5)
2571    message(FATAL_ERROR "error: git version 1.6.5 or later required for --recursive flag with 'git submodule ...': GIT_VERSION_STRING='${GIT_VERSION_STRING}'")
2572  endif()
2573endfunction()
2574
2575
2576function(_ep_add_download_command name)
2577  ExternalProject_Get_Property(${name} source_dir stamp_dir download_dir tmp_dir)
2578
2579  get_property(cmd_set TARGET ${name} PROPERTY _EP_DOWNLOAD_COMMAND SET)
2580  get_property(cmd TARGET ${name} PROPERTY _EP_DOWNLOAD_COMMAND)
2581  get_property(cvs_repository TARGET ${name} PROPERTY _EP_CVS_REPOSITORY)
2582  get_property(svn_repository TARGET ${name} PROPERTY _EP_SVN_REPOSITORY)
2583  get_property(git_repository TARGET ${name} PROPERTY _EP_GIT_REPOSITORY)
2584  get_property(hg_repository  TARGET ${name} PROPERTY _EP_HG_REPOSITORY )
2585  get_property(url TARGET ${name} PROPERTY _EP_URL)
2586  get_property(fname TARGET ${name} PROPERTY _EP_DOWNLOAD_NAME)
2587
2588  # TODO: Perhaps file:// should be copied to download dir before extraction.
2589  string(REGEX REPLACE "file://" "" url "${url}")
2590
2591  set(depends)
2592  set(comment)
2593  set(work_dir)
2594
2595  if(cmd_set)
2596    set(work_dir ${download_dir})
2597  elseif(cvs_repository)
2598    find_package(CVS QUIET)
2599    if(NOT CVS_EXECUTABLE)
2600      message(FATAL_ERROR "error: could not find cvs for checkout of ${name}")
2601    endif()
2602
2603    get_target_property(cvs_module ${name} _EP_CVS_MODULE)
2604    if(NOT cvs_module)
2605      message(FATAL_ERROR "error: no CVS_MODULE")
2606    endif()
2607
2608    get_property(cvs_tag TARGET ${name} PROPERTY _EP_CVS_TAG)
2609
2610    set(repository ${cvs_repository})
2611    set(module ${cvs_module})
2612    set(tag ${cvs_tag})
2613    configure_file(
2614      "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
2615      "${stamp_dir}/${name}-cvsinfo.txt"
2616      @ONLY
2617      )
2618
2619    get_filename_component(src_name "${source_dir}" NAME)
2620    get_filename_component(work_dir "${source_dir}" PATH)
2621    set(comment "Performing download step (CVS checkout) for '${name}'")
2622    set(cmd ${CVS_EXECUTABLE} -d ${cvs_repository} -q co ${cvs_tag} -d ${src_name} ${cvs_module})
2623    list(APPEND depends ${stamp_dir}/${name}-cvsinfo.txt)
2624  elseif(svn_repository)
2625    find_package(Subversion QUIET)
2626    if(NOT Subversion_SVN_EXECUTABLE)
2627      message(FATAL_ERROR "error: could not find svn for checkout of ${name}")
2628    endif()
2629
2630    get_property(svn_revision TARGET ${name} PROPERTY _EP_SVN_REVISION)
2631    get_property(svn_username TARGET ${name} PROPERTY _EP_SVN_USERNAME)
2632    get_property(svn_password TARGET ${name} PROPERTY _EP_SVN_PASSWORD)
2633    get_property(svn_trust_cert TARGET ${name} PROPERTY _EP_SVN_TRUST_CERT)
2634
2635    set(repository "${svn_repository} user=${svn_username} password=${svn_password}")
2636    set(module)
2637    set(tag ${svn_revision})
2638    configure_file(
2639      "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
2640      "${stamp_dir}/${name}-svninfo.txt"
2641      @ONLY
2642      )
2643
2644    get_filename_component(src_name "${source_dir}" NAME)
2645    get_filename_component(work_dir "${source_dir}" PATH)
2646    set(comment "Performing download step (SVN checkout) for '${name}'")
2647    set(svn_user_pw_args "")
2648    if(DEFINED svn_username)
2649      set(svn_user_pw_args ${svn_user_pw_args} "--username=${svn_username}")
2650    endif()
2651    if(DEFINED svn_password)
2652      set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}")
2653    endif()
2654    if(svn_trust_cert)
2655      set(svn_trust_cert_args --trust-server-cert)
2656    endif()
2657    set(cmd ${Subversion_SVN_EXECUTABLE} co ${svn_repository} ${svn_revision}
2658      --non-interactive ${svn_trust_cert_args} ${svn_user_pw_args} ${src_name})
2659    list(APPEND depends ${stamp_dir}/${name}-svninfo.txt)
2660  elseif(git_repository)
2661    # FetchContent gives us these directly, so don't try to recompute them
2662    if(NOT GIT_EXECUTABLE OR NOT GIT_VERSION_STRING)
2663      unset(CMAKE_MODULE_PATH) # Use CMake builtin find module
2664      find_package(Git QUIET)
2665      if(NOT GIT_EXECUTABLE)
2666        message(FATAL_ERROR "error: could not find git for clone of ${name}")
2667      endif()
2668    endif()
2669
2670    _ep_get_git_submodules_recurse(git_submodules_recurse)
2671
2672    get_property(git_tag TARGET ${name} PROPERTY _EP_GIT_TAG)
2673    if(NOT git_tag)
2674      set(git_tag "master")
2675    endif()
2676
2677    set(git_init_submodules TRUE)
2678    get_property(git_submodules_set TARGET ${name} PROPERTY _EP_GIT_SUBMODULES SET)
2679    if(git_submodules_set)
2680      get_property(git_submodules TARGET ${name} PROPERTY _EP_GIT_SUBMODULES)
2681      if(git_submodules  STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW")
2682        set(git_init_submodules FALSE)
2683      endif()
2684    endif()
2685
2686    get_property(git_remote_name TARGET ${name} PROPERTY _EP_GIT_REMOTE_NAME)
2687    if(NOT git_remote_name)
2688      set(git_remote_name "origin")
2689    endif()
2690
2691    get_property(tls_verify TARGET ${name} PROPERTY _EP_TLS_VERIFY)
2692    if("x${tls_verify}" STREQUAL "x" AND DEFINED CMAKE_TLS_VERIFY)
2693      set(tls_verify "${CMAKE_TLS_VERIFY}")
2694    endif()
2695    get_property(git_shallow TARGET ${name} PROPERTY _EP_GIT_SHALLOW)
2696    get_property(git_progress TARGET ${name} PROPERTY _EP_GIT_PROGRESS)
2697    get_property(git_config TARGET ${name} PROPERTY _EP_GIT_CONFIG)
2698
2699    # If git supports it, make checkouts quiet when checking out a git hash.
2700    # This avoids the very noisy detached head message.
2701    if(GIT_VERSION_STRING VERSION_GREATER_EQUAL 1.7.7)
2702      list(PREPEND git_config advice.detachedHead=false)
2703    endif()
2704
2705    # For the download step, and the git clone operation, only the repository
2706    # should be recorded in a configured RepositoryInfo file. If the repo
2707    # changes, the clone script should be run again. But if only the tag
2708    # changes, avoid running the clone script again. Let the 'always' running
2709    # update step checkout the new tag.
2710    #
2711    set(repository ${git_repository})
2712    set(module)
2713    set(tag ${git_remote_name})
2714    configure_file(
2715      "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
2716      "${stamp_dir}/${name}-gitinfo.txt"
2717      @ONLY
2718      )
2719
2720    get_filename_component(src_name "${source_dir}" NAME)
2721    get_filename_component(work_dir "${source_dir}" PATH)
2722
2723    # Since git clone doesn't succeed if the non-empty source_dir exists,
2724    # create a cmake script to invoke as download command.
2725    # The script will delete the source directory and then call git clone.
2726    #
2727    _ep_write_gitclone_script(${tmp_dir}/${name}-gitclone.cmake ${source_dir}
2728      ${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}
2729      ${stamp_dir}/${name}-gitinfo.txt ${stamp_dir}/${name}-gitclone-lastrun.txt "${tls_verify}"
2730      )
2731    set(comment "Performing download step (git clone) for '${name}'")
2732    set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-gitclone.cmake)
2733    list(APPEND depends ${stamp_dir}/${name}-gitinfo.txt)
2734  elseif(hg_repository)
2735    find_package(Hg QUIET)
2736    if(NOT HG_EXECUTABLE)
2737      message(FATAL_ERROR "error: could not find hg for clone of ${name}")
2738    endif()
2739
2740    get_property(hg_tag TARGET ${name} PROPERTY _EP_HG_TAG)
2741    if(NOT hg_tag)
2742      set(hg_tag "tip")
2743    endif()
2744
2745    # For the download step, and the hg clone operation, only the repository
2746    # should be recorded in a configured RepositoryInfo file. If the repo
2747    # changes, the clone script should be run again. But if only the tag
2748    # changes, avoid running the clone script again. Let the 'always' running
2749    # update step checkout the new tag.
2750    #
2751    set(repository ${hg_repository})
2752    set(module)
2753    set(tag)
2754    configure_file(
2755      "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
2756      "${stamp_dir}/${name}-hginfo.txt"
2757      @ONLY
2758      )
2759
2760    get_filename_component(src_name "${source_dir}" NAME)
2761    get_filename_component(work_dir "${source_dir}" PATH)
2762
2763    # Since hg clone doesn't succeed if the non-empty source_dir exists,
2764    # create a cmake script to invoke as download command.
2765    # The script will delete the source directory and then call hg clone.
2766    #
2767    _ep_write_hgclone_script(${tmp_dir}/${name}-hgclone.cmake ${source_dir}
2768      ${HG_EXECUTABLE} ${hg_repository} ${hg_tag} ${src_name} ${work_dir}
2769      ${stamp_dir}/${name}-hginfo.txt ${stamp_dir}/${name}-hgclone-lastrun.txt
2770      )
2771    set(comment "Performing download step (hg clone) for '${name}'")
2772    set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-hgclone.cmake)
2773    list(APPEND depends ${stamp_dir}/${name}-hginfo.txt)
2774  elseif(url)
2775    get_filename_component(work_dir "${source_dir}" PATH)
2776    get_property(hash TARGET ${name} PROPERTY _EP_URL_HASH)
2777    _ep_get_hash_regex(_ep_hash_regex)
2778    if(hash AND NOT "${hash}" MATCHES "${_ep_hash_regex}")
2779      _ep_get_hash_algos(_ep_hash_algos)
2780      list(JOIN _ep_hash_algos "|" _ep_hash_algos)
2781      message(FATAL_ERROR "URL_HASH is set to\n  ${hash}\n"
2782        "but must be ALGO=value where ALGO is\n  ${_ep_hash_algos}\n"
2783        "and value is a hex string.")
2784    endif()
2785    get_property(md5 TARGET ${name} PROPERTY _EP_URL_MD5)
2786    if(md5 AND NOT "MD5=${md5}" MATCHES "${_ep_hash_regex}")
2787      message(FATAL_ERROR "URL_MD5 is set to\n  ${md5}\nbut must be a hex string.")
2788    endif()
2789    if(md5 AND NOT hash)
2790      set(hash "MD5=${md5}")
2791    endif()
2792    set(repository "external project URL")
2793    set(module "${url}")
2794    set(tag "${hash}")
2795    configure_file(
2796      "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
2797      "${stamp_dir}/${name}-urlinfo.txt"
2798      @ONLY
2799      )
2800    list(APPEND depends ${stamp_dir}/${name}-urlinfo.txt)
2801
2802    list(LENGTH url url_list_length)
2803    if(NOT "${url_list_length}" STREQUAL "1")
2804      foreach(entry ${url})
2805        if(NOT "${entry}" MATCHES "^[a-z]+://")
2806          message(FATAL_ERROR "At least one entry of URL is a path (invalid in a list)")
2807        endif()
2808      endforeach()
2809      if("x${fname}" STREQUAL "x")
2810        list(GET url 0 fname)
2811      endif()
2812    endif()
2813
2814    if(IS_DIRECTORY "${url}")
2815      get_filename_component(abs_dir "${url}" ABSOLUTE)
2816      set(comment "Performing download step (DIR copy) for '${name}'")
2817      set(cmd   ${CMAKE_COMMAND} -E rm -rf ${source_dir}
2818        COMMAND ${CMAKE_COMMAND} -E copy_directory ${abs_dir} ${source_dir})
2819    else()
2820      get_property(no_extract TARGET "${name}" PROPERTY _EP_DOWNLOAD_NO_EXTRACT)
2821      if("${url}" MATCHES "^[a-z]+://")
2822        # TODO: Should download and extraction be different steps?
2823        if("x${fname}" STREQUAL "x")
2824          set(fname "${url}")
2825        endif()
2826        if("${fname}" MATCHES [[([^/\?#]+(\.|=)(7z|tar|tar\.bz2|tar\.gz|tar\.xz|tbz2|tgz|txz|zip))([/?#].*)?$]])
2827          set(fname "${CMAKE_MATCH_1}")
2828        elseif(no_extract)
2829          get_filename_component(fname "${fname}" NAME)
2830        else()
2831          # Fall back to a default file name.  The actual file name does not
2832          # matter because it is used only internally and our extraction tool
2833          # inspects the file content directly.  If it turns out the wrong URL
2834          # was given that will be revealed during the build which is an easier
2835          # place for users to diagnose than an error here anyway.
2836          set(fname "archive.tar")
2837        endif()
2838        string(REPLACE ";" "-" fname "${fname}")
2839        set(file ${download_dir}/${fname})
2840        get_property(timeout TARGET ${name} PROPERTY _EP_TIMEOUT)
2841        get_property(inactivity_timeout TARGET ${name} PROPERTY _EP_INACTIVITY_TIMEOUT)
2842        get_property(no_progress TARGET ${name} PROPERTY _EP_DOWNLOAD_NO_PROGRESS)
2843        get_property(tls_verify TARGET ${name} PROPERTY _EP_TLS_VERIFY)
2844        get_property(tls_cainfo TARGET ${name} PROPERTY _EP_TLS_CAINFO)
2845        get_property(netrc TARGET ${name} PROPERTY _EP_NETRC)
2846        get_property(netrc_file TARGET ${name} PROPERTY _EP_NETRC_FILE)
2847        get_property(http_username TARGET ${name} PROPERTY _EP_HTTP_USERNAME)
2848        get_property(http_password TARGET ${name} PROPERTY _EP_HTTP_PASSWORD)
2849        get_property(http_headers TARGET ${name} PROPERTY _EP_HTTP_HEADER)
2850        set(download_script "${stamp_dir}/download-${name}.cmake")
2851        _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}")
2852        set(cmd ${CMAKE_COMMAND} -P "${download_script}"
2853          COMMAND)
2854        if (no_extract)
2855          set(steps "download and verify")
2856        else ()
2857          set(steps "download, verify and extract")
2858        endif ()
2859        set(comment "Performing download step (${steps}) for '${name}'")
2860        file(WRITE "${stamp_dir}/verify-${name}.cmake" "") # already verified by 'download_script'
2861      else()
2862        set(file "${url}")
2863        if (no_extract)
2864          set(steps "verify")
2865        else ()
2866          set(steps "verify and extract")
2867        endif ()
2868        set(comment "Performing download step (${steps}) for '${name}'")
2869        _ep_write_verifyfile_script("${stamp_dir}/verify-${name}.cmake" "${file}" "${hash}")
2870      endif()
2871      list(APPEND cmd ${CMAKE_COMMAND} -P ${stamp_dir}/verify-${name}.cmake)
2872      if (NOT no_extract)
2873        _ep_write_extractfile_script("${stamp_dir}/extract-${name}.cmake" "${name}" "${file}" "${source_dir}")
2874        list(APPEND cmd COMMAND ${CMAKE_COMMAND} -P ${stamp_dir}/extract-${name}.cmake)
2875      else ()
2876        set_property(TARGET ${name} PROPERTY _EP_DOWNLOADED_FILE ${file})
2877      endif ()
2878    endif()
2879  else()
2880    _ep_is_dir_empty("${source_dir}" empty)
2881    if(${empty})
2882      message(SEND_ERROR
2883        "No download info given for '${name}' and its source directory:\n"
2884        " ${source_dir}\n"
2885        "is not an existing non-empty directory.  Please specify one of:\n"
2886        " * SOURCE_DIR with an existing non-empty directory\n"
2887        " * DOWNLOAD_COMMAND\n"
2888        " * URL\n"
2889        " * GIT_REPOSITORY\n"
2890        " * SVN_REPOSITORY\n"
2891        " * HG_REPOSITORY\n"
2892        " * CVS_REPOSITORY and CVS_MODULE"
2893        )
2894    endif()
2895  endif()
2896
2897  get_property(log TARGET ${name} PROPERTY _EP_LOG_DOWNLOAD)
2898  if(log)
2899    set(log LOG 1)
2900  else()
2901    set(log "")
2902  endif()
2903
2904  get_property(uses_terminal TARGET ${name} PROPERTY
2905    _EP_USES_TERMINAL_DOWNLOAD)
2906  if(uses_terminal)
2907    set(uses_terminal USES_TERMINAL 1)
2908  else()
2909    set(uses_terminal "")
2910  endif()
2911
2912  set(__cmdQuoted)
2913  foreach(__item IN LISTS cmd)
2914    string(APPEND __cmdQuoted " [==[${__item}]==]")
2915  endforeach()
2916  cmake_language(EVAL CODE "
2917    ExternalProject_Add_Step(\${name} download
2918      INDEPENDENT TRUE
2919      COMMENT \${comment}
2920      COMMAND ${__cmdQuoted}
2921      WORKING_DIRECTORY \${work_dir}
2922      DEPENDS \${depends}
2923      DEPENDEES mkdir
2924      ${log}
2925      ${uses_terminal}
2926      )"
2927  )
2928endfunction()
2929
2930function(_ep_get_update_disconnected var name)
2931  get_property(update_disconnected_set TARGET ${name} PROPERTY _EP_UPDATE_DISCONNECTED SET)
2932  if(update_disconnected_set)
2933    get_property(update_disconnected TARGET ${name} PROPERTY _EP_UPDATE_DISCONNECTED)
2934  else()
2935    get_property(update_disconnected DIRECTORY PROPERTY EP_UPDATE_DISCONNECTED)
2936  endif()
2937  set(${var} "${update_disconnected}" PARENT_SCOPE)
2938endfunction()
2939
2940function(_ep_add_update_command name)
2941  ExternalProject_Get_Property(${name} source_dir tmp_dir)
2942
2943  get_property(cmd_set TARGET ${name} PROPERTY _EP_UPDATE_COMMAND SET)
2944  get_property(cmd TARGET ${name} PROPERTY _EP_UPDATE_COMMAND)
2945  get_property(cvs_repository TARGET ${name} PROPERTY _EP_CVS_REPOSITORY)
2946  get_property(svn_repository TARGET ${name} PROPERTY _EP_SVN_REPOSITORY)
2947  get_property(git_repository TARGET ${name} PROPERTY _EP_GIT_REPOSITORY)
2948  get_property(hg_repository  TARGET ${name} PROPERTY _EP_HG_REPOSITORY )
2949
2950  _ep_get_update_disconnected(update_disconnected ${name})
2951
2952  set(work_dir)
2953  set(comment)
2954  set(always)
2955
2956  if(cmd_set)
2957    set(work_dir ${source_dir})
2958    if(NOT "x${cmd}" STREQUAL "x")
2959      set(always 1)
2960    endif()
2961  elseif(cvs_repository)
2962    if(NOT CVS_EXECUTABLE)
2963      message(FATAL_ERROR "error: could not find cvs for update of ${name}")
2964    endif()
2965    set(work_dir ${source_dir})
2966    set(comment "Performing update step (CVS update) for '${name}'")
2967    get_property(cvs_tag TARGET ${name} PROPERTY _EP_CVS_TAG)
2968    set(cmd ${CVS_EXECUTABLE} -d ${cvs_repository} -q up -dP ${cvs_tag})
2969    set(always 1)
2970  elseif(svn_repository)
2971    if(NOT Subversion_SVN_EXECUTABLE)
2972      message(FATAL_ERROR "error: could not find svn for update of ${name}")
2973    endif()
2974    set(work_dir ${source_dir})
2975    set(comment "Performing update step (SVN update) for '${name}'")
2976    get_property(svn_revision TARGET ${name} PROPERTY _EP_SVN_REVISION)
2977    get_property(svn_username TARGET ${name} PROPERTY _EP_SVN_USERNAME)
2978    get_property(svn_password TARGET ${name} PROPERTY _EP_SVN_PASSWORD)
2979    get_property(svn_trust_cert TARGET ${name} PROPERTY _EP_SVN_TRUST_CERT)
2980    set(svn_user_pw_args "")
2981    if(DEFINED svn_username)
2982      set(svn_user_pw_args ${svn_user_pw_args} "--username=${svn_username}")
2983    endif()
2984    if(DEFINED svn_password)
2985      set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}")
2986    endif()
2987    if(svn_trust_cert)
2988      set(svn_trust_cert_args --trust-server-cert)
2989    endif()
2990    set(cmd ${Subversion_SVN_EXECUTABLE} up ${svn_revision}
2991      --non-interactive ${svn_trust_cert_args} ${svn_user_pw_args})
2992    set(always 1)
2993  elseif(git_repository)
2994    # FetchContent gives us these directly, so don't try to recompute them
2995    if(NOT GIT_EXECUTABLE OR NOT GIT_VERSION_STRING)
2996      unset(CMAKE_MODULE_PATH) # Use CMake builtin find module
2997      find_package(Git QUIET)
2998      if(NOT GIT_EXECUTABLE)
2999        message(FATAL_ERROR "error: could not find git for fetch of ${name}")
3000      endif()
3001    endif()
3002    set(work_dir ${source_dir})
3003    set(comment "Performing update step for '${name}'")
3004    get_property(git_tag TARGET ${name} PROPERTY _EP_GIT_TAG)
3005    if(NOT git_tag)
3006      set(git_tag "master")
3007    endif()
3008    get_property(git_remote_name TARGET ${name} PROPERTY _EP_GIT_REMOTE_NAME)
3009    if(NOT git_remote_name)
3010      set(git_remote_name "origin")
3011    endif()
3012
3013    set(git_init_submodules TRUE)
3014    get_property(git_submodules_set TARGET ${name} PROPERTY _EP_GIT_SUBMODULES SET)
3015    if(git_submodules_set)
3016      get_property(git_submodules TARGET ${name} PROPERTY _EP_GIT_SUBMODULES)
3017      if(git_submodules  STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW")
3018        set(git_init_submodules FALSE)
3019      endif()
3020    endif()
3021
3022    get_property(git_update_strategy TARGET ${name} PROPERTY _EP_GIT_REMOTE_UPDATE_STRATEGY)
3023    if(NOT git_update_strategy)
3024      set(git_update_strategy "${CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY}")
3025    endif()
3026    if(NOT git_update_strategy)
3027      set(git_update_strategy REBASE)
3028    endif()
3029    set(strategies CHECKOUT REBASE REBASE_CHECKOUT)
3030    if(NOT git_update_strategy IN_LIST strategies)
3031      message(FATAL_ERROR "'${git_update_strategy}' is not one of the supported strategies: ${strategies}")
3032    endif()
3033
3034    _ep_get_git_submodules_recurse(git_submodules_recurse)
3035
3036    _ep_write_gitupdate_script(${tmp_dir}/${name}-gitupdate.cmake
3037      ${GIT_EXECUTABLE} ${git_tag} ${git_remote_name} ${git_init_submodules} "${git_submodules_recurse}" "${git_submodules}" ${git_repository} ${work_dir} ${git_update_strategy}
3038      )
3039    set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-gitupdate.cmake)
3040    set(always 1)
3041  elseif(hg_repository)
3042    if(NOT HG_EXECUTABLE)
3043      message(FATAL_ERROR "error: could not find hg for pull of ${name}")
3044    endif()
3045    set(work_dir ${source_dir})
3046    set(comment "Performing update step (hg pull) for '${name}'")
3047    get_property(hg_tag TARGET ${name} PROPERTY _EP_HG_TAG)
3048    if(NOT hg_tag)
3049      set(hg_tag "tip")
3050    endif()
3051    if("${HG_VERSION_STRING}" STREQUAL "2.1")
3052      message(WARNING "Mercurial 2.1 does not distinguish an empty pull from a failed pull:
3053 http://mercurial.selenic.com/wiki/UpgradeNotes#A2.1.1:_revert_pull_return_code_change.2C_compile_issue_on_OS_X
3054 http://thread.gmane.org/gmane.comp.version-control.mercurial.devel/47656
3055Update to Mercurial >= 2.1.1.
3056")
3057    endif()
3058    set(cmd ${HG_EXECUTABLE} pull
3059      COMMAND ${HG_EXECUTABLE} update ${hg_tag}
3060      )
3061    set(always 1)
3062  endif()
3063
3064  get_property(log TARGET ${name} PROPERTY _EP_LOG_UPDATE)
3065  if(log)
3066    set(log LOG 1)
3067  else()
3068    set(log "")
3069  endif()
3070
3071  get_property(uses_terminal TARGET ${name} PROPERTY
3072    _EP_USES_TERMINAL_UPDATE)
3073  if(uses_terminal)
3074    set(uses_terminal USES_TERMINAL 1)
3075  else()
3076    set(uses_terminal "")
3077  endif()
3078
3079  set(__cmdQuoted)
3080  foreach(__item IN LISTS cmd)
3081    string(APPEND __cmdQuoted " [==[${__item}]==]")
3082  endforeach()
3083  cmake_language(EVAL CODE "
3084    ExternalProject_Add_Step(${name} update
3085      INDEPENDENT TRUE
3086      COMMENT \${comment}
3087      COMMAND ${__cmdQuoted}
3088      ALWAYS \${always}
3089      EXCLUDE_FROM_MAIN \${update_disconnected}
3090      WORKING_DIRECTORY \${work_dir}
3091      DEPENDEES download
3092      ${log}
3093      ${uses_terminal}
3094      )"
3095  )
3096
3097endfunction()
3098
3099
3100function(_ep_add_patch_command name)
3101  ExternalProject_Get_Property(${name} source_dir)
3102
3103  get_property(cmd_set TARGET ${name} PROPERTY _EP_PATCH_COMMAND SET)
3104  get_property(cmd TARGET ${name} PROPERTY _EP_PATCH_COMMAND)
3105
3106  set(work_dir)
3107
3108  if(cmd_set)
3109    set(work_dir ${source_dir})
3110  endif()
3111
3112  get_property(log TARGET ${name} PROPERTY _EP_LOG_PATCH)
3113  if(log)
3114    set(log LOG 1)
3115  else()
3116    set(log "")
3117  endif()
3118
3119  _ep_get_update_disconnected(update_disconnected ${name})
3120  if(update_disconnected)
3121    set(patch_dep download)
3122  else()
3123    set(patch_dep update)
3124  endif()
3125
3126  set(__cmdQuoted)
3127  foreach(__item IN LISTS cmd)
3128    string(APPEND __cmdQuoted " [==[${__item}]==]")
3129  endforeach()
3130  cmake_language(EVAL CODE "
3131    ExternalProject_Add_Step(${name} patch
3132      INDEPENDENT TRUE
3133      COMMAND ${__cmdQuoted}
3134      WORKING_DIRECTORY \${work_dir}
3135      DEPENDEES \${patch_dep}
3136      ${log}
3137      )"
3138  )
3139endfunction()
3140
3141function(_ep_get_file_deps var name)
3142  set(file_deps)
3143
3144  get_property(deps TARGET ${name} PROPERTY _EP_DEPENDS)
3145  foreach(dep IN LISTS deps)
3146    get_property(dep_type TARGET ${dep} PROPERTY TYPE)
3147    if(dep_type STREQUAL "UTILITY")
3148      get_property(is_ep TARGET ${dep} PROPERTY _EP_IS_EXTERNAL_PROJECT)
3149      if(is_ep)
3150        _ep_get_step_stampfile(${dep} "done" done_stamp_file)
3151        list(APPEND file_deps ${done_stamp_file})
3152      endif()
3153    endif()
3154  endforeach()
3155
3156  set("${var}" "${file_deps}" PARENT_SCOPE)
3157endfunction()
3158
3159function(_ep_extract_configure_command var name)
3160  get_property(cmd_set TARGET ${name} PROPERTY _EP_CONFIGURE_COMMAND SET)
3161  if(cmd_set)
3162    get_property(cmd TARGET ${name} PROPERTY _EP_CONFIGURE_COMMAND)
3163  else()
3164    get_target_property(cmake_command ${name} _EP_CMAKE_COMMAND)
3165    if(cmake_command)
3166      set(cmd "${cmake_command}")
3167    else()
3168      set(cmd "${CMAKE_COMMAND}")
3169    endif()
3170
3171    get_property(cmake_args TARGET ${name} PROPERTY _EP_CMAKE_ARGS)
3172    list(APPEND cmd ${cmake_args})
3173
3174    # If there are any CMAKE_CACHE_ARGS or CMAKE_CACHE_DEFAULT_ARGS,
3175    # write an initial cache and use it
3176    get_property(cmake_cache_args TARGET ${name} PROPERTY _EP_CMAKE_CACHE_ARGS)
3177    get_property(cmake_cache_default_args TARGET ${name} PROPERTY _EP_CMAKE_CACHE_DEFAULT_ARGS)
3178
3179    set(has_cmake_cache_args 0)
3180    if(NOT "${cmake_cache_args}" STREQUAL "")
3181      set(has_cmake_cache_args 1)
3182    endif()
3183
3184    set(has_cmake_cache_default_args 0)
3185    if(NOT "${cmake_cache_default_args}" STREQUAL "")
3186      set(has_cmake_cache_default_args 1)
3187    endif()
3188
3189    get_target_property(cmake_generator ${name} _EP_CMAKE_GENERATOR)
3190    get_target_property(cmake_generator_instance ${name} _EP_CMAKE_GENERATOR_INSTANCE)
3191    get_target_property(cmake_generator_platform ${name} _EP_CMAKE_GENERATOR_PLATFORM)
3192    get_target_property(cmake_generator_toolset ${name} _EP_CMAKE_GENERATOR_TOOLSET)
3193    if(cmake_generator)
3194      list(APPEND cmd "-G${cmake_generator}")
3195      if(cmake_generator_platform)
3196        list(APPEND cmd "-A${cmake_generator_platform}")
3197      endif()
3198      if(cmake_generator_toolset)
3199        list(APPEND cmd "-T${cmake_generator_toolset}")
3200      endif()
3201      if(cmake_generator_instance)
3202        list(APPEND cmd "-DCMAKE_GENERATOR_INSTANCE:INTERNAL=${cmake_generator_instance}")
3203      endif()
3204    else()
3205      if(CMAKE_EXTRA_GENERATOR)
3206        list(APPEND cmd "-G${CMAKE_EXTRA_GENERATOR} - ${CMAKE_GENERATOR}")
3207      else()
3208        list(APPEND cmd "-G${CMAKE_GENERATOR}")
3209        if("${CMAKE_GENERATOR}" MATCHES "Green Hills MULTI")
3210          set(has_cmake_cache_default_args 1)
3211          set(cmake_cache_default_args ${cmake_cache_default_args}
3212            "-DGHS_TARGET_PLATFORM:STRING=${GHS_TARGET_PLATFORM}"
3213            "-DGHS_PRIMARY_TARGET:STRING=${GHS_PRIMARY_TARGET}"
3214            "-DGHS_TOOLSET_ROOT:STRING=${GHS_TOOLSET_ROOT}"
3215            "-DGHS_OS_ROOT:STRING=${GHS_OS_ROOT}"
3216            "-DGHS_OS_DIR:STRING=${GHS_OS_DIR}"
3217            "-DGHS_BSP_NAME:STRING=${GHS_BSP_NAME}")
3218        endif()
3219      endif()
3220      if(cmake_generator_platform)
3221        message(FATAL_ERROR "Option CMAKE_GENERATOR_PLATFORM not allowed without CMAKE_GENERATOR.")
3222      endif()
3223      if(CMAKE_GENERATOR_PLATFORM)
3224        list(APPEND cmd "-A${CMAKE_GENERATOR_PLATFORM}")
3225      endif()
3226      if(cmake_generator_toolset)
3227        message(FATAL_ERROR "Option CMAKE_GENERATOR_TOOLSET not allowed without CMAKE_GENERATOR.")
3228      endif()
3229      if(CMAKE_GENERATOR_TOOLSET)
3230        list(APPEND cmd "-T${CMAKE_GENERATOR_TOOLSET}")
3231      endif()
3232      if(cmake_generator_instance)
3233        message(FATAL_ERROR "Option CMAKE_GENERATOR_INSTANCE not allowed without CMAKE_GENERATOR.")
3234      endif()
3235      if(CMAKE_GENERATOR_INSTANCE)
3236        list(APPEND cmd "-DCMAKE_GENERATOR_INSTANCE:INTERNAL=${CMAKE_GENERATOR_INSTANCE}")
3237      endif()
3238    endif()
3239
3240    if(has_cmake_cache_args OR has_cmake_cache_default_args)
3241      set(_ep_cache_args_script "<TMP_DIR>/${name}-cache-$<CONFIG>.cmake")
3242      if(has_cmake_cache_args)
3243        _ep_command_line_to_initial_cache(script_initial_cache_force "${cmake_cache_args}" 1)
3244      endif()
3245      if(has_cmake_cache_default_args)
3246        _ep_command_line_to_initial_cache(script_initial_cache_default "${cmake_cache_default_args}" 0)
3247      endif()
3248      _ep_write_initial_cache(${name} "${_ep_cache_args_script}" "${script_initial_cache_force}${script_initial_cache_default}")
3249      list(APPEND cmd "-C${_ep_cache_args_script}")
3250      _ep_replace_location_tags(${name} _ep_cache_args_script)
3251      set(_ep_cache_args_script
3252        "${_ep_cache_args_script}"
3253        PARENT_SCOPE)
3254    endif()
3255
3256    list(APPEND cmd "<SOURCE_DIR><SOURCE_SUBDIR>")
3257  endif()
3258
3259  set("${var}" "${cmd}" PARENT_SCOPE)
3260endfunction()
3261
3262# TODO: Make sure external projects use the proper compiler
3263function(_ep_add_configure_command name)
3264  ExternalProject_Get_Property(${name} binary_dir tmp_dir)
3265
3266  set(file_deps)
3267  get_property(configure_handled_by_build TARGET ${name}
3268               PROPERTY _EP_CONFIGURE_HANDLED_BY_BUILD)
3269  if(NOT configure_handled_by_build)
3270    # Depend on other external projects (file-level)
3271    _ep_get_file_deps(file_deps ${name})
3272  endif()
3273
3274  _ep_extract_configure_command(cmd ${name})
3275
3276  # If anything about the configure command changes, (command itself, cmake
3277  # used, cmake args or cmake generator) then re-run the configure step.
3278  # Fixes issue https://gitlab.kitware.com/cmake/cmake/-/issues/10258
3279  #
3280  if(NOT EXISTS ${tmp_dir}/${name}-cfgcmd.txt.in)
3281    file(WRITE ${tmp_dir}/${name}-cfgcmd.txt.in "cmd='\@cmd\@'\n")
3282  endif()
3283  configure_file(${tmp_dir}/${name}-cfgcmd.txt.in ${tmp_dir}/${name}-cfgcmd.txt)
3284  list(APPEND file_deps ${tmp_dir}/${name}-cfgcmd.txt)
3285  list(APPEND file_deps ${_ep_cache_args_script})
3286
3287  get_property(log TARGET ${name} PROPERTY _EP_LOG_CONFIGURE)
3288  if(log)
3289    set(log LOG 1)
3290  else()
3291    set(log "")
3292  endif()
3293
3294  get_property(uses_terminal TARGET ${name} PROPERTY
3295    _EP_USES_TERMINAL_CONFIGURE)
3296  if(uses_terminal)
3297    set(uses_terminal USES_TERMINAL 1)
3298  else()
3299    set(uses_terminal "")
3300  endif()
3301
3302  set(__cmdQuoted)
3303  foreach(__item IN LISTS cmd)
3304    string(APPEND __cmdQuoted " [==[${__item}]==]")
3305  endforeach()
3306  cmake_language(EVAL CODE "
3307    ExternalProject_Add_Step(${name} configure
3308      INDEPENDENT FALSE
3309      COMMAND ${__cmdQuoted}
3310      WORKING_DIRECTORY \${binary_dir}
3311      DEPENDEES patch
3312      DEPENDS \${file_deps}
3313      ${log}
3314      ${uses_terminal}
3315      )"
3316  )
3317endfunction()
3318
3319
3320function(_ep_add_build_command name)
3321  ExternalProject_Get_Property(${name} binary_dir)
3322
3323  set(file_deps)
3324  get_property(configure_handled_by_build TARGET ${name}
3325               PROPERTY _EP_CONFIGURE_HANDLED_BY_BUILD)
3326  if(configure_handled_by_build)
3327    # Depend on other external projects (file-level)
3328    _ep_get_file_deps(file_deps ${name})
3329  endif()
3330
3331  get_property(cmd_set TARGET ${name} PROPERTY _EP_BUILD_COMMAND SET)
3332  if(cmd_set)
3333    get_property(cmd TARGET ${name} PROPERTY _EP_BUILD_COMMAND)
3334  else()
3335    _ep_get_build_command(${name} BUILD cmd)
3336  endif()
3337
3338  get_property(log TARGET ${name} PROPERTY _EP_LOG_BUILD)
3339  if(log)
3340    set(log LOG 1)
3341  else()
3342    set(log "")
3343  endif()
3344
3345  get_property(uses_terminal TARGET ${name} PROPERTY
3346    _EP_USES_TERMINAL_BUILD)
3347  if(uses_terminal)
3348    set(uses_terminal USES_TERMINAL 1)
3349  else()
3350    set(uses_terminal "")
3351  endif()
3352
3353  get_property(build_always TARGET ${name} PROPERTY _EP_BUILD_ALWAYS)
3354  if(build_always)
3355    set(always 1)
3356  else()
3357    set(always 0)
3358  endif()
3359
3360  get_property(build_byproducts TARGET ${name} PROPERTY _EP_BUILD_BYPRODUCTS)
3361
3362  set(__cmdQuoted)
3363  foreach(__item IN LISTS cmd)
3364    string(APPEND __cmdQuoted " [==[${__item}]==]")
3365  endforeach()
3366  cmake_language(EVAL CODE "
3367    ExternalProject_Add_Step(${name} build
3368      INDEPENDENT FALSE
3369      COMMAND ${__cmdQuoted}
3370      BYPRODUCTS \${build_byproducts}
3371      WORKING_DIRECTORY \${binary_dir}
3372      DEPENDEES configure
3373      DEPENDS \${file_deps}
3374      ALWAYS \${always}
3375      ${log}
3376      ${uses_terminal}
3377      )"
3378  )
3379endfunction()
3380
3381
3382function(_ep_add_install_command name)
3383  ExternalProject_Get_Property(${name} binary_dir)
3384
3385  get_property(cmd_set TARGET ${name} PROPERTY _EP_INSTALL_COMMAND SET)
3386  if(cmd_set)
3387    get_property(cmd TARGET ${name} PROPERTY _EP_INSTALL_COMMAND)
3388  else()
3389    _ep_get_build_command(${name} INSTALL cmd)
3390  endif()
3391
3392  get_property(log TARGET ${name} PROPERTY _EP_LOG_INSTALL)
3393  if(log)
3394    set(log LOG 1)
3395  else()
3396    set(log "")
3397  endif()
3398
3399  get_property(uses_terminal TARGET ${name} PROPERTY
3400    _EP_USES_TERMINAL_INSTALL)
3401  if(uses_terminal)
3402    set(uses_terminal USES_TERMINAL 1)
3403  else()
3404    set(uses_terminal "")
3405  endif()
3406
3407  set(__cmdQuoted)
3408  foreach(__item IN LISTS cmd)
3409    string(APPEND __cmdQuoted " [==[${__item}]==]")
3410  endforeach()
3411  cmake_language(EVAL CODE "
3412    ExternalProject_Add_Step(${name} install
3413      INDEPENDENT FALSE
3414      COMMAND ${__cmdQuoted}
3415      WORKING_DIRECTORY \${binary_dir}
3416      DEPENDEES build
3417      ${log}
3418      ${uses_terminal}
3419      )"
3420  )
3421endfunction()
3422
3423
3424function(_ep_add_test_command name)
3425  ExternalProject_Get_Property(${name} binary_dir)
3426
3427  get_property(before TARGET ${name} PROPERTY _EP_TEST_BEFORE_INSTALL)
3428  get_property(after TARGET ${name} PROPERTY _EP_TEST_AFTER_INSTALL)
3429  get_property(exclude TARGET ${name} PROPERTY _EP_TEST_EXCLUDE_FROM_MAIN)
3430  get_property(cmd_set TARGET ${name} PROPERTY _EP_TEST_COMMAND SET)
3431
3432  # Only actually add the test step if one of the test related properties is
3433  # explicitly set. (i.e. the test step is omitted unless requested...)
3434  #
3435  if(cmd_set OR before OR after OR exclude)
3436    if(cmd_set)
3437      get_property(cmd TARGET ${name} PROPERTY _EP_TEST_COMMAND)
3438    else()
3439      _ep_get_build_command(${name} TEST cmd)
3440    endif()
3441
3442    if(before)
3443      set(dependees_args DEPENDEES build)
3444    else()
3445      set(dependees_args DEPENDEES install)
3446    endif()
3447
3448    if(exclude)
3449      set(dependers_args "")
3450      set(exclude_args EXCLUDE_FROM_MAIN 1)
3451    else()
3452      if(before)
3453        set(dependers_args DEPENDERS install)
3454      else()
3455        set(dependers_args "")
3456      endif()
3457      set(exclude_args "")
3458    endif()
3459
3460    get_property(log TARGET ${name} PROPERTY _EP_LOG_TEST)
3461    if(log)
3462      set(log LOG 1)
3463    else()
3464      set(log "")
3465    endif()
3466
3467    get_property(uses_terminal TARGET ${name} PROPERTY
3468      _EP_USES_TERMINAL_TEST)
3469    if(uses_terminal)
3470      set(uses_terminal USES_TERMINAL 1)
3471    else()
3472      set(uses_terminal "")
3473    endif()
3474
3475    set(__cmdQuoted)
3476    foreach(__item IN LISTS cmd)
3477      string(APPEND __cmdQuoted " [==[${__item}]==]")
3478    endforeach()
3479    cmake_language(EVAL CODE "
3480      ExternalProject_Add_Step(${name} test
3481        INDEPENDENT FALSE
3482        COMMAND ${__cmdQuoted}
3483        WORKING_DIRECTORY \${binary_dir}
3484        ${dependees_args}
3485        ${dependers_args}
3486        ${exclude_args}
3487        ${log}
3488        ${uses_terminal}
3489        )"
3490    )
3491  endif()
3492endfunction()
3493
3494
3495function(ExternalProject_Add name)
3496  cmake_policy(GET CMP0097 _EP_CMP0097
3497    PARENT_SCOPE # undocumented, do not use outside of CMake
3498    )
3499  cmake_policy(GET CMP0114 cmp0114
3500    PARENT_SCOPE # undocumented, do not use outside of CMake
3501    )
3502  if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12 AND NOT cmp0114 STREQUAL "NEW")
3503    message(AUTHOR_WARNING
3504      "Policy CMP0114 is not set to NEW.  "
3505      "In order to support the Xcode \"new build system\", "
3506      "this project must be updated to set policy CMP0114 to NEW."
3507      "\n"
3508      "Since CMake is generating for the Xcode \"new build system\", "
3509      "ExternalProject_Add will use policy CMP0114's NEW behavior anyway, "
3510      "but the generated build system may not match what the project intends."
3511      )
3512    set(cmp0114 "NEW")
3513  endif()
3514
3515  _ep_get_configuration_subdir_suffix(cfgdir)
3516
3517  # Add a custom target for the external project.
3518  set(cmf_dir ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles)
3519  _ep_get_complete_stampfile(${name} complete_stamp_file)
3520
3521  cmake_policy(PUSH)
3522  if(cmp0114 STREQUAL "NEW")
3523    # To implement CMP0114 NEW behavior with Makefile generators,
3524    # we need CMP0113 NEW behavior.
3525    cmake_policy(SET CMP0113 NEW)
3526  endif()
3527  # The "ALL" option to add_custom_target just tells it to not set the
3528  # EXCLUDE_FROM_ALL target property. Later, if the EXCLUDE_FROM_ALL
3529  # argument was passed, we explicitly set it for the target.
3530  add_custom_target(${name} ALL DEPENDS ${complete_stamp_file})
3531  cmake_policy(POP)
3532  set_property(TARGET ${name} PROPERTY _EP_IS_EXTERNAL_PROJECT 1)
3533  set_property(TARGET ${name} PROPERTY LABELS ${name})
3534  set_property(TARGET ${name} PROPERTY FOLDER "ExternalProjectTargets/${name}")
3535
3536  set_property(TARGET ${name} PROPERTY _EP_CMP0114 "${cmp0114}")
3537
3538  set(keywords
3539    #
3540    # Directory options
3541    #
3542    PREFIX
3543    TMP_DIR
3544    STAMP_DIR
3545    LOG_DIR
3546    DOWNLOAD_DIR
3547    SOURCE_DIR
3548    BINARY_DIR
3549    INSTALL_DIR
3550    #
3551    # Download step options
3552    #
3553    DOWNLOAD_COMMAND
3554    #
3555    URL
3556    URL_HASH
3557    URL_MD5
3558    DOWNLOAD_NAME
3559    DOWNLOAD_NO_EXTRACT
3560    DOWNLOAD_NO_PROGRESS
3561    TIMEOUT
3562    INACTIVITY_TIMEOUT
3563    HTTP_USERNAME
3564    HTTP_PASSWORD
3565    HTTP_HEADER
3566    TLS_VERIFY     # Also used for git clone operations
3567    TLS_CAINFO
3568    NETRC
3569    NETRC_FILE
3570    #
3571    GIT_REPOSITORY
3572    GIT_TAG
3573    GIT_REMOTE_NAME
3574    GIT_SUBMODULES
3575    GIT_SUBMODULES_RECURSE
3576    GIT_SHALLOW
3577    GIT_PROGRESS
3578    GIT_CONFIG
3579    GIT_REMOTE_UPDATE_STRATEGY
3580    #
3581    SVN_REPOSITORY
3582    SVN_REVISION
3583    SVN_USERNAME
3584    SVN_PASSWORD
3585    SVN_TRUST_CERT
3586    #
3587    HG_REPOSITORY
3588    HG_TAG
3589    #
3590    CVS_REPOSITORY
3591    CVS_MODULE
3592    CVS_TAG
3593    #
3594    # Update step options
3595    #
3596    UPDATE_COMMAND
3597    UPDATE_DISCONNECTED
3598    #
3599    # Patch step options
3600    #
3601    PATCH_COMMAND
3602    #
3603    # Configure step options
3604    #
3605    CONFIGURE_COMMAND
3606    CMAKE_COMMAND
3607    CMAKE_GENERATOR
3608    CMAKE_GENERATOR_PLATFORM
3609    CMAKE_GENERATOR_TOOLSET
3610    CMAKE_GENERATOR_INSTANCE
3611    CMAKE_ARGS
3612    CMAKE_CACHE_ARGS
3613    CMAKE_CACHE_DEFAULT_ARGS
3614    SOURCE_SUBDIR
3615    CONFIGURE_HANDLED_BY_BUILD
3616    #
3617    # Build step options
3618    #
3619    BUILD_COMMAND
3620    BUILD_IN_SOURCE
3621    BUILD_ALWAYS
3622    BUILD_BYPRODUCTS
3623    #
3624    # Install step options
3625    #
3626    INSTALL_COMMAND
3627    #
3628    # Test step options
3629    #
3630    TEST_COMMAND
3631    TEST_BEFORE_INSTALL
3632    TEST_AFTER_INSTALL
3633    TEST_EXCLUDE_FROM_MAIN
3634    #
3635    # Logging options
3636    #
3637    LOG_DOWNLOAD
3638    LOG_UPDATE
3639    LOG_PATCH
3640    LOG_CONFIGURE
3641    LOG_BUILD
3642    LOG_INSTALL
3643    LOG_TEST
3644    LOG_MERGED_STDOUTERR
3645    LOG_OUTPUT_ON_FAILURE
3646    #
3647    # Terminal access options
3648    #
3649    USES_TERMINAL_DOWNLOAD
3650    USES_TERMINAL_UPDATE
3651    USES_TERMINAL_CONFIGURE
3652    USES_TERMINAL_BUILD
3653    USES_TERMINAL_INSTALL
3654    USES_TERMINAL_TEST
3655    #
3656    # Target options
3657    #
3658    DEPENDS
3659    EXCLUDE_FROM_ALL
3660    STEP_TARGETS
3661    INDEPENDENT_STEP_TARGETS
3662    #
3663    # Miscellaneous options
3664    #
3665    LIST_SEPARATOR
3666  )
3667  _ep_parse_arguments(ExternalProject_Add "${keywords}" ${name} _EP_ "${ARGN}")
3668  _ep_set_directories(${name})
3669  _ep_get_step_stampfile(${name} "done" done_stamp_file)
3670  _ep_get_step_stampfile(${name} "install" install_stamp_file)
3671
3672  # Set the EXCLUDE_FROM_ALL target property if required.
3673  get_property(exclude_from_all TARGET ${name} PROPERTY _EP_EXCLUDE_FROM_ALL)
3674  if(exclude_from_all)
3675    set_property(TARGET ${name} PROPERTY EXCLUDE_FROM_ALL TRUE)
3676  endif()
3677
3678  # The 'complete' step depends on all other steps and creates a
3679  # 'done' mark.  A dependent external project's 'configure' step
3680  # depends on the 'done' mark so that it rebuilds when this project
3681  # rebuilds.  It is important that 'done' is not the output of any
3682  # custom command so that CMake does not propagate build rules to
3683  # other external project targets, which may cause problems during
3684  # parallel builds.  However, the Ninja generator needs to see the entire
3685  # dependency graph, and can cope with custom commands belonging to
3686  # multiple targets, so we add the 'done' mark as an output for Ninja only.
3687  set(complete_outputs ${complete_stamp_file})
3688  if(${CMAKE_GENERATOR} MATCHES "Ninja")
3689    set(complete_outputs ${complete_outputs} ${done_stamp_file})
3690  endif()
3691
3692  add_custom_command(
3693    OUTPUT ${complete_outputs}
3694    COMMENT "Completed '${name}'"
3695    COMMAND ${CMAKE_COMMAND} -E make_directory ${cmf_dir}${cfgdir}
3696    COMMAND ${CMAKE_COMMAND} -E touch ${complete_stamp_file}
3697    COMMAND ${CMAKE_COMMAND} -E touch ${done_stamp_file}
3698    DEPENDS ${install_stamp_file}
3699    VERBATIM
3700    )
3701
3702
3703  # Depend on other external projects (target-level).
3704  get_property(deps TARGET ${name} PROPERTY _EP_DEPENDS)
3705  foreach(arg IN LISTS deps)
3706    add_dependencies(${name} ${arg})
3707  endforeach()
3708
3709  # Set up custom build steps based on the target properties.
3710  # Each step depends on the previous one.
3711  #
3712  # The target depends on the output of the final step.
3713  # (Already set up above in the DEPENDS of the add_custom_target command.)
3714  #
3715  _ep_add_mkdir_command(${name})
3716  _ep_add_download_command(${name})
3717  _ep_add_update_command(${name})
3718  _ep_add_patch_command(${name})
3719  _ep_add_configure_command(${name})
3720  _ep_add_build_command(${name})
3721  _ep_add_install_command(${name})
3722
3723  # Test is special in that it might depend on build, or it might depend
3724  # on install.
3725  #
3726  _ep_add_test_command(${name})
3727endfunction()
3728
3729cmake_policy(POP)
3730