summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Auxiliary/bash-completion/cmake2
-rw-r--r--Help/manual/cmake-modules.7.rst1
-rw-r--r--Help/manual/cmake.1.rst5
-rw-r--r--Help/module/FetchContent.rst1
-rw-r--r--Help/release/dev/0-sample-topic.rst7
-rw-r--r--Help/release/dev/FetchContent.rst11
-rw-r--r--Help/release/dev/cmake-open.rst6
-rw-r--r--Help/release/index.rst2
-rw-r--r--Modules/CMakeFindSublimeText2.cmake23
-rw-r--r--Modules/CPackWIX.cmake2
-rw-r--r--Modules/FetchContent.cmake914
-rw-r--r--Modules/FetchContent/CMakeLists.cmake.in21
-rw-r--r--Modules/FindOpenCL.cmake5
-rw-r--r--Modules/FindPythonLibs.cmake7
-rw-r--r--Modules/UseJava.cmake6
-rw-r--r--Source/CMakeLists.txt14
-rw-r--r--Source/CMakeVersion.cmake4
-rw-r--r--Source/CPack/WiX/cmCMakeToWixPath.cxx39
-rw-r--r--Source/CPack/WiX/cmCMakeToWixPath.h12
-rw-r--r--Source/CPack/WiX/cmCPackWIXGenerator.cxx41
-rw-r--r--Source/CPack/WiX/cmCPackWIXGenerator.h9
-rw-r--r--Source/CPack/WiX/cmWIXFilesSourceWriter.cxx4
-rw-r--r--Source/CPack/cmCPackDragNDropGenerator.cxx20
-rw-r--r--Source/CPack/cmCPackGeneratorFactory.cxx4
-rw-r--r--Source/Modules/FindLibUUID.cmake85
-rw-r--r--Source/QtDialog/CMakeSetupDialog.cxx36
-rw-r--r--Source/QtDialog/CMakeSetupDialog.h1
-rw-r--r--Source/QtDialog/QCMake.cxx29
-rw-r--r--Source/QtDialog/QCMake.h8
-rw-r--r--Source/cmExternalMakefileProjectGenerator.cxx7
-rw-r--r--Source/cmExternalMakefileProjectGenerator.h3
-rw-r--r--Source/cmExtraSublimeTextGenerator.cxx23
-rw-r--r--Source/cmExtraSublimeTextGenerator.h3
-rw-r--r--Source/cmGlobalGenerator.cxx10
-rw-r--r--Source/cmGlobalGenerator.h6
-rw-r--r--Source/cmGlobalVisualStudioGenerator.cxx33
-rw-r--r--Source/cmGlobalVisualStudioGenerator.h3
-rw-r--r--Source/cmGlobalXCodeGenerator.cxx34
-rw-r--r--Source/cmGlobalXCodeGenerator.h7
-rw-r--r--Source/cmLinkedTree.h6
-rw-r--r--Source/cmSetCommand.cxx14
-rw-r--r--Source/cmTryRunCommand.cxx9
-rw-r--r--Source/cmUnsetCommand.cxx13
-rw-r--r--Source/cmake.cxx43
-rw-r--r--Source/cmake.h3
-rw-r--r--Source/cmakemain.cxx43
-rw-r--r--Source/kwsys/CMakeLists.txt7
-rw-r--r--Source/kwsys/CommandLineArguments.cxx25
-rw-r--r--Source/kwsys/DynamicLoader.cxx17
-rw-r--r--Source/kwsys/ProcessUNIX.c28
-rw-r--r--Source/kwsys/ProcessWin32.c15
-rw-r--r--Source/kwsys/SystemInformation.cxx2
-rw-r--r--Source/kwsys/SystemTools.cxx17
-rw-r--r--Source/kwsys/kwsysPlatformTestsC.c15
-rw-r--r--Source/kwsys/testEncoding.cxx5
-rw-r--r--Source/kwsys/testProcess.c4
-rw-r--r--Source/kwsys/testSystemTools.cxx10
-rw-r--r--Tests/CMakeLists.txt12
-rw-r--r--Tests/Java/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/CMakeLists.txt1
-rw-r--r--Tests/RunCMake/FetchContent/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/FetchContent/DirOverrides.cmake46
-rw-r--r--Tests/RunCMake/FetchContent/DirectIgnoresDetails-stdout.txt1
-rw-r--r--Tests/RunCMake/FetchContent/DirectIgnoresDetails.cmake12
-rw-r--r--Tests/RunCMake/FetchContent/DownloadTwice-result.txt1
-rw-r--r--Tests/RunCMake/FetchContent/DownloadTwice-stderr.txt1
-rw-r--r--Tests/RunCMake/FetchContent/DownloadTwice.cmake9
-rw-r--r--Tests/RunCMake/FetchContent/FirstDetailsWin-stdout.txt1
-rw-r--r--Tests/RunCMake/FetchContent/FirstDetailsWin.cmake16
-rw-r--r--Tests/RunCMake/FetchContent/GetProperties.cmake67
-rw-r--r--Tests/RunCMake/FetchContent/MissingDetails-result.txt1
-rw-r--r--Tests/RunCMake/FetchContent/MissingDetails-stderr.txt1
-rw-r--r--Tests/RunCMake/FetchContent/MissingDetails.cmake3
-rw-r--r--Tests/RunCMake/FetchContent/RunCMakeTest.cmake28
-rw-r--r--Tests/RunCMake/FetchContent/SameGenerator.cmake17
-rw-r--r--Tests/RunCMake/FetchContent/ScriptMode.cmake35
-rw-r--r--Tests/RunCMake/FetchContent/VarDefinitions.cmake75
-rw-r--r--Tests/RunCMake/RunCMake.cmake14
78 files changed, 1913 insertions, 158 deletions
diff --git a/Auxiliary/bash-completion/cmake b/Auxiliary/bash-completion/cmake
index 0a862fa..5d67b0b 100644
--- a/Auxiliary/bash-completion/cmake
+++ b/Auxiliary/bash-completion/cmake
@@ -96,7 +96,7 @@ _cmake()
_filedir
return
;;
- --build)
+ --build|--open)
_filedir -d
return
;;
diff --git a/Help/manual/cmake-modules.7.rst b/Help/manual/cmake-modules.7.rst
index 8f4b252..9fd92ec 100644
--- a/Help/manual/cmake-modules.7.rst
+++ b/Help/manual/cmake-modules.7.rst
@@ -80,6 +80,7 @@ All Modules
/module/ExternalData
/module/ExternalProject
/module/FeatureSummary
+ /module/FetchContent
/module/FindALSA
/module/FindArmadillo
/module/FindASPELL
diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst
index 6a21683..ff8c6c7 100644
--- a/Help/manual/cmake.1.rst
+++ b/Help/manual/cmake.1.rst
@@ -11,6 +11,7 @@ Synopsis
cmake [<options>] (<path-to-source> | <path-to-existing-build>)
cmake [(-D <var>=<value>)...] -P <cmake-script-file>
cmake --build <dir> [<options>...] [-- <build-tool-options>...]
+ cmake --open <dir>
cmake -E <command> [<options>...]
cmake --find-package <options>...
@@ -51,6 +52,10 @@ Options
``--build <dir>``
See `Build Tool Mode`_.
+``--open <dir>``
+ Open the generated project in the associated application. This is
+ only supported by some generators.
+
``-N``
View mode only.
diff --git a/Help/module/FetchContent.rst b/Help/module/FetchContent.rst
new file mode 100644
index 0000000..c130a6d
--- /dev/null
+++ b/Help/module/FetchContent.rst
@@ -0,0 +1 @@
+.. cmake-module:: ../../Modules/FetchContent.cmake
diff --git a/Help/release/dev/0-sample-topic.rst b/Help/release/dev/0-sample-topic.rst
new file mode 100644
index 0000000..e4cc01e
--- /dev/null
+++ b/Help/release/dev/0-sample-topic.rst
@@ -0,0 +1,7 @@
+0-sample-topic
+--------------
+
+* This is a sample release note for the change in a topic.
+ Developers should add similar notes for each topic branch
+ making a noteworthy change. Each document should be named
+ and titled to match the topic name to avoid merge conflicts.
diff --git a/Help/release/dev/FetchContent.rst b/Help/release/dev/FetchContent.rst
new file mode 100644
index 0000000..3b12977
--- /dev/null
+++ b/Help/release/dev/FetchContent.rst
@@ -0,0 +1,11 @@
+FetchContent
+------------
+
+* A new :module:`FetchContent` module was added which supports populating
+ content at configure time using any of the download/update methods
+ supported by :command:`ExternalProject_Add`. This allows the content
+ to be used immediately during the configure stage, such as with
+ :command:`add_subdirectory`, etc. Hierarchical project structures are
+ well supported, allowing parent projects to override the content details
+ of child projects and ensuring content is not populated multiple times
+ throughout the whole project tree.
diff --git a/Help/release/dev/cmake-open.rst b/Help/release/dev/cmake-open.rst
new file mode 100644
index 0000000..a8f77ae
--- /dev/null
+++ b/Help/release/dev/cmake-open.rst
@@ -0,0 +1,6 @@
+cmake-open
+----------
+
+* The :manual:`cmake(1)` ``--open <dir>`` command line option can now
+ be used to open generated IDE projects like Visual Studio solutions
+ or Xcode projects.
diff --git a/Help/release/index.rst b/Help/release/index.rst
index 0c7572f..6472b51 100644
--- a/Help/release/index.rst
+++ b/Help/release/index.rst
@@ -7,6 +7,8 @@ CMake Release Notes
This file should include the adjacent "dev.txt" file
in development versions but not in release versions.
+.. include:: dev.txt
+
Releases
========
diff --git a/Modules/CMakeFindSublimeText2.cmake b/Modules/CMakeFindSublimeText2.cmake
new file mode 100644
index 0000000..022d010
--- /dev/null
+++ b/Modules/CMakeFindSublimeText2.cmake
@@ -0,0 +1,23 @@
+# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+
+# This file is included in CMakeSystemSpecificInformation.cmake if
+# the Sublime Text 2 extra generator has been selected.
+
+find_program(CMAKE_SUBLIMETEXT_EXECUTABLE
+ NAMES subl3 subl sublime_text
+ PATHS
+ "/Applications/Sublime Text.app/Contents/SharedSupport/bin"
+ "/Applications/Sublime Text 3.app/Contents/SharedSupport/bin"
+ "/Applications/Sublime Text 2.app/Contents/SharedSupport/bin"
+ "$ENV{HOME}/Applications/Sublime Text.app/Contents/SharedSupport/bin"
+ "$ENV{HOME}/Applications/Sublime Text 3.app/Contents/SharedSupport/bin"
+ "$ENV{HOME}/Applications/Sublime Text 2.app/Contents/SharedSupport/bin"
+ "/opt/sublime_text"
+ "/opt/sublime_text_3"
+ DOC "The Sublime Text executable")
+
+if(CMAKE_SUBLIMETEXT_EXECUTABLE)
+ set(CMAKE_OPEN_PROJECT_COMMAND "${CMAKE_SUBLIMETEXT_EXECUTABLE} --project <PROJECT_FILE>" )
+endif()
diff --git a/Modules/CPackWIX.cmake b/Modules/CPackWIX.cmake
index 1dc37d4..c723e72 100644
--- a/Modules/CPackWIX.cmake
+++ b/Modules/CPackWIX.cmake
@@ -289,7 +289,7 @@
#
if(NOT CPACK_WIX_ROOT)
- file(TO_CMAKE_PATH "$ENV{WIX}" CPACK_WIX_ROOT)
+ string(REPLACE "\\" "/" CPACK_WIX_ROOT "$ENV{WIX}")
endif()
find_program(CPACK_WIX_CANDLE_EXECUTABLE candle
diff --git a/Modules/FetchContent.cmake b/Modules/FetchContent.cmake
new file mode 100644
index 0000000..132354f
--- /dev/null
+++ b/Modules/FetchContent.cmake
@@ -0,0 +1,914 @@
+# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#[=======================================================================[.rst:
+FetchContent
+------------------
+
+.. only:: html
+
+ .. contents::
+
+Overview
+^^^^^^^^
+
+This module enables populating content at configure time via any method
+supported by the :module:`ExternalProject` module. Whereas
+:command:`ExternalProject_Add` downloads at build time, the
+``FetchContent`` module makes content available immediately, allowing the
+configure step to use the content in commands like :command:`add_subdirectory`,
+:command:`include` or :command:`file` operations.
+
+Content population details would normally be defined separately from the
+command that performs the actual population. Projects should also
+check whether the content has already been populated somewhere else in the
+project hierarchy. Typical usage would look something like this:
+
+.. code-block:: cmake
+
+ FetchContent_Declare(
+ googletest
+ GIT_REPOSITORY https://github.com/google/googletest.git
+ GIT_TAG release-1.8.0
+ )
+
+ FetchContent_GetProperties(googletest)
+ if(NOT googletest_POPULATED)
+ FetchContent_Populate(googletest)
+ add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
+ endif()
+
+When using the above pattern with a hierarchical project arrangement,
+projects at higher levels in the hierarchy are able to define or override
+the population details of content specified anywhere lower in the project
+hierarchy. The ability to detect whether content has already been
+populated ensures that even if multiple child projects want certain content
+to be available, the first one to populate it wins. The other child project
+can simply make use of the already available content instead of repeating
+the population for itself. See the
+:ref:`Examples <fetch-content-examples>` section which demonstrates
+this scenario.
+
+The ``FetchContent`` module also supports defining and populating
+content in a single call, with no check for whether the content has been
+populated elsewhere in the project already. This is a more low level
+operation and would not normally be the way the module is used, but it is
+sometimes useful as part of implementing some higher level feature or to
+populate some content in CMake's script mode.
+
+
+Declaring Content Details
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. command:: FetchContent_Declare
+
+ .. code-block:: cmake
+
+ FetchContent_Declare(<name> <contentOptions>...)
+
+ The ``FetchContent_Declare()`` function records the options that describe
+ how to populate the specified content, but if such details have already
+ been recorded earlier in this project (regardless of where in the project
+ hierarchy), this and all later calls for the same content ``<name>`` are
+ ignored. This "first to record, wins" approach is what allows hierarchical
+ projects to have parent projects override content details of child projects.
+
+ The content ``<name>`` can be any string without spaces, but good practice
+ would be to use only letters, numbers and underscores. The name will be
+ treated case-insensitively and it should be obvious for the content it
+ represents, often being the name of the child project or the value given
+ to its top level :command:`project` command (if it is a CMake project).
+ For well-known public projects, the name should generally be the official
+ name of the project. Choosing an unusual name makes it unlikely that other
+ projects needing that same content will use the same name, leading to
+ the content being populated multiple times.
+
+ The ``<contentOptions>`` can be any of the download or update/patch options
+ that the :command:`ExternalProject_Add` command understands. The configure,
+ build, install and test steps are explicitly disabled and therefore options
+ related to them will be ignored. In most cases, ``<contentOptions>`` will
+ just be a couple of options defining the download method and method-specific
+ details like a commit tag or archive hash. For example:
+
+ .. code-block:: cmake
+
+ FetchContent_Declare(
+ googletest
+ GIT_REPOSITORY https://github.com/google/googletest.git
+ GIT_TAG release-1.8.0
+ )
+
+ FetchContent_Declare(
+ myCompanyIcons
+ URL https://intranet.mycompany.com/assets/iconset_1.12.tar.gz
+ URL_HASH 5588a7b18261c20068beabfb4f530b87
+ )
+
+ FetchContent_Declare(
+ myCompanyCertificates
+ SVN_REPOSITORY svn+ssh://svn.mycompany.com/srv/svn/trunk/certs
+ SVN_REVISION -r12345
+ )
+
+Populating The Content
+^^^^^^^^^^^^^^^^^^^^^^
+
+.. command:: FetchContent_Populate
+
+ .. code-block:: cmake
+
+ FetchContent_Populate( <name> )
+
+ In most cases, the only argument given to ``FetchContent_Populate()`` is the
+ ``<name>``. When used this way, the command assumes the content details have
+ been recorded by an earlier call to :command:`FetchContent_Declare`. The
+ details are stored in a global property, so they are unaffected by things
+ like variable or directory scope. Therefore, it doesn't matter where in the
+ project the details were previously declared, as long as they have been
+ declared before the call to ``FetchContent_Populate()``. Those saved details
+ are then used to construct a call to :command:`ExternalProject_Add` in a
+ private sub-build to perform the content population immediately. The
+ implementation of ``ExternalProject_Add()`` ensures that if the content has
+ already been populated in a previous CMake run, that content will be reused
+ rather than repopulating them again. For the common case where population
+ involves downloading content, the cost of the download is only paid once.
+
+ An internal global property records when a particular content population
+ request has been processed. If ``FetchContent_Populate()`` is called more
+ than once for the same content name within a configure run, the second call
+ will halt with an error. Projects can and should check whether content
+ population has already been processed with the
+ :command:`FetchContent_GetProperties` command before calling
+ ``FetchContent_Populate()``.
+
+ ``FetchContent_Populate()`` will set three variables in the scope of the
+ caller; ``<lcName>_POPULATED``, ``<lcName>_SOURCE_DIR`` and
+ ``<lcName>_BINARY_DIR``, where ``<lcName>`` is the lowercased ``<name>``.
+ ``<lcName>_POPULATED`` will always be set to ``True`` by the call.
+ ``<lcName>_SOURCE_DIR`` is the location where the
+ content can be found upon return (it will have already been populated), while
+ ``<lcName>_BINARY_DIR`` is a directory intended for use as a corresponding
+ build directory. The main use case for the two directory variables is to
+ call :command:`add_subdirectory` immediately after population, i.e.:
+
+ .. code-block:: cmake
+
+ FetchContent_Populate(FooBar ...)
+ add_subdirectory(${foobar_SOURCE_DIR} ${foobar_BINARY_DIR})
+
+ The values of the three variables can also be retrieved from anywhere in the
+ project hierarchy using the :command:`FetchContent_GetProperties` command.
+
+ A number of cache variables influence the behavior of all content population
+ performed using details saved from a :command:`FetchContent_Declare` call:
+
+ ``FETCHCONTENT_BASE_DIR``
+ In most cases, the saved details do not specify any options relating to the
+ directories to use for the internal sub-build, final source and build areas.
+ It is generally best to leave these decisions up to the ``FetchContent``
+ module to handle on the project's behalf. The ``FETCHCONTENT_BASE_DIR``
+ cache variable controls the point under which all content population
+ directories are collected, but in most cases developers would not need to
+ change this. The default location is ``${CMAKE_BINARY_DIR}/_deps``, but if
+ developers change this value, they should aim to keep the path short and
+ just below the top level of the build tree to avoid running into path
+ length problems on Windows.
+
+ ``FETCHCONTENT_QUIET``
+ The logging output during population can be quite verbose, making the
+ configure stage quite noisy. This cache option (``ON`` by default) hides
+ all population output unless an error is encountered. If experiencing
+ problems with hung downloads, temporarily switching this option off may
+ help diagnose which content population is causing the issue.
+
+ ``FETCHCONTENT_FULLY_DISCONNECTED``
+ When this option is enabled, no attempt is made to download or update
+ any content. It is assumed that all content has already been populated in
+ a previous run or the source directories have been pointed at existing
+ contents the developer has provided manually (using options described
+ further below). When the developer knows that no changes have been made to
+ any content details, turning this option ``ON`` can significantly speed up
+ the configure stage. It is ``OFF`` by default.
+
+ ``FETCHCONTENT_UPDATES_DISCONNECTED``
+ This is a less severe download/update control compared to
+ ``FETCHCONTENT_FULLY_DISCONNECTED``. Instead of bypassing all download and
+ update logic, the ``FETCHCONTENT_UPDATES_DISCONNECTED`` only disables the
+ update stage. Therefore, if content has not been downloaded previously,
+ it will still be downloaded when this option is enabled. This can speed up
+ the configure stage, but not as much as
+ ``FETCHCONTENT_FULLY_DISCONNECTED``. It is ``OFF`` by default.
+
+ In addition to the above cache variables, the following cache variables are
+ also defined for each content name (``<ucName>`` is the uppercased value of
+ ``<name>``):
+
+ ``FETCHCONTENT_SOURCE_DIR_<ucName>``
+ If this is set, no download or update steps are performed for the specified
+ content and the ``<lcName>_SOURCE_DIR`` variable returned to the caller is
+ pointed at this location. This gives developers a way to have a separate
+ checkout of the content that they can modify freely without interference
+ from the build. The build simply uses that existing source, but it still
+ defines ``<lcName>_BINARY_DIR`` to point inside its own build area.
+ Developers are strongly encouraged to use this mechanism rather than
+ editing the sources populated in the default location, as changes to
+ sources in the default location can be lost when content population details
+ are changed by the project.
+
+ ``FETCHCONTENT_UPDATES_DISCONNECTED_<ucName>``
+ This is the per-content equivalent of
+ ``FETCHCONTENT_UPDATES_DISCONNECTED``. If the global option or this option
+ is ``ON``, then updates will be disabled for the named content.
+ Disabling updates for individual content can be useful for content whose
+ details rarely change, while still leaving other frequently changing
+ content with updates enabled.
+
+
+ The ``FetchContent_Populate()`` command also supports a syntax allowing the
+ content details to be specified directly rather than using any saved
+ details. This is more low-level and use of this form is generally to be
+ avoided in favour of using saved content details as outlined above.
+ Nevertheless, in certain situations it can be useful to invoke the content
+ population as an isolated operation (typically as part of implementing some
+ other higher level feature or when using CMake in script mode):
+
+ .. code-block:: cmake
+
+ FetchContent_Populate( <name>
+ [QUIET]
+ [SUBBUILD_DIR <subBuildDir>]
+ [SOURCE_DIR <srcDir>]
+ [BINARY_DIR <binDir>]
+ ...
+ )
+
+ This form has a number of key differences to that where only ``<name>`` is
+ provided:
+
+ - All required population details are assumed to have been provided directly
+ in the call to ``FetchContent_Populate()``. Any saved details for
+ ``<name>`` are ignored.
+ - No check is made for whether content for ``<name>`` has already been
+ populated.
+ - No global property is set to record that the population has occurred.
+ - No global properties record the source or binary directories used for the
+ populated content.
+ - The ``FETCHCONTENT_FULLY_DISCONNECTED`` and
+ ``FETCHCONTENT_UPDATES_DISCONNECTED`` cache variables are ignored.
+
+ The ``<lcName>_SOURCE_DIR`` and ``<lcName>_BINARY_DIR`` variables are still
+ returned to the caller, but since these locations are not stored as global
+ properties when this form is used, they are only available to the calling
+ scope and below rather than the entire project hierarchy. No
+ ``<lcName>_POPULATED`` variable is set in the caller's scope with this form.
+
+ The supported options for ``FetchContent_Populate()`` are the same as those
+ for :command:`FetchContent_Declare()`. Those few options shown just
+ above are either specific to ``FetchContent_Populate()`` or their behavior is
+ slightly modified from how :command:`ExternalProject_Add` treats them.
+
+ ``QUIET``
+ The ``QUIET`` option can be given to hide the output associated with
+ populating the specified content. If the population fails, the output will
+ be shown regardless of whether this option was given or not so that the
+ cause of the failure can be diagnosed. The global ``FETCHCONTENT_QUIET``
+ cache variable has no effect on ``FetchContent_Populate()`` calls where the
+ content details are provided directly.
+
+ ``SUBBUILD_DIR``
+ The ``SUBBUILD_DIR`` argument can be provided to change the location of the
+ sub-build created to perform the population. The default value is
+ ``${CMAKE_CURRENT_BINARY_DIR}/<lcName>-subbuild`` and it would be unusual
+ to need to override this default. If a relative path is specified, it will
+ be interpreted as relative to :variable:`CMAKE_CURRENT_BINARY_DIR`.
+
+ ``SOURCE_DIR``, ``BINARY_DIR``
+ The ``SOURCE_DIR`` and ``BINARY_DIR`` arguments are supported by
+ :command:`ExternalProject_Add`, but different default values are used by
+ ``FetchContent_Populate()``. ``SOURCE_DIR`` defaults to
+ ``${CMAKE_CURRENT_BINARY_DIR}/<lcName>-src`` and ``BINARY_DIR`` defaults to
+ ``${CMAKE_CURRENT_BINARY_DIR}/<lcName>-build``. If a relative path is
+ specified, it will be interpreted as relative to
+ :variable:`CMAKE_CURRENT_BINARY_DIR`.
+
+ In addition to the above explicit options, any other unrecognized options are
+ passed through unmodified to :command:`ExternalProject_Add` to perform the
+ download, patch and update steps. The following options are explicitly
+ prohibited (they are disabled by the ``FetchContent_Populate()`` command):
+
+ - ``CONFIGURE_COMMAND``
+ - ``BUILD_COMMAND``
+ - ``INSTALL_COMMAND``
+ - ``TEST_COMMAND``
+
+ If using ``FetchContent_Populate()`` within CMake's script mode, be aware
+ that the implementation sets up a sub-build which therefore requires a CMake
+ generator and build tool to be available. If these cannot be found by
+ default, then the :variable:`CMAKE_GENERATOR` and/or
+ :variable:`CMAKE_MAKE_PROGRAM` variables will need to be set appropriately
+ on the command line invoking the script.
+
+
+Retrieve Population Properties
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. command:: FetchContent_GetProperties
+
+ When using saved content details, a call to :command:`FetchContent_Populate`
+ records information in global properties which can be queried at any time.
+ This information includes the source and binary directories associated with
+ the content and also whether or not the content population has been processed
+ during the current configure run.
+
+ .. code-block:: cmake
+
+ FetchContent_GetProperties( <name>
+ [SOURCE_DIR <srcDirVar>]
+ [BINARY_DIR <binDirVar>]
+ [POPULATED <doneVar>]
+ )
+
+ The ``SOURCE_DIR``, ``BINARY_DIR`` and ``POPULATED`` options can be used to
+ specify which properties should be retrieved. Each option accepts a value
+ which is the name of the variable in which to store that property. Most of
+ the time though, only ``<name>`` is given, in which case the call will then
+ set the same variables as a call to
+ :command:`FetchContent_Populate(name) <FetchContent_Populate>`. This allows
+ the following canonical pattern to be used, which ensures that the relevant
+ variables will always be defined regardless of whether or not the population
+ has been performed elsewhere in the project already:
+
+ .. code-block:: cmake
+
+ FetchContent_GetProperties(foobar)
+ if(NOT foobar_POPULATED)
+ FetchContent_Populate(foobar)
+
+ # Set any custom variables, etc. here, then
+ # populate the content as part of this build
+
+ add_subdirectory(${foobar_SOURCE_DIR} ${foobar_BINARY_DIR})
+ endif()
+
+ The above pattern allows other parts of the overall project hierarchy to
+ re-use the same content and ensure that it is only populated once.
+
+
+.. _`fetch-content-examples`:
+
+Examples
+^^^^^^^^
+
+Consider a project hierarchy where ``projA`` is the top level project and it
+depends on projects ``projB`` and ``projC``. Both ``projB`` and ``projC``
+can be built standalone and they also both depend on another project
+``projD``. For simplicity, this example will assume that all four projects
+are available on a company git server. The ``CMakeLists.txt`` of each project
+might have sections like the following:
+
+*projA*:
+
+.. code-block:: cmake
+
+ include(FetchContent)
+ FetchContent_Declare(
+ projB
+ GIT_REPOSITORY git@mycompany.com/git/projB.git
+ GIT_TAG 4a89dc7e24ff212a7b5167bef7ab079d
+ )
+ FetchContent_Declare(
+ projC
+ GIT_REPOSITORY git@mycompany.com/git/projC.git
+ GIT_TAG 4ad4016bd1d8d5412d135cf8ceea1bb9
+ )
+ FetchContent_Declare(
+ projD
+ GIT_REPOSITORY git@mycompany.com/git/projD.git
+ GIT_TAG origin/integrationBranch
+ )
+
+ FetchContent_GetProperties(projB)
+ if(NOT projb_POPULATED)
+ FetchContent_Populate(projB)
+ add_subdirectory(${projb_SOURCE_DIR} ${projb_BINARY_DIR})
+ endif()
+
+ FetchContent_GetProperties(projC)
+ if(NOT projc_POPULATED)
+ FetchContent_Populate(projC)
+ add_subdirectory(${projc_SOURCE_DIR} ${projc_BINARY_DIR})
+ endif()
+
+*projB*:
+
+.. code-block:: cmake
+
+ include(FetchContent)
+ FetchContent_Declare(
+ projD
+ GIT_REPOSITORY git@mycompany.com/git/projD.git
+ GIT_TAG 20b415f9034bbd2a2e8216e9a5c9e632
+ )
+
+ FetchContent_GetProperties(projD)
+ if(NOT projd_POPULATED)
+ FetchContent_Populate(projD)
+ add_subdirectory(${projd_SOURCE_DIR} ${projd_BINARY_DIR})
+ endif()
+
+
+*projC*:
+
+.. code-block:: cmake
+
+ include(FetchContent)
+ FetchContent_Declare(
+ projD
+ GIT_REPOSITORY git@mycompany.com/git/projD.git
+ GIT_TAG 7d9a17ad2c962aa13e2fbb8043fb6b8a
+ )
+
+ FetchContent_GetProperties(projD)
+ if(NOT projd_POPULATED)
+ FetchContent_Populate(projD)
+ add_subdirectory(${projd_SOURCE_DIR} ${projd_BINARY_DIR})
+ endif()
+
+A few key points should be noted in the above:
+
+- ``projB`` and ``projC`` define different content details for ``projD``,
+ but ``projA`` also defines a set of content details for ``projD`` and
+ because ``projA`` will define them first, the details from ``projB`` and
+ ``projC`` will not be used. The override details defined by ``projA``
+ are not required to match either of those from ``projB`` or ``projC``, but
+ it is up to the higher level project to ensure that the details it does
+ define still make sense for the child projects.
+- While ``projA`` defined content details for ``projD``, it did not need
+ to explicitly call ``FetchContent_Populate(projD)`` itself. Instead, it
+ leaves that to a child project to do (in this case it will be ``projB``
+ since it is added to the build ahead of ``projC``). If ``projA`` needed to
+ customize how the ``projD`` content was brought into the build as well
+ (e.g. define some CMake variables before calling
+ :command:`add_subdirectory` after populating), it would do the call to
+ ``FetchContent_Populate()``, etc. just as it did for the ``projB`` and
+ ``projC`` content. For higher level projects, it is usually enough to
+ just define the override content details and leave the actual population
+ to the child projects. This saves repeating the same thing at each level
+ of the project hierarchy unnecessarily.
+- Even though ``projA`` is the top level project in this example, it still
+ checks whether ``projB`` and ``projC`` have already been populated before
+ going ahead to do those populations. This makes ``projA`` able to be more
+ easily incorporated as a child of some other higher level project in the
+ future if required. Always protect a call to
+ :command:`FetchContent_Populate` with a check to
+ :command:`FetchContent_GetProperties`, even in what may be considered a top
+ level project at the time.
+
+
+The following example demonstrates how one might download and unpack a
+firmware tarball using CMake's :manual:`script mode <cmake(1)>`. The call to
+:command:`FetchContent_Populate` specifies all the content details and the
+unpacked firmware will be placed in a ``firmware`` directory below the
+current working directory.
+
+*getFirmware.cmake*:
+
+.. code-block:: cmake
+
+ # NOTE: Intended to be run in script mode with cmake -P
+ include(FetchContent)
+ FetchContent_Populate(
+ firmware
+ URL https://mycompany.com/assets/firmware-1.23-arm.tar.gz
+ URL_HASH MD5=68247684da89b608d466253762b0ff11
+ SOURCE_DIR firmware
+ )
+
+#]=======================================================================]
+
+
+set(__FetchContent_privateDir "${CMAKE_CURRENT_LIST_DIR}/FetchContent")
+
+#=======================================================================
+# Recording and retrieving content details for later population
+#=======================================================================
+
+# Internal use, projects must not call this directly. It is
+# intended for use by FetchContent_Declare() only.
+#
+# Sets a content-specific global property (not meant for use
+# outside of functions defined here in this file) which can later
+# be retrieved using __FetchContent_getSavedDetails() with just the
+# same content name. If there is already a value stored in the
+# property, it is left unchanged and this call has no effect.
+# This allows parent projects to define the content details,
+# overriding anything a child project may try to set (properties
+# are not cached between runs, so the first thing to set it in a
+# build will be in control).
+function(__FetchContent_declareDetails contentName)
+
+ string(TOLOWER ${contentName} contentNameLower)
+ set(propertyName "_FetchContent_${contentNameLower}_savedDetails")
+ get_property(alreadyDefined GLOBAL PROPERTY ${propertyName} DEFINED)
+ if(NOT alreadyDefined)
+ define_property(GLOBAL PROPERTY ${propertyName}
+ BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()"
+ FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}"
+ )
+ set_property(GLOBAL PROPERTY ${propertyName} ${ARGN})
+ endif()
+
+endfunction()
+
+
+# Internal use, projects must not call this directly. It is
+# intended for use by the FetchContent_Declare() function.
+#
+# Retrieves details saved for the specified content in an
+# earlier call to __FetchContent_declareDetails().
+function(__FetchContent_getSavedDetails contentName outVar)
+
+ string(TOLOWER ${contentName} contentNameLower)
+ set(propertyName "_FetchContent_${contentNameLower}_savedDetails")
+ get_property(alreadyDefined GLOBAL PROPERTY ${propertyName} DEFINED)
+ if(NOT alreadyDefined)
+ message(FATAL_ERROR "No content details recorded for ${contentName}")
+ endif()
+ get_property(propertyValue GLOBAL PROPERTY ${propertyName})
+ set(${outVar} "${propertyValue}" PARENT_SCOPE)
+
+endfunction()
+
+
+# Saves population details of the content, sets defaults for the
+# SOURCE_DIR and BUILD_DIR.
+function(FetchContent_Declare contentName)
+
+ set(options "")
+ set(oneValueArgs SVN_REPOSITORY)
+ set(multiValueArgs "")
+
+ cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ unset(srcDirSuffix)
+ unset(svnRepoArgs)
+ if(ARG_SVN_REPOSITORY)
+ # Add a hash of the svn repository URL to the source dir. This works
+ # around the problem where if the URL changes, the download would
+ # fail because it tries to checkout/update rather than switch the
+ # old URL to the new one. We limit the hash to the first 7 characters
+ # so that the source path doesn't get overly long (which can be a
+ # problem on windows due to path length limits).
+ string(SHA1 urlSHA ${ARG_SVN_REPOSITORY})
+ string(SUBSTRING ${urlSHA} 0 7 urlSHA)
+ set(srcDirSuffix "-${urlSHA}")
+ set(svnRepoArgs SVN_REPOSITORY ${ARG_SVN_REPOSITORY})
+ endif()
+
+ string(TOLOWER ${contentName} contentNameLower)
+ __FetchContent_declareDetails(
+ ${contentNameLower}
+ SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src${srcDirSuffix}"
+ BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build"
+ ${svnRepoArgs}
+ # List these last so they can override things we set above
+ ${ARG_UNPARSED_ARGUMENTS}
+ )
+
+endfunction()
+
+
+#=======================================================================
+# Set/get whether the specified content has been populated yet.
+# The setter also records the source and binary dirs used.
+#=======================================================================
+
+# Internal use, projects must not call this directly. It is
+# intended for use by the FetchContent_Populate() function to
+# record when FetchContent_Populate() is called for a particular
+# content name.
+function(__FetchContent_setPopulated contentName sourceDir binaryDir)
+
+ string(TOLOWER ${contentName} contentNameLower)
+ set(prefix "_FetchContent_${contentNameLower}")
+
+ set(propertyName "${prefix}_sourceDir")
+ define_property(GLOBAL PROPERTY ${propertyName}
+ BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()"
+ FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}"
+ )
+ set_property(GLOBAL PROPERTY ${propertyName} ${sourceDir})
+
+ set(propertyName "${prefix}_binaryDir")
+ define_property(GLOBAL PROPERTY ${propertyName}
+ BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()"
+ FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}"
+ )
+ set_property(GLOBAL PROPERTY ${propertyName} ${binaryDir})
+
+ set(propertyName "${prefix}_populated")
+ define_property(GLOBAL PROPERTY ${propertyName}
+ BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()"
+ FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}"
+ )
+ set_property(GLOBAL PROPERTY ${propertyName} True)
+
+endfunction()
+
+
+# Set variables in the calling scope for any of the retrievable
+# properties. If no specific properties are requested, variables
+# will be set for all retrievable properties.
+#
+# This function is intended to also be used by projects as the canonical
+# way to detect whether they should call FetchContent_Populate()
+# and pull the populated source into the build with add_subdirectory(),
+# if they are using the populated content in that way.
+function(FetchContent_GetProperties contentName)
+
+ string(TOLOWER ${contentName} contentNameLower)
+
+ set(options "")
+ set(oneValueArgs SOURCE_DIR BINARY_DIR POPULATED)
+ set(multiValueArgs "")
+
+ cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if(NOT ARG_SOURCE_DIR AND
+ NOT ARG_BINARY_DIR AND
+ NOT ARG_POPULATED)
+ # No specific properties requested, provide them all
+ set(ARG_SOURCE_DIR ${contentNameLower}_SOURCE_DIR)
+ set(ARG_BINARY_DIR ${contentNameLower}_BINARY_DIR)
+ set(ARG_POPULATED ${contentNameLower}_POPULATED)
+ endif()
+
+ set(prefix "_FetchContent_${contentNameLower}")
+
+ if(ARG_SOURCE_DIR)
+ set(propertyName "${prefix}_sourceDir")
+ get_property(value GLOBAL PROPERTY ${propertyName})
+ if(value)
+ set(${ARG_SOURCE_DIR} ${value} PARENT_SCOPE)
+ endif()
+ endif()
+
+ if(ARG_BINARY_DIR)
+ set(propertyName "${prefix}_binaryDir")
+ get_property(value GLOBAL PROPERTY ${propertyName})
+ if(value)
+ set(${ARG_BINARY_DIR} ${value} PARENT_SCOPE)
+ endif()
+ endif()
+
+ if(ARG_POPULATED)
+ set(propertyName "${prefix}_populated")
+ get_property(value GLOBAL PROPERTY ${propertyName} DEFINED)
+ set(${ARG_POPULATED} ${value} PARENT_SCOPE)
+ endif()
+
+endfunction()
+
+
+#=======================================================================
+# Performing the population
+#=======================================================================
+
+# The value of contentName will always have been lowercased by the caller.
+# All other arguments are assumed to be options that are understood by
+# ExternalProject_Add(), except for QUIET and SUBBUILD_DIR.
+function(__FetchContent_directPopulate contentName)
+
+ set(options
+ QUIET
+ )
+ set(oneValueArgs
+ SUBBUILD_DIR
+ SOURCE_DIR
+ BINARY_DIR
+ # Prevent the following from being passed through
+ CONFIGURE_COMMAND
+ BUILD_COMMAND
+ INSTALL_COMMAND
+ TEST_COMMAND
+ )
+ set(multiValueArgs "")
+
+ cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if(NOT ARG_SUBBUILD_DIR)
+ message(FATAL_ERROR "Internal error: SUBBUILD_DIR not set")
+ elseif(NOT IS_ABSOLUTE "${ARG_SUBBUILD_DIR}")
+ set(ARG_SUBBUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${ARG_SUBBUILD_DIR}")
+ endif()
+
+ if(NOT ARG_SOURCE_DIR)
+ message(FATAL_ERROR "Internal error: SOURCE_DIR not set")
+ elseif(NOT IS_ABSOLUTE "${ARG_SOURCE_DIR}")
+ set(ARG_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/${ARG_SOURCE_DIR}")
+ endif()
+
+ if(NOT ARG_BINARY_DIR)
+ message(FATAL_ERROR "Internal error: BINARY_DIR not set")
+ elseif(NOT IS_ABSOLUTE "${ARG_BINARY_DIR}")
+ set(ARG_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/${ARG_BINARY_DIR}")
+ endif()
+
+ # Ensure the caller can know where to find the source and build directories
+ # with some convenient variables. Doing this here ensures the caller sees
+ # the correct result in the case where the default values are overridden by
+ # the content details set by the project.
+ set(${contentName}_SOURCE_DIR "${ARG_SOURCE_DIR}" PARENT_SCOPE)
+ set(${contentName}_BINARY_DIR "${ARG_BINARY_DIR}" PARENT_SCOPE)
+
+ # The unparsed arguments may contain spaces, so build up ARG_EXTRA
+ # in such a way that it correctly substitutes into the generated
+ # CMakeLists.txt file with each argument quoted.
+ unset(ARG_EXTRA)
+ foreach(arg IN LISTS ARG_UNPARSED_ARGUMENTS)
+ set(ARG_EXTRA "${ARG_EXTRA} \"${arg}\"")
+ endforeach()
+
+ # Hide output if requested, but save it to a variable in case there's an
+ # error so we can show the output upon failure. When not quiet, don't
+ # capture the output to a variable because the user may want to see the
+ # output as it happens (e.g. progress during long downloads). Combine both
+ # stdout and stderr in the one capture variable so the output stays in order.
+ if (ARG_QUIET)
+ set(outputOptions
+ OUTPUT_VARIABLE capturedOutput
+ ERROR_VARIABLE capturedOutput
+ )
+ else()
+ set(capturedOutput)
+ set(outputOptions)
+ message(STATUS "Populating ${contentName}")
+ endif()
+
+ if(CMAKE_GENERATOR)
+ set(generatorOpts "-G${CMAKE_GENERATOR}")
+ if(CMAKE_GENERATOR_PLATFORM)
+ list(APPEND generatorOpts "-A${CMAKE_GENERATOR_PLATFORM}")
+ endif()
+ if(CMAKE_GENERATOR_TOOLSET)
+ list(APPEND generatorOpts "-T${CMAKE_GENERATOR_TOOLSET}")
+ endif()
+
+ list(APPEND generatorOpts "-DCMAKE_MAKE_PROGRAM:FILE=${CMAKE_MAKE_PROGRAM}")
+
+ else()
+ # Likely we've been invoked via CMake's script mode where no
+ # generator is set (and hence CMAKE_MAKE_PROGRAM could not be
+ # trusted even if provided). We will have to rely on being
+ # able to find the default generator and build tool.
+ unset(generatorOpts)
+ endif()
+
+ # Create and build a separate CMake project to carry out the population.
+ # If we've already previously done these steps, they will not cause
+ # anything to be updated, so extra rebuilds of the project won't occur.
+ # Make sure to pass through CMAKE_MAKE_PROGRAM in case the main project
+ # has this set to something not findable on the PATH.
+ configure_file("${__FetchContent_privateDir}/CMakeLists.cmake.in"
+ "${ARG_SUBBUILD_DIR}/CMakeLists.txt")
+ execute_process(
+ COMMAND ${CMAKE_COMMAND} ${generatorOpts} .
+ RESULT_VARIABLE result
+ ${outputOptions}
+ WORKING_DIRECTORY "${ARG_SUBBUILD_DIR}"
+ )
+ if(result)
+ if(capturedOutput)
+ message("${capturedOutput}")
+ endif()
+ message(FATAL_ERROR "CMake step for ${contentName} failed: ${result}")
+ endif()
+ execute_process(
+ COMMAND ${CMAKE_COMMAND} --build .
+ RESULT_VARIABLE result
+ ${outputOptions}
+ WORKING_DIRECTORY "${ARG_SUBBUILD_DIR}"
+ )
+ if(result)
+ if(capturedOutput)
+ message("${capturedOutput}")
+ endif()
+ message(FATAL_ERROR "Build step for ${contentName} failed: ${result}")
+ endif()
+
+endfunction()
+
+
+option(FETCHCONTENT_FULLY_DISCONNECTED "Disables all attempts to download or update content and assumes source dirs already exist")
+option(FETCHCONTENT_UPDATES_DISCONNECTED "Enables UPDATE_DISCONNECTED behavior for all content population")
+option(FETCHCONTENT_QUIET "Enables QUIET option for all content population" ON)
+set(FETCHCONTENT_BASE_DIR "${CMAKE_BINARY_DIR}/_deps" CACHE PATH "Directory under which to collect all populated content")
+
+# Populate the specified content using details stored from
+# an earlier call to FetchContent_Declare().
+function(FetchContent_Populate contentName)
+
+ if(NOT contentName)
+ message(FATAL_ERROR "Empty contentName not allowed for FetchContent_Populate()")
+ endif()
+
+ string(TOLOWER ${contentName} contentNameLower)
+
+ if(ARGN)
+ # This is the direct population form with details fully specified
+ # as part of the call, so we already have everything we need
+ __FetchContent_directPopulate(
+ ${contentNameLower}
+ SUBBUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-subbuild"
+ SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-src"
+ BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-build"
+ ${ARGN} # Could override any of the above ..._DIR variables
+ )
+
+ # Pass source and binary dir variables back to the caller
+ set(${contentNameLower}_SOURCE_DIR "${${contentNameLower}_SOURCE_DIR}" PARENT_SCOPE)
+ set(${contentNameLower}_BINARY_DIR "${${contentNameLower}_BINARY_DIR}" PARENT_SCOPE)
+
+ # Don't set global properties, or record that we did this population, since
+ # this was a direct call outside of the normal declared details form.
+ # We only want to save values in the global properties for content that
+ # honours the hierarchical details mechanism so that projects are not
+ # robbed of the ability to override details set in nested projects.
+ return()
+ endif()
+
+ # No details provided, so assume they were saved from an earlier call
+ # to FetchContent_Declare(). Do a check that we haven't already
+ # populated this content before in case the caller forgot to check.
+ FetchContent_GetProperties(${contentName})
+ if(${contentNameLower}_POPULATED)
+ message(FATAL_ERROR "Content ${contentName} already populated in ${${contentNameLower}_SOURCE_DIR}")
+ endif()
+
+ string(TOUPPER ${contentName} contentNameUpper)
+ set(FETCHCONTENT_SOURCE_DIR_${contentNameUpper}
+ "${FETCHCONTENT_SOURCE_DIR_${contentNameUpper}}"
+ CACHE PATH "When not empty, overrides where to find pre-populated content for ${contentName}")
+
+ if(FETCHCONTENT_SOURCE_DIR_${contentNameUpper})
+ # The source directory has been explicitly provided in the cache,
+ # so no population is required
+ set(${contentNameLower}_SOURCE_DIR "${FETCHCONTENT_SOURCE_DIR_${contentNameUpper}}")
+ set(${contentNameLower}_BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build")
+
+ elseif(FETCHCONTENT_FULLY_DISCONNECTED)
+ # Bypass population and assume source is already there from a previous run
+ set(${contentNameLower}_SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src")
+ set(${contentNameLower}_BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build")
+
+ else()
+ # Support both a global "disconnect all updates" and a per-content
+ # update test (either one being set disables updates for this content).
+ option(FETCHCONTENT_UPDATES_DISCONNECTED_${contentNameUpper}
+ "Enables UPDATE_DISCONNECTED behavior just for population of ${contentName}")
+ if(FETCHCONTENT_UPDATES_DISCONNECTED OR
+ FETCHCONTENT_UPDATES_DISCONNECTED_${contentNameUpper})
+ set(disconnectUpdates True)
+ else()
+ set(disconnectUpdates False)
+ endif()
+
+ if(FETCHCONTENT_QUIET)
+ set(quietFlag QUIET)
+ else()
+ unset(quietFlag)
+ endif()
+
+ __FetchContent_getSavedDetails(${contentName} contentDetails)
+ if("${contentDetails}" STREQUAL "")
+ message(FATAL_ERROR "No details have been set for content: ${contentName}")
+ endif()
+
+ __FetchContent_directPopulate(
+ ${contentNameLower}
+ ${quietFlag}
+ UPDATE_DISCONNECTED ${disconnectUpdates}
+ SUBBUILD_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-subbuild"
+ SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src"
+ BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build"
+ # Put the saved details last so they can override any of the
+ # the options we set above (this can include SOURCE_DIR or
+ # BUILD_DIR)
+ ${contentDetails}
+ )
+ endif()
+
+ __FetchContent_setPopulated(
+ ${contentName}
+ ${${contentNameLower}_SOURCE_DIR}
+ ${${contentNameLower}_BINARY_DIR}
+ )
+
+ # Pass variables back to the caller. The variables passed back here
+ # must match what FetchContent_GetProperties() sets when it is called
+ # with just the content name.
+ set(${contentNameLower}_SOURCE_DIR "${${contentNameLower}_SOURCE_DIR}" PARENT_SCOPE)
+ set(${contentNameLower}_BINARY_DIR "${${contentNameLower}_BINARY_DIR}" PARENT_SCOPE)
+ set(${contentNameLower}_POPULATED True PARENT_SCOPE)
+
+endfunction()
diff --git a/Modules/FetchContent/CMakeLists.cmake.in b/Modules/FetchContent/CMakeLists.cmake.in
new file mode 100644
index 0000000..9a7a771
--- /dev/null
+++ b/Modules/FetchContent/CMakeLists.cmake.in
@@ -0,0 +1,21 @@
+# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+cmake_minimum_required(VERSION ${CMAKE_VERSION})
+
+# We name the project and the target for the ExternalProject_Add() call
+# to something that will highlight to the user what we are working on if
+# something goes wrong and an error message is produced.
+
+project(${contentName}-populate NONE)
+
+include(ExternalProject)
+ExternalProject_Add(${contentName}-populate
+ ${ARG_EXTRA}
+ SOURCE_DIR "${ARG_SOURCE_DIR}"
+ BINARY_DIR "${ARG_BINARY_DIR}"
+ CONFIGURE_COMMAND ""
+ BUILD_COMMAND ""
+ INSTALL_COMMAND ""
+ TEST_COMMAND ""
+)
diff --git a/Modules/FindOpenCL.cmake b/Modules/FindOpenCL.cmake
index b8a7d82..ba87086 100644
--- a/Modules/FindOpenCL.cmake
+++ b/Modules/FindOpenCL.cmake
@@ -120,9 +120,12 @@ else()
NAMES OpenCL
PATHS
ENV AMDAPPSDKROOT
+ ENV CUDA_PATH
PATH_SUFFIXES
lib/x86_64
- lib/x64)
+ lib/x64
+ lib
+ lib64)
endif()
set(OpenCL_LIBRARIES ${OpenCL_LIBRARY})
diff --git a/Modules/FindPythonLibs.cmake b/Modules/FindPythonLibs.cmake
index 63ec9a8..341d5d9 100644
--- a/Modules/FindPythonLibs.cmake
+++ b/Modules/FindPythonLibs.cmake
@@ -122,6 +122,7 @@ foreach(_CURRENT_VERSION ${_Python_VERSIONS})
if(WIN32)
find_library(PYTHON_DEBUG_LIBRARY
NAMES python${_CURRENT_VERSION_NO_DOTS}_d python
+ NAMES_PER_DIR
HINTS ${_Python_LIBRARY_PATH_HINT}
PATHS
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}\\InstallPath]/libs/Debug
@@ -145,20 +146,18 @@ foreach(_CURRENT_VERSION ${_Python_VERSIONS})
python${_CURRENT_VERSION}m
python${_CURRENT_VERSION}u
python${_CURRENT_VERSION}
+ NAMES_PER_DIR
HINTS
${_Python_LIBRARY_PATH_HINT}
PATHS
${PYTHON_FRAMEWORK_LIBRARIES}
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}\\InstallPath]/libs
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}\\InstallPath]/libs
- # Avoid finding the .dll in the PATH. We want the .lib.
- NO_SYSTEM_ENVIRONMENT_PATH
)
# Look for the static library in the Python config directory
find_library(PYTHON_LIBRARY
NAMES python${_CURRENT_VERSION_NO_DOTS} python${_CURRENT_VERSION}
- # Avoid finding the .dll in the PATH. We want the .lib.
- NO_SYSTEM_ENVIRONMENT_PATH
+ NAMES_PER_DIR
# This is where the static library is usually located
PATH_SUFFIXES python${_CURRENT_VERSION}/config
)
diff --git a/Modules/UseJava.cmake b/Modules/UseJava.cmake
index 1182875..5e06adc 100644
--- a/Modules/UseJava.cmake
+++ b/Modules/UseJava.cmake
@@ -481,6 +481,8 @@ function(add_jar _TARGET_NAME)
else()
get_filename_component(_add_jar_OUTPUT_DIR ${_add_jar_OUTPUT_DIR} ABSOLUTE)
endif()
+ # ensure output directory exists
+ file (MAKE_DIRECTORY "${_add_jar_OUTPUT_DIR}")
if (_add_jar_ENTRY_POINT)
set(_ENTRY_POINT_OPTION e)
@@ -515,7 +517,7 @@ function(add_jar _TARGET_NAME)
string(APPEND CMAKE_JAVA_INCLUDE_PATH_FINAL "${CMAKE_JAVA_INCLUDE_FLAG_SEP}${JAVA_INCLUDE_DIR}")
endforeach()
- set(CMAKE_JAVA_CLASS_OUTPUT_PATH "${_add_jar_OUTPUT_DIR}${CMAKE_FILES_DIRECTORY}/${_TARGET_NAME}.dir")
+ set(CMAKE_JAVA_CLASS_OUTPUT_PATH "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${_TARGET_NAME}.dir")
set(_JAVA_TARGET_OUTPUT_NAME "${_TARGET_NAME}.jar")
if (_add_jar_OUTPUT_NAME AND _add_jar_VERSION)
@@ -546,7 +548,7 @@ function(add_jar _TARGET_NAME)
list(APPEND _JAVA_COMPILE_FILELISTS ${_JAVA_FULL})
elseif (_JAVA_EXT MATCHES ".java")
- file(RELATIVE_PATH _JAVA_REL_BINARY_PATH ${_add_jar_OUTPUT_DIR} ${_JAVA_FULL})
+ file(RELATIVE_PATH _JAVA_REL_BINARY_PATH ${CMAKE_CURRENT_BINARY_DIR} ${_JAVA_FULL})
file(RELATIVE_PATH _JAVA_REL_SOURCE_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${_JAVA_FULL})
string(LENGTH ${_JAVA_REL_BINARY_PATH} _BIN_LEN)
string(LENGTH ${_JAVA_REL_SOURCE_PATH} _SRC_LEN)
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index a4dd918..e1c34bf 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -936,8 +936,13 @@ if(UNIX)
endif()
endif()
-if(WIN32)
+if(CYGWIN)
+ find_package(LibUUID)
+endif()
+if(WIN32 OR (CYGWIN AND LibUUID_FOUND))
set(CPACK_SRCS ${CPACK_SRCS}
+ CPack/Wix/cmCMakeToWixPath.cxx
+ CPack/Wix/cmCMakeToWixPath.h
CPack/WiX/cmCPackWIXGenerator.cxx
CPack/WiX/cmCPackWIXGenerator.h
CPack/WiX/cmWIXAccessControlList.cxx
@@ -958,7 +963,7 @@ if(WIN32)
CPack/WiX/cmWIXShortcut.h
CPack/WiX/cmWIXSourceWriter.cxx
CPack/WiX/cmWIXSourceWriter.h
- )
+ )
endif()
if(APPLE)
@@ -991,6 +996,11 @@ if(APPLE)
"See CMakeFiles/CMakeError.log for details of the failure.")
endif()
endif()
+if(CYGWIN AND LibUUID_FOUND)
+ target_link_libraries(CPackLib ${LibUUID_LIBRARIES})
+ include_directories(CPackLib ${LibUUID_INCLUDE_DIRS})
+ set_property(SOURCE CPack/cmCPackGeneratorFactory.cxx PROPERTY COMPILE_DEFINITIONS HAVE_LIBUUID)
+endif()
if(CPACK_ENABLE_FREEBSD_PKG AND FREEBSD_PKG_INCLUDE_DIRS AND FREEBSD_PKG_LIBRARIES)
target_link_libraries(CPackLib ${FREEBSD_PKG_LIBRARIES})
include_directories(${FREEBSD_PKG_INCLUDE_DIRS})
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 9eaee8a..cb9c370 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,5 +1,5 @@
# CMake version number components.
set(CMake_VERSION_MAJOR 3)
set(CMake_VERSION_MINOR 10)
-set(CMake_VERSION_PATCH 0)
-set(CMake_VERSION_RC 2)
+set(CMake_VERSION_PATCH 20171018)
+#set(CMake_VERSION_RC 1)
diff --git a/Source/CPack/WiX/cmCMakeToWixPath.cxx b/Source/CPack/WiX/cmCMakeToWixPath.cxx
new file mode 100644
index 0000000..0b0e42a
--- /dev/null
+++ b/Source/CPack/WiX/cmCMakeToWixPath.cxx
@@ -0,0 +1,39 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmCMakeToWixPath.h"
+
+#include "cmSystemTools.h"
+
+#include <string>
+#include <vector>
+
+#ifdef __CYGWIN__
+#include <sys/cygwin.h>
+std::string CMakeToWixPath(const std::string& cygpath)
+{
+ std::vector<char> winpath_chars;
+ ssize_t winpath_size;
+
+ // Get the required buffer size.
+ winpath_size =
+ cygwin_conv_path(CCP_POSIX_TO_WIN_A, cygpath.c_str(), nullptr, 0);
+ if (winpath_size <= 0) {
+ return cygpath;
+ }
+
+ winpath_chars.assign(static_cast<size_t>(winpath_size) + 1, '\0');
+
+ winpath_size = cygwin_conv_path(CCP_POSIX_TO_WIN_A, cygpath.c_str(),
+ winpath_chars.data(), winpath_size);
+ if (winpath_size < 0) {
+ return cygpath;
+ }
+
+ return cmSystemTools::TrimWhitespace(winpath_chars.data());
+}
+#else
+std::string CMakeToWixPath(const std::string& path)
+{
+ return path;
+}
+#endif
diff --git a/Source/CPack/WiX/cmCMakeToWixPath.h b/Source/CPack/WiX/cmCMakeToWixPath.h
new file mode 100644
index 0000000..8bb9e04
--- /dev/null
+++ b/Source/CPack/WiX/cmCMakeToWixPath.h
@@ -0,0 +1,12 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmCMakeToWixPath_h
+#define cmCMakeToWixPath_h
+
+#include "cmConfigure.h" //IWYU pragma: keep
+
+#include <string>
+
+std::string CMakeToWixPath(const std::string& cygpath);
+
+#endif // cmCMakeToWixPath_h
diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.cxx b/Source/CPack/WiX/cmCPackWIXGenerator.cxx
index ba07d08..a0bc0ea 100644
--- a/Source/CPack/WiX/cmCPackWIXGenerator.cxx
+++ b/Source/CPack/WiX/cmCPackWIXGenerator.cxx
@@ -22,7 +22,13 @@
#include "cmsys/FStream.hxx"
#include "cmsys/SystemTools.hxx"
-#include <rpc.h> // for GUID generation
+#ifdef _WIN32
+#include <rpc.h> // for GUID generation (windows only)
+#else
+#include <uuid/uuid.h> // for GUID generation (libuuid)
+#endif
+
+#include "cmCMakeToWixPath.h"
cmCPackWIXGenerator::cmCPackWIXGenerator()
: Patch(0)
@@ -110,7 +116,7 @@ bool cmCPackWIXGenerator::RunLightCommand(std::string const& objectFiles)
std::ostringstream command;
command << QuotePath(executable);
command << " -nologo";
- command << " -out " << QuotePath(packageFileNames.at(0));
+ command << " -out " << QuotePath(CMakeToWixPath(packageFileNames.at(0)));
for (std::string const& ext : this->LightExtensions) {
command << " -ext " << QuotePath(ext);
@@ -270,11 +276,12 @@ bool cmCPackWIXGenerator::PackageFilesImpl()
std::string objectFilename =
this->CPackTopLevel + "/" + uniqueBaseName + ".wixobj";
- if (!RunCandleCommand(sourceFilename, objectFilename)) {
+ if (!RunCandleCommand(CMakeToWixPath(sourceFilename),
+ CMakeToWixPath(objectFilename))) {
return false;
}
- objectFiles << " " << QuotePath(objectFilename);
+ objectFiles << " " << QuotePath(CMakeToWixPath(objectFilename));
}
AppendUserSuppliedExtraObjects(objectFiles);
@@ -320,10 +327,10 @@ void cmCPackWIXGenerator::CreateWiXVariablesIncludeFile()
CopyDefinition(includeFile, "CPACK_PACKAGE_VENDOR");
CopyDefinition(includeFile, "CPACK_PACKAGE_NAME");
CopyDefinition(includeFile, "CPACK_PACKAGE_VERSION");
- CopyDefinition(includeFile, "CPACK_WIX_LICENSE_RTF");
- CopyDefinition(includeFile, "CPACK_WIX_PRODUCT_ICON");
- CopyDefinition(includeFile, "CPACK_WIX_UI_BANNER");
- CopyDefinition(includeFile, "CPACK_WIX_UI_DIALOG");
+ CopyDefinition(includeFile, "CPACK_WIX_LICENSE_RTF", DefinitionType::PATH);
+ CopyDefinition(includeFile, "CPACK_WIX_PRODUCT_ICON", DefinitionType::PATH);
+ CopyDefinition(includeFile, "CPACK_WIX_UI_BANNER", DefinitionType::PATH);
+ CopyDefinition(includeFile, "CPACK_WIX_UI_DIALOG", DefinitionType::PATH);
SetOptionIfNotSet("CPACK_WIX_PROGRAM_MENU_FOLDER",
GetOption("CPACK_PACKAGE_NAME"));
CopyDefinition(includeFile, "CPACK_WIX_PROGRAM_MENU_FOLDER");
@@ -390,11 +397,16 @@ void cmCPackWIXGenerator::CreateWiXProductFragmentIncludeFile()
}
void cmCPackWIXGenerator::CopyDefinition(cmWIXSourceWriter& source,
- std::string const& name)
+ std::string const& name,
+ DefinitionType type)
{
const char* value = GetOption(name.c_str());
if (value) {
- AddDefinition(source, name, value);
+ if (type == DefinitionType::PATH) {
+ AddDefinition(source, name, CMakeToWixPath(value));
+ } else {
+ AddDefinition(source, name, value);
+ }
}
}
@@ -966,6 +978,7 @@ std::string cmCPackWIXGenerator::GetArchitecture() const
std::string cmCPackWIXGenerator::GenerateGUID()
{
+#ifdef _WIN32
UUID guid;
UuidCreate(&guid);
@@ -975,6 +988,14 @@ std::string cmCPackWIXGenerator::GenerateGUID()
std::string result =
cmsys::Encoding::ToNarrow(reinterpret_cast<wchar_t*>(tmp));
RpcStringFreeW(&tmp);
+#else
+ uuid_t guid;
+ char guid_ch[37] = { 0 };
+
+ uuid_generate(guid);
+ uuid_unparse(guid, guid_ch);
+ std::string result = guid_ch;
+#endif
return cmSystemTools::UpperCase(result);
}
diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.h b/Source/CPack/WiX/cmCPackWIXGenerator.h
index b2633a7..128a04d 100644
--- a/Source/CPack/WiX/cmCPackWIXGenerator.h
+++ b/Source/CPack/WiX/cmCPackWIXGenerator.h
@@ -48,6 +48,12 @@ private:
typedef std::map<std::string, size_t> ambiguity_map_t;
typedef std::set<std::string> extension_set_t;
+ enum class DefinitionType
+ {
+ STRING,
+ PATH
+ };
+
bool InitializeWiXConfiguration();
bool PackageFilesImpl();
@@ -58,7 +64,8 @@ private:
void CreateWiXProductFragmentIncludeFile();
- void CopyDefinition(cmWIXSourceWriter& source, std::string const& name);
+ void CopyDefinition(cmWIXSourceWriter& source, std::string const& name,
+ DefinitionType type = DefinitionType::STRING);
void AddDefinition(cmWIXSourceWriter& source, std::string const& name,
std::string const& value);
diff --git a/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx b/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx
index b4cd1a3..dd3caf9 100644
--- a/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx
+++ b/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx
@@ -11,6 +11,8 @@
#include "cm_sys_stat.h"
+#include "cmCMakeToWixPath.h"
+
cmWIXFilesSourceWriter::cmWIXFilesSourceWriter(cmCPackLog* logger,
std::string const& filename,
GuidType componentGuidType)
@@ -139,7 +141,7 @@ std::string cmWIXFilesSourceWriter::EmitComponentFile(
patch.ApplyFragment(componentId, *this);
BeginElement("File");
AddAttribute("Id", fileId);
- AddAttribute("Source", filePath);
+ AddAttribute("Source", CMakeToWixPath(filePath));
AddAttribute("KeyPath", "yes");
mode_t fileMode = 0;
diff --git a/Source/CPack/cmCPackDragNDropGenerator.cxx b/Source/CPack/cmCPackDragNDropGenerator.cxx
index 1e1543f..bb35623 100644
--- a/Source/CPack/cmCPackDragNDropGenerator.cxx
+++ b/Source/CPack/cmCPackDragNDropGenerator.cxx
@@ -411,6 +411,7 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir,
std::string temp_image = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
temp_image += "/temp.dmg";
+ std::string create_error;
std::ostringstream temp_image_command;
temp_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
temp_image_command << " create";
@@ -421,9 +422,11 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir,
temp_image_command << " -format " << temp_image_format;
temp_image_command << " \"" << temp_image << "\"";
- if (!this->RunCommand(temp_image_command)) {
+ if (!this->RunCommand(temp_image_command, &create_error)) {
cmCPackLogger(cmCPackLog::LOG_ERROR,
- "Error generating temporary disk image." << std::endl);
+ "Error generating temporary disk image." << std::endl
+ << create_error
+ << std::endl);
return 0;
}
@@ -464,15 +467,17 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir,
// Optionally set the custom icon flag for the image ...
if (!had_error && !cpack_package_icon.empty()) {
+ std::string error;
std::ostringstream setfile_command;
setfile_command << this->GetOption("CPACK_COMMAND_SETFILE");
setfile_command << " -a C";
setfile_command << " \"" << temp_mount << "\"";
- if (!this->RunCommand(setfile_command)) {
+ if (!this->RunCommand(setfile_command, &error)) {
cmCPackLogger(cmCPackLog::LOG_ERROR,
"Error assigning custom icon to temporary disk image."
- << std::endl);
+ << std::endl
+ << error << std::endl);
had_error = true;
}
@@ -717,9 +722,12 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir,
final_image_command << " zlib-level=9";
final_image_command << " -o \"" << output_file << "\"";
- if (!this->RunCommand(final_image_command)) {
+ std::string convert_error;
+
+ if (!this->RunCommand(final_image_command, &convert_error)) {
cmCPackLogger(cmCPackLog::LOG_ERROR, "Error compressing disk image."
- << std::endl);
+ << std::endl
+ << convert_error << std::endl);
return 0;
}
diff --git a/Source/CPack/cmCPackGeneratorFactory.cxx b/Source/CPack/cmCPackGeneratorFactory.cxx
index 4b81bbc..47e7527 100644
--- a/Source/CPack/cmCPackGeneratorFactory.cxx
+++ b/Source/CPack/cmCPackGeneratorFactory.cxx
@@ -40,7 +40,7 @@
#include "cmCPackRPMGenerator.h"
#endif
-#ifdef _WIN32
+#if defined(_WIN32) || (defined(__CYGWIN__) && defined(HAVE_LIBUUID))
#include "WiX/cmCPackWIXGenerator.h"
#endif
@@ -87,7 +87,7 @@ cmCPackGeneratorFactory::cmCPackGeneratorFactory()
this->RegisterGenerator("7Z", "7-Zip file format",
cmCPack7zGenerator::CreateGenerator);
}
-#ifdef _WIN32
+#if defined(_WIN32) || (defined(__CYGWIN__) && defined(HAVE_LIBUUID))
if (cmCPackWIXGenerator::CanGenerate()) {
this->RegisterGenerator("WIX", "MSI file format via WiX tools",
cmCPackWIXGenerator::CreateGenerator);
diff --git a/Source/Modules/FindLibUUID.cmake b/Source/Modules/FindLibUUID.cmake
new file mode 100644
index 0000000..17f11c1
--- /dev/null
+++ b/Source/Modules/FindLibUUID.cmake
@@ -0,0 +1,85 @@
+# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#[=======================================================================[.rst:
+FindLibUUID
+------------
+
+Find LibUUID include directory and library.
+
+Imported Targets
+^^^^^^^^^^^^^^^^
+
+An :ref:`imported target <Imported targets>` named
+``LibUUID::LibUUID`` is provided if LibUUID has been found.
+
+Result Variables
+^^^^^^^^^^^^^^^^
+
+This module defines the following variables:
+
+``LibUUID_FOUND``
+ True if LibUUID was found, false otherwise.
+``LibUUID_INCLUDE_DIRS``
+ Include directories needed to include LibUUID headers.
+``LibUUID_LIBRARIES``
+ Libraries needed to link to LibUUID.
+
+Cache Variables
+^^^^^^^^^^^^^^^
+
+This module uses the following cache variables:
+
+``LibUUID_LIBRARY``
+ The location of the LibUUID library file.
+``LibUUID_INCLUDE_DIR``
+ The location of the LibUUID include directory containing ``uuid/uuid.h``.
+
+The cache variables should not be used by project code.
+They may be set by end users to point at LibUUID components.
+#]=======================================================================]
+
+#-----------------------------------------------------------------------------
+if(CYGWIN)
+ # Note: on current version of Cygwin, linking to libuuid.dll.a doesn't
+ # import the right symbols sometimes. Fix this by linking directly
+ # to the DLL that provides the symbols, instead.
+ set(old_suffixes ${CMAKE_FIND_LIBRARY_SUFFIXES})
+ set(CMAKE_FIND_LIBRARY_SUFFIXES .dll)
+ find_library(LibUUID_LIBRARY
+ NAMES cyguuid-1.dll
+ )
+ set(CMAKE_FIND_LIBRARY_SUFFIXES ${old_suffixes})
+else()
+ find_library(LibUUID_LIBRARY
+ NAMES uuid
+ )
+endif()
+mark_as_advanced(LibUUID_LIBRARY)
+
+find_path(LibUUID_INCLUDE_DIR
+ NAMES uuid/uuid.h
+ )
+mark_as_advanced(LibUUID_INCLUDE_DIR)
+
+#-----------------------------------------------------------------------------
+include(${CMAKE_CURRENT_LIST_DIR}/../../Modules/FindPackageHandleStandardArgs.cmake)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibUUID
+ FOUND_VAR LibUUID_FOUND
+ REQUIRED_VARS LibUUID_LIBRARY LibUUID_INCLUDE_DIR
+ )
+set(LIBUUID_FOUND ${LibUUID_FOUND})
+
+#-----------------------------------------------------------------------------
+# Provide documented result variables and targets.
+if(LibUUID_FOUND)
+ set(LibUUID_INCLUDE_DIRS ${LibUUID_INCLUDE_DIR})
+ set(LibUUID_LIBRARIES ${LibUUID_LIBRARY})
+ if(NOT TARGET LibUUID::LibUUID)
+ add_library(LibUUID::LibUUID UNKNOWN IMPORTED)
+ set_target_properties(LibUUID::LibUUID PROPERTIES
+ IMPORTED_LOCATION "${LibUUID_LIBRARY}"
+ INTERFACE_INCLUDE_DIRECTORIES "${LibUUID_INCLUDE_DIRS}"
+ )
+ endif()
+endif()
diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx
index bbb2395..5be9ec3 100644
--- a/Source/QtDialog/CMakeSetupDialog.cxx
+++ b/Source/QtDialog/CMakeSetupDialog.cxx
@@ -188,6 +188,9 @@ CMakeSetupDialog::CMakeSetupDialog()
connect(this->Output, SIGNAL(customContextMenuRequested(const QPoint&)),
this, SLOT(doOutputContextMenu(const QPoint&)));
+ // disable open project button
+ this->OpenProjectButton->setDisabled(true);
+
// start the cmake worker thread
this->CMakeThread = new QCMakeThread(this);
QObject::connect(this->CMakeThread, SIGNAL(cmakeInitialized()), this,
@@ -249,6 +252,10 @@ void CMakeSetupDialog::initialize()
SIGNAL(outputMessage(QString)), this,
SLOT(message(QString)));
+ QObject::connect(this->CMakeThread->cmakeInstance(),
+ SIGNAL(openPossible(bool)), this->OpenProjectButton,
+ SLOT(setEnabled(bool)));
+
QObject::connect(this->groupedCheck, SIGNAL(toggled(bool)), this,
SLOT(setGroupedView(bool)));
QObject::connect(this->advancedCheck, SIGNAL(toggled(bool)), this,
@@ -492,24 +499,10 @@ void CMakeSetupDialog::doGenerate()
this->ConfigureNeeded = true;
}
-QString CMakeSetupDialog::getProjectFilename()
-{
- QStringList nameFilter;
- nameFilter << "*.sln"
- << "*.xcodeproj";
- QDir directory(this->BinaryDirectory->currentText());
- QStringList nlnFile = directory.entryList(nameFilter);
-
- if (nlnFile.count() == 1) {
- return this->BinaryDirectory->currentText() + "/" + nlnFile.at(0);
- }
-
- return QString();
-}
-
void CMakeSetupDialog::doOpenProject()
{
- QDesktopServices::openUrl(QUrl::fromLocalFile(this->getProjectFilename()));
+ QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), "open",
+ Qt::QueuedConnection);
}
void CMakeSetupDialog::closeEvent(QCloseEvent* e)
@@ -630,11 +623,6 @@ void CMakeSetupDialog::updateBinaryDirectory(const QString& dir)
this->BinaryDirectory->setEditText(dir);
this->BinaryDirectory->blockSignals(false);
}
- if (!this->getProjectFilename().isEmpty()) {
- this->OpenProjectButton->setEnabled(true);
- } else {
- this->OpenProjectButton->setEnabled(false);
- }
}
void CMakeSetupDialog::doBinaryBrowse()
@@ -1039,9 +1027,6 @@ void CMakeSetupDialog::enterState(CMakeSetupDialog::State s)
this->GenerateButton->setEnabled(true);
this->GenerateAction->setEnabled(true);
this->ConfigureButton->setEnabled(true);
- if (!this->getProjectFilename().isEmpty()) {
- this->OpenProjectButton->setEnabled(true);
- }
this->ConfigureButton->setText(tr("&Configure"));
this->GenerateButton->setText(tr("&Generate"));
} else if (s == ReadyGenerate) {
@@ -1049,9 +1034,6 @@ void CMakeSetupDialog::enterState(CMakeSetupDialog::State s)
this->GenerateButton->setEnabled(true);
this->GenerateAction->setEnabled(true);
this->ConfigureButton->setEnabled(true);
- if (!this->getProjectFilename().isEmpty()) {
- this->OpenProjectButton->setEnabled(true);
- }
this->ConfigureButton->setText(tr("&Configure"));
this->GenerateButton->setText(tr("&Generate"));
}
diff --git a/Source/QtDialog/CMakeSetupDialog.h b/Source/QtDialog/CMakeSetupDialog.h
index 0da28d8..7b767e5 100644
--- a/Source/QtDialog/CMakeSetupDialog.h
+++ b/Source/QtDialog/CMakeSetupDialog.h
@@ -31,7 +31,6 @@ protected slots:
void initialize();
void doConfigure();
void doGenerate();
- QString getProjectFilename();
void doOpenProject();
void doInstallForCommandLine();
void doHelp();
diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx
index d473d9b..7e94a27 100644
--- a/Source/QtDialog/QCMake.cxx
+++ b/Source/QtDialog/QCMake.cxx
@@ -115,6 +115,8 @@ void QCMake::setBinaryDirectory(const QString& _dir)
if (toolset) {
this->setToolset(QString::fromLocal8Bit(toolset));
}
+
+ checkOpenPossible();
}
}
@@ -183,6 +185,26 @@ void QCMake::generate()
#endif
emit this->generateDone(err);
+ checkOpenPossible();
+}
+
+void QCMake::open()
+{
+#ifdef Q_OS_WIN
+ UINT lastErrorMode = SetErrorMode(0);
+#endif
+
+ InterruptFlag = 0;
+ cmSystemTools::ResetErrorOccuredFlag();
+
+ auto successful = this->CMakeInstance->Open(
+ this->BinaryDirectory.toLocal8Bit().data(), false);
+
+#ifdef Q_OS_WIN
+ SetErrorMode(lastErrorMode);
+#endif
+
+ emit this->openDone(successful);
}
void QCMake::setProperties(const QCMakePropertyList& newProps)
@@ -450,3 +472,10 @@ void QCMake::setWarnUnusedMode(bool value)
{
this->WarnUnusedMode = value;
}
+
+void QCMake::checkOpenPossible()
+{
+ auto data = this->BinaryDirectory.toLocal8Bit().data();
+ auto possible = this->CMakeInstance->Open(data, true);
+ emit openPossible(possible);
+}
diff --git a/Source/QtDialog/QCMake.h b/Source/QtDialog/QCMake.h
index 3b8cea7..6fae7e3 100644
--- a/Source/QtDialog/QCMake.h
+++ b/Source/QtDialog/QCMake.h
@@ -80,6 +80,8 @@ public slots:
void configure();
/// generate the files
void generate();
+ /// open the project
+ void open();
/// set the property values
void setProperties(const QCMakePropertyList&);
/// interrupt the configure or generate process (if connecting, make a direct
@@ -111,6 +113,8 @@ public slots:
void setWarnUninitializedMode(bool value);
/// set whether to run cmake with warnings about unused variables
void setWarnUnusedMode(bool value);
+ /// check if project IDE open is possible and emit openPossible signal
+ void checkOpenPossible();
public:
/// get the list of cache properties
@@ -151,6 +155,10 @@ signals:
void debugOutputChanged(bool);
/// signal when the toolset changes
void toolsetChanged(const QString& toolset);
+ /// signal when open is done
+ void openDone(bool successful);
+ /// signal when open is done
+ void openPossible(bool possible);
protected:
cmake* CMakeInstance;
diff --git a/Source/cmExternalMakefileProjectGenerator.cxx b/Source/cmExternalMakefileProjectGenerator.cxx
index 825ec65..fecd821 100644
--- a/Source/cmExternalMakefileProjectGenerator.cxx
+++ b/Source/cmExternalMakefileProjectGenerator.cxx
@@ -24,6 +24,13 @@ std::string cmExternalMakefileProjectGenerator::CreateFullGeneratorName(
return fullName;
}
+bool cmExternalMakefileProjectGenerator::Open(
+ const std::string& /*bindir*/, const std::string& /*projectName*/,
+ bool /*dryRun*/)
+{
+ return false;
+}
+
cmExternalMakefileProjectGeneratorFactory::
cmExternalMakefileProjectGeneratorFactory(const std::string& n,
const std::string& doc)
diff --git a/Source/cmExternalMakefileProjectGenerator.h b/Source/cmExternalMakefileProjectGenerator.h
index a1734ee..7f332a8 100644
--- a/Source/cmExternalMakefileProjectGenerator.h
+++ b/Source/cmExternalMakefileProjectGenerator.h
@@ -55,6 +55,9 @@ public:
void SetName(const std::string& n) { Name = n; }
std::string GetName() const { return Name; }
+ virtual bool Open(const std::string& bindir, const std::string& projectName,
+ bool dryRun);
+
protected:
///! Contains the names of the global generators support by this generator.
std::vector<std::string> SupportedGlobalGenerators;
diff --git a/Source/cmExtraSublimeTextGenerator.cxx b/Source/cmExtraSublimeTextGenerator.cxx
index 73a9c85..3d72ae3 100644
--- a/Source/cmExtraSublimeTextGenerator.cxx
+++ b/Source/cmExtraSublimeTextGenerator.cxx
@@ -400,3 +400,26 @@ std::string cmExtraSublimeTextGenerator::ComputeDefines(
return definesString;
}
+
+bool cmExtraSublimeTextGenerator::Open(const std::string& bindir,
+ const std::string& projectName,
+ bool dryRun)
+{
+ const char* sublExecutable =
+ this->GlobalGenerator->GetCMakeInstance()->GetCacheDefinition(
+ "CMAKE_SUBLIMETEXT_EXECUTABLE");
+ if (!sublExecutable) {
+ return false;
+ }
+ if (cmSystemTools::IsNOTFOUND(sublExecutable)) {
+ return false;
+ }
+
+ std::string filename = bindir + "/" + projectName + ".sublime-project";
+ if (dryRun) {
+ return cmSystemTools::FileExists(filename, true);
+ }
+
+ return cmSystemTools::RunSingleCommand(
+ { sublExecutable, "--project", filename });
+}
diff --git a/Source/cmExtraSublimeTextGenerator.h b/Source/cmExtraSublimeTextGenerator.h
index 7fb304e..57ba1cf 100644
--- a/Source/cmExtraSublimeTextGenerator.h
+++ b/Source/cmExtraSublimeTextGenerator.h
@@ -65,6 +65,9 @@ private:
std::string ComputeDefines(cmSourceFile* source, cmLocalGenerator* lg,
cmGeneratorTarget* gtgt);
+ bool Open(const std::string& bindir, const std::string& projectName,
+ bool dryRun) override;
+
bool ExcludeBuildFolder;
std::string EnvSettings;
};
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index 05efff3..38669c9 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -1824,6 +1824,16 @@ int cmGlobalGenerator::Build(const std::string& /*unused*/,
return retVal;
}
+bool cmGlobalGenerator::Open(const std::string& bindir,
+ const std::string& projectName, bool dryRun)
+{
+ if (this->ExtraGenerator) {
+ return this->ExtraGenerator->Open(bindir, projectName, dryRun);
+ }
+
+ return false;
+}
+
std::string cmGlobalGenerator::GenerateCMakeBuildCommand(
const std::string& target, const std::string& config,
const std::string& native, bool ignoreErrors)
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index 18ca682..04e9dc1 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -162,6 +162,12 @@ public:
std::vector<std::string> const& nativeOptions =
std::vector<std::string>());
+ /**
+ * Open a generated IDE project given the following information.
+ */
+ virtual bool Open(const std::string& bindir, const std::string& projectName,
+ bool dryRun);
+
virtual void GenerateBuildCommand(
std::vector<std::string>& makeCommand, const std::string& makeProgram,
const std::string& projectName, const std::string& projectDir,
diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx
index 0651536..b752f84 100644
--- a/Source/cmGlobalVisualStudioGenerator.cxx
+++ b/Source/cmGlobalVisualStudioGenerator.cxx
@@ -4,7 +4,10 @@
#include "cmGlobalVisualStudioGenerator.h"
#include "cmsys/Encoding.hxx"
+#include <future>
#include <iostream>
+#include <objbase.h>
+#include <shellapi.h>
#include <windows.h>
#include "cmAlgorithms.h"
@@ -928,3 +931,33 @@ void cmGlobalVisualStudioGenerator::AddSymbolExportCommand(
commandLines, "Auto build dll exports", ".");
commands.push_back(command);
}
+
+static bool OpenSolution(std::string sln)
+{
+ HRESULT comInitialized =
+ CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
+ if (FAILED(comInitialized)) {
+ return false;
+ }
+
+ HINSTANCE hi =
+ ShellExecuteA(NULL, "open", sln.c_str(), NULL, NULL, SW_SHOWNORMAL);
+
+ CoUninitialize();
+
+ return reinterpret_cast<intptr_t>(hi) > 32;
+}
+
+bool cmGlobalVisualStudioGenerator::Open(const std::string& bindir,
+ const std::string& projectName,
+ bool dryRun)
+{
+ std::string buildDir = cmSystemTools::ConvertToOutputPath(bindir.c_str());
+ std::string sln = buildDir + "\\" + projectName + ".sln";
+
+ if (dryRun) {
+ return cmSystemTools::FileExists(sln, true);
+ }
+
+ return std::async(std::launch::async, OpenSolution, sln).get();
+}
diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h
index 62bfd3b..55a6813 100644
--- a/Source/cmGlobalVisualStudioGenerator.h
+++ b/Source/cmGlobalVisualStudioGenerator.h
@@ -131,6 +131,9 @@ public:
std::vector<cmCustomCommand>& commands,
std::string const& configName);
+ bool Open(const std::string& bindir, const std::string& projectName,
+ bool dryRun) override;
+
protected:
virtual void AddExtraIDETargets();
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index c79ee47..e5471f2 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -35,6 +35,11 @@
struct cmLinkImplementation;
+#if defined(CMAKE_BUILD_WITH_CMAKE) && defined(__APPLE__)
+#define HAVE_APPLICATION_SERVICES
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
#if defined(CMAKE_BUILD_WITH_CMAKE)
#include "cmXMLParser.h"
@@ -287,6 +292,35 @@ void cmGlobalXCodeGenerator::EnableLanguage(
this->ComputeArchitectures(mf);
}
+bool cmGlobalXCodeGenerator::Open(const std::string& bindir,
+ const std::string& projectName, bool dryRun)
+{
+ bool ret = false;
+
+#ifdef HAVE_APPLICATION_SERVICES
+ std::string url = bindir + "/" + projectName + ".xcodeproj";
+
+ if (dryRun) {
+ return cmSystemTools::FileExists(url, false);
+ }
+
+ CFStringRef cfStr = CFStringCreateWithCString(
+ kCFAllocatorDefault, url.c_str(), kCFStringEncodingUTF8);
+ if (cfStr) {
+ CFURLRef cfUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, cfStr,
+ kCFURLPOSIXPathStyle, true);
+ if (cfUrl) {
+ OSStatus err = LSOpenCFURLRef(cfUrl, nullptr);
+ ret = err == noErr;
+ CFRelease(cfUrl);
+ }
+ CFRelease(cfStr);
+ }
+#endif
+
+ return ret;
+}
+
void cmGlobalXCodeGenerator::GenerateBuildCommand(
std::vector<std::string>& makeCommand, const std::string& makeProgram,
const std::string& projectName, const std::string& /*projectDir*/,
diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h
index e9ca91c..b758e97 100644
--- a/Source/cmGlobalXCodeGenerator.h
+++ b/Source/cmGlobalXCodeGenerator.h
@@ -55,6 +55,13 @@ public:
*/
void EnableLanguage(std::vector<std::string> const& languages, cmMakefile*,
bool optional) override;
+
+ /**
+ * Open a generated IDE project given the following information.
+ */
+ bool Open(const std::string& bindir, const std::string& projectName,
+ bool dryRun) override;
+
/**
* Try running cmake and building a file. This is used for dynalically
* loaded commands, not as part of the usual build process.
diff --git a/Source/cmLinkedTree.h b/Source/cmLinkedTree.h
index 8865e23..975f052 100644
--- a/Source/cmLinkedTree.h
+++ b/Source/cmLinkedTree.h
@@ -137,7 +137,7 @@ public:
iterator Push(iterator it) { return Push_impl(it, T()); }
- iterator Push(iterator it, T t) { return Push_impl(it, t); }
+ iterator Push(iterator it, T t) { return Push_impl(it, std::move(t)); }
bool IsLast(iterator it) { return it.Position == this->Data.size(); }
@@ -177,12 +177,12 @@ private:
T* GetPointer(PositionType pos) { return &this->Data[pos]; }
- iterator Push_impl(iterator it, T t)
+ iterator Push_impl(iterator it, T&& t)
{
assert(this->UpPositions.size() == this->Data.size());
assert(it.Position <= this->UpPositions.size());
this->UpPositions.push_back(it.Position);
- this->Data.push_back(t);
+ this->Data.push_back(std::move(t));
return iterator(this, this->UpPositions.size());
}
diff --git a/Source/cmSetCommand.cxx b/Source/cmSetCommand.cxx
index b32cda3..985aac8 100644
--- a/Source/cmSetCommand.cxx
+++ b/Source/cmSetCommand.cxx
@@ -2,8 +2,6 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmSetCommand.h"
-#include <string.h>
-
#include "cmAlgorithms.h"
#include "cmMakefile.h"
#include "cmState.h"
@@ -22,19 +20,15 @@ bool cmSetCommand::InitialPass(std::vector<std::string> const& args,
}
// watch for ENV signatures
- const char* variable = args[0].c_str(); // VAR is always first
- if (cmHasLiteralPrefix(variable, "ENV{") && strlen(variable) > 5) {
+ auto const& variable = args[0]; // VAR is always first
+ if (cmHasLiteralPrefix(variable, "ENV{") && variable.size() > 5) {
// what is the variable name
- char* varName = new char[strlen(variable)];
- strncpy(varName, variable + 4, strlen(variable) - 5);
- varName[strlen(variable) - 5] = '\0';
- std::string putEnvArg = varName;
- putEnvArg += "=";
+ auto const& varName = variable.substr(4, variable.size() - 5);
+ std::string putEnvArg = varName + "=";
// what is the current value if any
std::string currValue;
const bool currValueSet = cmSystemTools::GetEnv(varName, currValue);
- delete[] varName;
// will it be set to something, then set it
if (args.size() > 1 && !args[1].empty()) {
diff --git a/Source/cmTryRunCommand.cxx b/Source/cmTryRunCommand.cxx
index dcaa493..932b976 100644
--- a/Source/cmTryRunCommand.cxx
+++ b/Source/cmTryRunCommand.cxx
@@ -4,7 +4,6 @@
#include "cmsys/FStream.hxx"
#include <stdio.h>
-#include <string.h>
#include "cmMakefile.h"
#include "cmState.h"
@@ -191,13 +190,15 @@ void cmTryRunCommand::RunExecutable(const std::string& runArgs,
finalCommand.c_str(), out, out, &retVal, nullptr,
cmSystemTools::OUTPUT_NONE, timeout);
// set the run var
- char retChar[1000];
+ char retChar[16];
+ const char* retStr;
if (worked) {
sprintf(retChar, "%i", retVal);
+ retStr = retChar;
} else {
- strcpy(retChar, "FAILED_TO_RUN");
+ retStr = "FAILED_TO_RUN";
}
- this->Makefile->AddCacheDefinition(this->RunResultVariable, retChar,
+ this->Makefile->AddCacheDefinition(this->RunResultVariable, retStr,
"Result of TRY_RUN",
cmStateEnums::INTERNAL);
}
diff --git a/Source/cmUnsetCommand.cxx b/Source/cmUnsetCommand.cxx
index 18bbdd7..cfaa1fd2 100644
--- a/Source/cmUnsetCommand.cxx
+++ b/Source/cmUnsetCommand.cxx
@@ -2,8 +2,6 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmUnsetCommand.h"
-#include <string.h>
-
#include "cmAlgorithms.h"
#include "cmMakefile.h"
#include "cmSystemTools.h"
@@ -19,19 +17,16 @@ bool cmUnsetCommand::InitialPass(std::vector<std::string> const& args,
return false;
}
- const char* variable = args[0].c_str();
+ auto const& variable = args[0];
// unset(ENV{VAR})
- if (cmHasLiteralPrefix(variable, "ENV{") && strlen(variable) > 5) {
+ if (cmHasLiteralPrefix(variable, "ENV{") && variable.size() > 5) {
// what is the variable name
- char* envVarName = new char[strlen(variable)];
- strncpy(envVarName, variable + 4, strlen(variable) - 5);
- envVarName[strlen(variable) - 5] = '\0';
+ auto const& envVarName = variable.substr(4, variable.size() - 5);
#ifdef CMAKE_BUILD_WITH_CMAKE
- cmSystemTools::UnsetEnv(envVarName);
+ cmSystemTools::UnsetEnv(envVarName.c_str());
#endif
- delete[] envVarName;
return true;
}
// unset(VAR)
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index fd7151f..d7ed772 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -2425,6 +2425,49 @@ int cmake::Build(const std::string& dir, const std::string& target,
nativeOptions);
}
+bool cmake::Open(const std::string& dir, bool dryRun)
+{
+ this->SetHomeDirectory("");
+ this->SetHomeOutputDirectory("");
+ if (!cmSystemTools::FileIsDirectory(dir)) {
+ std::cerr << "Error: " << dir << " is not a directory\n";
+ return false;
+ }
+
+ std::string cachePath = FindCacheFile(dir);
+ if (!this->LoadCache(cachePath)) {
+ std::cerr << "Error: could not load cache\n";
+ return false;
+ }
+ const char* genName = this->State->GetCacheEntryValue("CMAKE_GENERATOR");
+ if (!genName) {
+ std::cerr << "Error: could not find CMAKE_GENERATOR in Cache\n";
+ return false;
+ }
+ const char* extraGenName =
+ this->State->GetInitializedCacheValue("CMAKE_EXTRA_GENERATOR");
+ std::string fullName =
+ cmExternalMakefileProjectGenerator::CreateFullGeneratorName(
+ genName, extraGenName ? extraGenName : "");
+
+ std::unique_ptr<cmGlobalGenerator> gen(
+ this->CreateGlobalGenerator(fullName));
+ if (!gen.get()) {
+ std::cerr << "Error: could create CMAKE_GENERATOR \"" << fullName
+ << "\"\n";
+ return false;
+ }
+
+ const char* cachedProjectName =
+ this->State->GetCacheEntryValue("CMAKE_PROJECT_NAME");
+ if (!cachedProjectName) {
+ std::cerr << "Error: could not find CMAKE_PROJECT_NAME in Cache\n";
+ return false;
+ }
+
+ return gen->Open(dir, cachedProjectName, dryRun);
+}
+
void cmake::WatchUnusedCli(const std::string& var)
{
#ifdef CMAKE_BUILD_WITH_CMAKE
diff --git a/Source/cmake.h b/Source/cmake.h
index b31b6f5..ed3ebe0 100644
--- a/Source/cmake.h
+++ b/Source/cmake.h
@@ -401,6 +401,9 @@ public:
const std::string& config,
const std::vector<std::string>& nativeOptions, bool clean);
+ ///! run the --open option
+ bool Open(const std::string& dir, bool dryRun);
+
void UnwatchUnusedCli(const std::string& var);
void WatchUnusedCli(const std::string& var);
diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx
index a1dfc3e..59b908a 100644
--- a/Source/cmakemain.cxx
+++ b/Source/cmakemain.cxx
@@ -66,6 +66,7 @@ static const char* cmDocumentationOptions[][2] = {
{ "-E", "CMake command mode." },
{ "-L[A][H]", "List non-advanced cached variables." },
{ "--build <dir>", "Build a CMake-generated project binary tree." },
+ { "--open <dir>", "Open generated project in the associated application." },
{ "-N", "View mode only." },
{ "-P <file>", "Process script mode." },
{ "--find-package", "Run in pkg-config like mode." },
@@ -100,6 +101,7 @@ static int do_command(int ac, char const* const* av)
int do_cmake(int ac, char const* const* av);
static int do_build(int ac, char const* const* av);
+static int do_open(int ac, char const* const* av);
static cmMakefile* cmakemainGetMakefile(void* clientdata)
{
@@ -186,6 +188,9 @@ int main(int ac, char const* const* av)
if (strcmp(av[1], "--build") == 0) {
return do_build(ac, av);
}
+ if (strcmp(av[1], "--open") == 0) {
+ return do_open(ac, av);
+ }
if (strcmp(av[1], "-E") == 0) {
return do_command(ac, av);
}
@@ -423,3 +428,41 @@ static int do_build(int ac, char const* const* av)
return cm.Build(dir, target, config, nativeOptions, clean);
#endif
}
+
+static int do_open(int ac, char const* const* av)
+{
+#ifndef CMAKE_BUILD_WITH_CMAKE
+ std::cerr << "This cmake does not support --open\n";
+ return -1;
+#else
+ std::string dir;
+
+ enum Doing
+ {
+ DoingNone,
+ DoingDir,
+ };
+ Doing doing = DoingDir;
+ for (int i = 2; i < ac; ++i) {
+ switch (doing) {
+ case DoingDir:
+ dir = cmSystemTools::CollapseFullPath(av[i]);
+ doing = DoingNone;
+ break;
+ default:
+ std::cerr << "Unknown argument " << av[i] << std::endl;
+ dir.clear();
+ break;
+ }
+ }
+ if (dir.empty()) {
+ std::cerr << "Usage: cmake --open <dir>\n";
+ return 1;
+ }
+
+ cmake cm(cmake::RoleInternal);
+ cmSystemTools::SetMessageCallback(cmakemainMessageCallback, &cm);
+ cm.SetProgressCallback(cmakemainProgressCallback, &cm);
+ return cm.Open(dir, false) ? 0 : 1;
+#endif
+}
diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt
index 21568bb..f36f9b6 100644
--- a/Source/kwsys/CMakeLists.txt
+++ b/Source/kwsys/CMakeLists.txt
@@ -445,8 +445,13 @@ KWSYS_PLATFORM_C_TEST(KWSYS_C_HAS_PTRDIFF_T
"Checking whether C compiler has ptrdiff_t in stddef.h" DIRECT)
KWSYS_PLATFORM_C_TEST(KWSYS_C_HAS_SSIZE_T
"Checking whether C compiler has ssize_t in unistd.h" DIRECT)
+IF(KWSYS_USE_Process)
+ KWSYS_PLATFORM_C_TEST(KWSYS_C_HAS_CLOCK_GETTIME_MONOTONIC
+ "Checking whether C compiler has clock_gettime" DIRECT)
+ENDIF()
+
SET_SOURCE_FILES_PROPERTIES(ProcessUNIX.c System.c PROPERTIES
- COMPILE_FLAGS "-DKWSYS_C_HAS_PTRDIFF_T=${KWSYS_C_HAS_PTRDIFF_T} -DKWSYS_C_HAS_SSIZE_T=${KWSYS_C_HAS_SSIZE_T}"
+ COMPILE_FLAGS "-DKWSYS_C_HAS_PTRDIFF_T=${KWSYS_C_HAS_PTRDIFF_T} -DKWSYS_C_HAS_SSIZE_T=${KWSYS_C_HAS_SSIZE_T} -DKWSYS_C_HAS_CLOCK_GETTIME_MONOTONIC=${KWSYS_C_HAS_CLOCK_GETTIME_MONOTONIC}"
)
IF(DEFINED KWSYS_PROCESS_USE_SELECT)
diff --git a/Source/kwsys/CommandLineArguments.cxx b/Source/kwsys/CommandLineArguments.cxx
index 5613bd7..5792da9 100644
--- a/Source/kwsys/CommandLineArguments.cxx
+++ b/Source/kwsys/CommandLineArguments.cxx
@@ -529,10 +529,7 @@ void CommandLineArguments::GenerateHelp()
}
}
- // Create format for that string
- char format[80];
- sprintf(format, " %%-%us ", static_cast<unsigned int>(maxlen));
-
+ CommandLineArguments::Internal::String::size_type maxstrlen = maxlen;
maxlen += 4; // For the space before and after the option
// Print help for each option
@@ -540,27 +537,24 @@ void CommandLineArguments::GenerateHelp()
CommandLineArguments::Internal::SetOfStrings::iterator sit;
for (sit = mpit->second.begin(); sit != mpit->second.end(); sit++) {
str << std::endl;
- char argument[100];
- sprintf(argument, "%s", sit->c_str());
+ std::string argument = *sit;
switch (this->Internals->Callbacks[*sit].ArgumentType) {
case CommandLineArguments::NO_ARGUMENT:
break;
case CommandLineArguments::CONCAT_ARGUMENT:
- strcat(argument, "opt");
+ argument += "opt";
break;
case CommandLineArguments::SPACE_ARGUMENT:
- strcat(argument, " opt");
+ argument += " opt";
break;
case CommandLineArguments::EQUAL_ARGUMENT:
- strcat(argument, "=opt");
+ argument += "=opt";
break;
case CommandLineArguments::MULTI_ARGUMENT:
- strcat(argument, " opt opt ...");
+ argument += " opt opt ...";
break;
}
- char buffer[80];
- sprintf(buffer, format, argument);
- str << buffer;
+ str << " " << argument.substr(0, maxstrlen) << " ";
}
const char* ptr = this->Internals->Callbacks[mpit->first].Help;
size_t len = strlen(ptr);
@@ -649,10 +643,7 @@ void CommandLineArguments::PopulateVariable(double* variable,
void CommandLineArguments::PopulateVariable(char** variable,
const std::string& value)
{
- if (*variable) {
- delete[] * variable;
- *variable = 0;
- }
+ delete[] * variable;
*variable = new char[value.size() + 1];
strcpy(*variable, value.c_str());
}
diff --git a/Source/kwsys/DynamicLoader.cxx b/Source/kwsys/DynamicLoader.cxx
index 1b4596a..664f183 100644
--- a/Source/kwsys/DynamicLoader.cxx
+++ b/Source/kwsys/DynamicLoader.cxx
@@ -163,17 +163,13 @@ DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
{
void* result = 0;
// Need to prepend symbols with '_' on Apple-gcc compilers
- size_t len = sym.size();
- char* rsym = new char[len + 1 + 1];
- strcpy(rsym, "_");
- strcat(rsym + 1, sym.c_str());
+ std::string rsym = '_' + sym;
- NSSymbol symbol = NSLookupSymbolInModule(lib, rsym);
+ NSSymbol symbol = NSLookupSymbolInModule(lib, rsym.c_str());
if (symbol) {
result = NSAddressOfSymbol(symbol);
}
- delete[] rsym;
// Hack to cast pointer-to-data to pointer-to-function.
return *reinterpret_cast<DynamicLoader::SymbolPointer*>(&result);
}
@@ -237,17 +233,12 @@ DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
void* result;
#if defined(__BORLANDC__) || defined(__WATCOMC__)
// Need to prepend symbols with '_'
- size_t len = sym.size();
- char* rsym = new char[len + 1 + 1];
- strcpy(rsym, "_");
- strcat(rsym, sym.c_str());
+ std::string ssym = '_' + sym;
+ const char* rsym = ssym.c_str();
#else
const char* rsym = sym.c_str();
#endif
result = (void*)GetProcAddress(lib, rsym);
-#if defined(__BORLANDC__) || defined(__WATCOMC__)
- delete[] rsym;
-#endif
// Hack to cast pointer-to-data to pointer-to-function.
#ifdef __WATCOMC__
return *(DynamicLoader::SymbolPointer*)(&result);
diff --git a/Source/kwsys/ProcessUNIX.c b/Source/kwsys/ProcessUNIX.c
index 3b32ca7..e62ef34 100644
--- a/Source/kwsys/ProcessUNIX.c
+++ b/Source/kwsys/ProcessUNIX.c
@@ -359,9 +359,7 @@ void kwsysProcess_Delete(kwsysProcess* cp)
kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDIN, 0);
kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDOUT, 0);
kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDERR, 0);
- if (cp->CommandExitCodes) {
- free(cp->CommandExitCodes);
- }
+ free(cp->CommandExitCodes);
free(cp->ProcessResults);
free(cp);
}
@@ -498,11 +496,10 @@ int kwsysProcess_SetWorkingDirectory(kwsysProcess* cp, const char* dir)
cp->WorkingDirectory = 0;
}
if (dir) {
- cp->WorkingDirectory = (char*)malloc(strlen(dir) + 1);
+ cp->WorkingDirectory = strdup(dir);
if (!cp->WorkingDirectory) {
return 0;
}
- strcpy(cp->WorkingDirectory, dir);
}
return 1;
}
@@ -531,11 +528,10 @@ int kwsysProcess_SetPipeFile(kwsysProcess* cp, int prPipe, const char* file)
*pfile = 0;
}
if (file) {
- *pfile = (char*)malloc(strlen(file) + 1);
+ *pfile = strdup(file);
if (!*pfile) {
return 0;
}
- strcpy(*pfile, file);
}
/* If we are redirecting the pipe, do not share it or use a native
@@ -1514,9 +1510,7 @@ static int kwsysProcessInitialize(kwsysProcess* cp)
oldForkPIDs = cp->ForkPIDs;
cp->ForkPIDs = (volatile pid_t*)malloc(sizeof(volatile pid_t) *
(size_t)(cp->NumberOfCommands));
- if (oldForkPIDs) {
- kwsysProcessVolatileFree(oldForkPIDs);
- }
+ kwsysProcessVolatileFree(oldForkPIDs);
if (!cp->ForkPIDs) {
return 0;
}
@@ -1524,9 +1518,7 @@ static int kwsysProcessInitialize(kwsysProcess* cp)
cp->ForkPIDs[i] = 0; /* can't use memset due to volatile */
}
- if (cp->CommandExitCodes) {
- free(cp->CommandExitCodes);
- }
+ free(cp->CommandExitCodes);
cp->CommandExitCodes =
(int*)malloc(sizeof(int) * (size_t)(cp->NumberOfCommands));
if (!cp->CommandExitCodes) {
@@ -1938,6 +1930,7 @@ static int kwsysProcessSetupOutputPipeFile(int* p, const char* name)
/* Set close-on-exec flag on the pipe's end. */
if (fcntl(fout, F_SETFD, FD_CLOEXEC) < 0) {
+ close(fout);
return 0;
}
@@ -2033,7 +2026,15 @@ static kwsysProcessTime kwsysProcessTimeGetCurrent(void)
{
kwsysProcessTime current;
kwsysProcessTimeNative current_native;
+#if KWSYS_C_HAS_CLOCK_GETTIME_MONOTONIC
+ struct timespec current_timespec;
+ clock_gettime(CLOCK_MONOTONIC, &current_timespec);
+
+ current_native.tv_sec = current_timespec.tv_sec;
+ current_native.tv_usec = current_timespec.tv_nsec / 1000;
+#else
gettimeofday(&current_native, 0);
+#endif
current.tv_sec = (long)current_native.tv_sec;
current.tv_usec = (long)current_native.tv_usec;
return current;
@@ -2290,6 +2291,7 @@ static void kwsysProcessChildErrorExit(int errorPipe)
char buffer[KWSYSPE_PIPE_BUFFER_SIZE];
kwsysProcess_ssize_t result;
strncpy(buffer, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE);
+ buffer[KWSYSPE_PIPE_BUFFER_SIZE - 1] = '\0';
/* Report the error to the parent through the special pipe. */
result = write(errorPipe, buffer, strlen(buffer));
diff --git a/Source/kwsys/ProcessWin32.c b/Source/kwsys/ProcessWin32.c
index 5183e3d..945fa28 100644
--- a/Source/kwsys/ProcessWin32.c
+++ b/Source/kwsys/ProcessWin32.c
@@ -523,9 +523,7 @@ void kwsysProcess_Delete(kwsysProcess* cp)
kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDIN, 0);
kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDOUT, 0);
kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDERR, 0);
- if (cp->CommandExitCodes) {
- free(cp->CommandExitCodes);
- }
+ free(cp->CommandExitCodes);
free(cp->ProcessResults);
free(cp);
}
@@ -713,11 +711,10 @@ int kwsysProcess_SetPipeFile(kwsysProcess* cp, int pipe, const char* file)
*pfile = 0;
}
if (file) {
- *pfile = (char*)malloc(strlen(file) + 1);
+ *pfile = strdup(file);
if (!*pfile) {
return 0;
}
- strcpy(*pfile, file);
}
/* If we are redirecting the pipe, do not share it or use a native
@@ -1607,9 +1604,7 @@ int kwsysProcessInitialize(kwsysProcess* cp)
}
ZeroMemory(cp->ProcessInformation,
sizeof(PROCESS_INFORMATION) * cp->NumberOfCommands);
- if (cp->CommandExitCodes) {
- free(cp->CommandExitCodes);
- }
+ free(cp->CommandExitCodes);
cp->CommandExitCodes = (DWORD*)malloc(sizeof(DWORD) * cp->NumberOfCommands);
if (!cp->CommandExitCodes) {
return 0;
@@ -2362,9 +2357,7 @@ static int kwsysProcess_List__New_NT4(kwsysProcess_List* self)
static void kwsysProcess_List__Delete_NT4(kwsysProcess_List* self)
{
/* Free the process information buffer. */
- if (self->Buffer) {
- free(self->Buffer);
- }
+ free(self->Buffer);
}
static int kwsysProcess_List__Update_NT4(kwsysProcess_List* self)
diff --git a/Source/kwsys/SystemInformation.cxx b/Source/kwsys/SystemInformation.cxx
index 86fdccd..366fe30 100644
--- a/Source/kwsys/SystemInformation.cxx
+++ b/Source/kwsys/SystemInformation.cxx
@@ -1346,7 +1346,7 @@ std::string SymbolProperties::GetBinary() const
std::string binary;
char buf[1024] = { '\0' };
ssize_t ll = 0;
- if ((ll = readlink("/proc/self/exe", buf, 1024)) > 0) {
+ if ((ll = readlink("/proc/self/exe", buf, 1024)) > 0 && ll < 1024) {
buf[ll] = '\0';
binary = buf;
} else {
diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx
index ecfa331..a24a326 100644
--- a/Source/kwsys/SystemTools.cxx
+++ b/Source/kwsys/SystemTools.cxx
@@ -20,6 +20,7 @@
#include KWSYS_HEADER(SystemTools.hxx)
#include KWSYS_HEADER(Directory.hxx)
#include KWSYS_HEADER(FStream.hxx)
+#include KWSYS_HEADER(Encoding.h)
#include KWSYS_HEADER(Encoding.hxx)
#include <fstream>
@@ -227,13 +228,17 @@ inline const char* Getcwd(char* buf, unsigned int len)
{
std::vector<wchar_t> w_buf(len);
if (_wgetcwd(&w_buf[0], len)) {
- // make sure the drive letter is capital
- if (wcslen(&w_buf[0]) > 1 && w_buf[1] == L':') {
- w_buf[0] = towupper(w_buf[0]);
+ size_t nlen = kwsysEncoding_wcstombs(buf, &w_buf[0], len);
+ if (nlen == static_cast<size_t>(-1)) {
+ return 0;
+ }
+ if (nlen < len) {
+ // make sure the drive letter is capital
+ if (nlen > 1 && buf[1] == ':') {
+ buf[0] = toupper(buf[0]);
+ }
+ return buf;
}
- std::string tmp = KWSYS_NAMESPACE::Encoding::ToNarrow(&w_buf[0]);
- strcpy(buf, tmp.c_str());
- return buf;
}
return 0;
}
diff --git a/Source/kwsys/kwsysPlatformTestsC.c b/Source/kwsys/kwsysPlatformTestsC.c
index 64a361b..5432633 100644
--- a/Source/kwsys/kwsysPlatformTestsC.c
+++ b/Source/kwsys/kwsysPlatformTestsC.c
@@ -55,6 +55,21 @@ int KWSYS_PLATFORM_TEST_C_MAIN()
}
#endif
+#ifdef TEST_KWSYS_C_HAS_CLOCK_GETTIME_MONOTONIC
+#if defined(__APPLE__)
+#include <AvailabilityMacros.h>
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101200
+#error "clock_gettime not available on macOS < 10.12"
+#endif
+#endif
+#include <time.h>
+int KWSYS_PLATFORM_TEST_C_MAIN()
+{
+ struct timespec ts;
+ return clock_gettime(CLOCK_MONOTONIC, &ts);
+}
+#endif
+
#ifdef TEST_KWSYS_C_TYPE_MACROS
char* info_macros =
#if defined(__SIZEOF_SHORT__)
diff --git a/Source/kwsys/testEncoding.cxx b/Source/kwsys/testEncoding.cxx
index 2c5ef46..2742fe4 100644
--- a/Source/kwsys/testEncoding.cxx
+++ b/Source/kwsys/testEncoding.cxx
@@ -75,6 +75,10 @@ static int testRobustEncoding()
// test that the conversion functions handle invalid
// unicode correctly/gracefully
+ // we manipulate the format flags of stdout, remember
+ // the original state here to restore before return
+ std::ios::fmtflags const& flags = std::cout.flags();
+
int ret = 0;
char cstr[] = { (char)-1, 0 };
// this conversion could fail
@@ -120,6 +124,7 @@ static int testRobustEncoding()
ret++;
}
+ std::cout.flags(flags);
return ret;
}
diff --git a/Source/kwsys/testProcess.c b/Source/kwsys/testProcess.c
index 092dd03..cd817d9 100644
--- a/Source/kwsys/testProcess.c
+++ b/Source/kwsys/testProcess.c
@@ -687,9 +687,7 @@ int main(int argc, const char* argv[])
fflush(stdout);
fflush(stderr);
#if defined(_WIN32)
- if (argv0) {
- free(argv0);
- }
+ free(argv0);
#endif
return r;
} else if (argc > 2 && strcmp(argv[1], "0") == 0) {
diff --git a/Source/kwsys/testSystemTools.cxx b/Source/kwsys/testSystemTools.cxx
index 768eb4d..3b694c9 100644
--- a/Source/kwsys/testSystemTools.cxx
+++ b/Source/kwsys/testSystemTools.cxx
@@ -254,22 +254,22 @@ static bool CheckFileOperations()
}
// should work, was created as new file before
if (!kwsys::SystemTools::FileExists(testNewFile)) {
- std::cerr << "Problem with FileExists for: " << testNewDir << std::endl;
+ std::cerr << "Problem with FileExists for: " << testNewFile << std::endl;
res = false;
}
if (!kwsys::SystemTools::FileExists(testNewFile.c_str())) {
- std::cerr << "Problem with FileExists as C string for: " << testNewDir
+ std::cerr << "Problem with FileExists as C string for: " << testNewFile
<< std::endl;
res = false;
}
if (!kwsys::SystemTools::FileExists(testNewFile, true)) {
- std::cerr << "Problem with FileExists as file for: " << testNewDir
+ std::cerr << "Problem with FileExists as file for: " << testNewFile
<< std::endl;
res = false;
}
if (!kwsys::SystemTools::FileExists(testNewFile.c_str(), true)) {
std::cerr << "Problem with FileExists as C string and file for: "
- << testNewDir << std::endl;
+ << testNewFile << std::endl;
res = false;
}
@@ -285,7 +285,7 @@ static bool CheckFileOperations()
}
// should work, was created as new file before
if (!kwsys::SystemTools::PathExists(testNewFile)) {
- std::cerr << "Problem with PathExists for: " << testNewDir << std::endl;
+ std::cerr << "Problem with PathExists for: " << testNewFile << std::endl;
res = false;
}
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index ce53454..c25fc44 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -3261,6 +3261,18 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
--build-options ${build_options}
--test-command ${JAVA_RUNTIME} -classpath hello2.jar HelloWorld)
list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJarSourceList")
+ add_test(Java.JarSourceListAndOutput ${CMAKE_CTEST_COMMAND}
+ --build-and-test
+ "${CMake_SOURCE_DIR}/Tests/Java"
+ "${CMake_BINARY_DIR}/Tests/JavaJarSourceListAndOutput"
+ ${build_generator_args}
+ --build-project hello
+ --build-target hello3
+ --build-two-config
+ --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaJarSourceListAndOutput/hello3"
+ --build-options ${build_options}
+ --test-command ${JAVA_RUNTIME} -classpath hello3.jar HelloWorld)
+ list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJarSourceListAndOutput")
# For next test, java tool must have same architecture as toolchain
math(EXPR _object_mode "${CMAKE_SIZEOF_VOID_P} * 8")
diff --git a/Tests/Java/CMakeLists.txt b/Tests/Java/CMakeLists.txt
index e1bcf3c..0b8269b 100644
--- a/Tests/Java/CMakeLists.txt
+++ b/Tests/Java/CMakeLists.txt
@@ -11,3 +11,6 @@ add_jar(hello A.java HelloWorld.java)
# use listing file to specify sources
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/java_fileslist "A.java\nHelloWorld.java\n")
add_jar(hello2 @${CMAKE_CURRENT_BINARY_DIR}/java_fileslist)
+
+# use listing file to specify sources and specify output directory (issue #17316)
+add_jar(hello3 @${CMAKE_CURRENT_BINARY_DIR}/java_fileslist OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/hello3")
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 57cc144..7eafa06 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -327,6 +327,7 @@ add_RunCMake_test(install)
add_RunCMake_test(CPackConfig)
add_RunCMake_test(CPackInstallProperties)
add_RunCMake_test(ExternalProject)
+add_RunCMake_test(FetchContent)
add_RunCMake_test(CTestCommandLine)
# Only run this test on unix platforms that support
# symbolic links
diff --git a/Tests/RunCMake/FetchContent/CMakeLists.txt b/Tests/RunCMake/FetchContent/CMakeLists.txt
new file mode 100644
index 0000000..d3137f6
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.9)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/FetchContent/DirOverrides.cmake b/Tests/RunCMake/FetchContent/DirOverrides.cmake
new file mode 100644
index 0000000..50eef16
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/DirOverrides.cmake
@@ -0,0 +1,46 @@
+include(FetchContent)
+
+# Test using saved details
+FetchContent_Declare(
+ t1
+ SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/savedSrc
+ DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E make_directory <SOURCE_DIR>
+)
+FetchContent_Populate(t1)
+if(NOT IS_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/savedSrc)
+ message(FATAL_ERROR "Saved details SOURCE_DIR override failed")
+endif()
+
+# Test direct population
+FetchContent_Populate(
+ t2
+ SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/directSrc
+ DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E make_directory <SOURCE_DIR>
+)
+if(NOT IS_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/directSrc)
+ message(FATAL_ERROR "Direct details SOURCE_DIR override failed")
+endif()
+
+# Ensure setting BINARY_DIR to SOURCE_DIR works (a technique to
+# prevent an unwanted separate BINARY_DIR from being created, which
+# ExternalProject_Add() does whether we like it or not)
+FetchContent_Declare(
+ t3
+ SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/savedNoBuildDir
+ BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/savedNoBuildDir
+ DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E make_directory <SOURCE_DIR>
+)
+FetchContent_Populate(t3)
+if(IS_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/savedNobuildDir-build)
+ message(FATAL_ERROR "Saved details BINARY_DIR override failed")
+endif()
+
+FetchContent_Populate(
+ t4
+ SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/directNoBuildDir
+ BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/directNoBuildDir
+ DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E make_directory <SOURCE_DIR>
+)
+if(IS_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/savedNobuildDir-build)
+ message(FATAL_ERROR "Direct details BINARY_DIR override failed")
+endif()
diff --git a/Tests/RunCMake/FetchContent/DirectIgnoresDetails-stdout.txt b/Tests/RunCMake/FetchContent/DirectIgnoresDetails-stdout.txt
new file mode 100644
index 0000000..6fa5a57
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/DirectIgnoresDetails-stdout.txt
@@ -0,0 +1 @@
+Local details used
diff --git a/Tests/RunCMake/FetchContent/DirectIgnoresDetails.cmake b/Tests/RunCMake/FetchContent/DirectIgnoresDetails.cmake
new file mode 100644
index 0000000..0731b43
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/DirectIgnoresDetails.cmake
@@ -0,0 +1,12 @@
+include(FetchContent)
+
+FetchContent_Declare(
+ t1
+ DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "Saved details used"
+)
+
+# No QUIET option given, so command output will be shown
+FetchContent_Populate(
+ t1
+ DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "Local details used"
+)
diff --git a/Tests/RunCMake/FetchContent/DownloadTwice-result.txt b/Tests/RunCMake/FetchContent/DownloadTwice-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/DownloadTwice-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/FetchContent/DownloadTwice-stderr.txt b/Tests/RunCMake/FetchContent/DownloadTwice-stderr.txt
new file mode 100644
index 0000000..96fed48
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/DownloadTwice-stderr.txt
@@ -0,0 +1 @@
+Content t1 already populated in
diff --git a/Tests/RunCMake/FetchContent/DownloadTwice.cmake b/Tests/RunCMake/FetchContent/DownloadTwice.cmake
new file mode 100644
index 0000000..6863c30
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/DownloadTwice.cmake
@@ -0,0 +1,9 @@
+include(FetchContent)
+
+FetchContent_Declare(
+ t1
+ DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "Download command executed"
+)
+
+FetchContent_Populate(t1)
+FetchContent_Populate(t1) # Triggers error
diff --git a/Tests/RunCMake/FetchContent/FirstDetailsWin-stdout.txt b/Tests/RunCMake/FetchContent/FirstDetailsWin-stdout.txt
new file mode 100644
index 0000000..7a8bf18
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/FirstDetailsWin-stdout.txt
@@ -0,0 +1 @@
+First details used
diff --git a/Tests/RunCMake/FetchContent/FirstDetailsWin.cmake b/Tests/RunCMake/FetchContent/FirstDetailsWin.cmake
new file mode 100644
index 0000000..208b12d
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/FirstDetailsWin.cmake
@@ -0,0 +1,16 @@
+include(FetchContent)
+
+# Need to see the download command output
+set(FETCHCONTENT_QUIET OFF)
+
+FetchContent_Declare(
+ t1
+ DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "First details used"
+)
+
+FetchContent_Declare(
+ t1
+ DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "Second details used"
+)
+
+FetchContent_Populate(t1)
diff --git a/Tests/RunCMake/FetchContent/GetProperties.cmake b/Tests/RunCMake/FetchContent/GetProperties.cmake
new file mode 100644
index 0000000..61c99fe
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/GetProperties.cmake
@@ -0,0 +1,67 @@
+include(FetchContent)
+
+# First confirm properties are empty even before declare
+FetchContent_GetProperties(t1)
+if(t1_POPULATED)
+ message(FATAL_ERROR "Property says populated before doing anything")
+endif()
+if(t1_SOURCE_DIR)
+ message(FATAL_ERROR "SOURCE_DIR property not initially empty")
+endif()
+if(t1_BINARY_DIR)
+ message(FATAL_ERROR "BINARY_DIR property not initially empty")
+endif()
+
+# Declare, but no properties should change yet
+FetchContent_Declare(
+ t1
+ SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/savedSrc
+ BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/savedBin
+ DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "Do nothing"
+)
+
+FetchContent_GetProperties(t1)
+if(t1_POPULATED)
+ message(FATAL_ERROR "Property says populated after only declaring details")
+endif()
+if(t1_SOURCE_DIR)
+ message(FATAL_ERROR "SOURCE_DIR property not empty after declare")
+endif()
+if(t1_BINARY_DIR)
+ message(FATAL_ERROR "BINARY_DIR property not empty after declare")
+endif()
+
+# Populate should make all properties non-empty/set
+FetchContent_Populate(t1)
+
+FetchContent_GetProperties(t1)
+if(NOT t1_POPULATED)
+ message(FATAL_ERROR "Population did not set POPULATED property")
+endif()
+if(NOT "${t1_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/savedSrc")
+ message(FATAL_ERROR "SOURCE_DIR property not correct after population: "
+ "${t1_SOURCE_DIR}\n"
+ " Expected: ${CMAKE_CURRENT_BINARY_DIR}/savedSrc")
+endif()
+if(NOT "${t1_BINARY_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/savedBin")
+ message(FATAL_ERROR "BINARY_DIR property not correct after population: "
+ "${t1_BINARY_DIR}\n"
+ " Expected: ${CMAKE_CURRENT_BINARY_DIR}/savedBin")
+endif()
+
+# Verify we can retrieve properties individually too
+FetchContent_GetProperties(t1 POPULATED varPop)
+FetchContent_GetProperties(t1 SOURCE_DIR varSrc)
+FetchContent_GetProperties(t1 BINARY_DIR varBin)
+
+if(NOT varPop)
+ message(FATAL_ERROR "Failed to retrieve POPULATED property")
+endif()
+if(NOT "${varSrc}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/savedSrc")
+ message(FATAL_ERROR "SOURCE_DIR property not retrieved correctly: ${varSrc}\n"
+ " Expected: ${CMAKE_CURRENT_BINARY_DIR}/savedSrc")
+endif()
+if(NOT "${varBin}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/savedBin")
+ message(FATAL_ERROR "BINARY_DIR property not retrieved correctly: ${varBin}\n"
+ " Expected: ${CMAKE_CURRENT_BINARY_DIR}/savedBin")
+endif()
diff --git a/Tests/RunCMake/FetchContent/MissingDetails-result.txt b/Tests/RunCMake/FetchContent/MissingDetails-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/MissingDetails-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/FetchContent/MissingDetails-stderr.txt b/Tests/RunCMake/FetchContent/MissingDetails-stderr.txt
new file mode 100644
index 0000000..c4f1daf
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/MissingDetails-stderr.txt
@@ -0,0 +1 @@
+No content details recorded for t1
diff --git a/Tests/RunCMake/FetchContent/MissingDetails.cmake b/Tests/RunCMake/FetchContent/MissingDetails.cmake
new file mode 100644
index 0000000..ba8d121
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/MissingDetails.cmake
@@ -0,0 +1,3 @@
+include(FetchContent)
+
+FetchContent_Populate(t1)
diff --git a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake
new file mode 100644
index 0000000..621fb8b
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake
@@ -0,0 +1,28 @@
+include(RunCMake)
+
+unset(RunCMake_TEST_NO_CLEAN)
+
+run_cmake(MissingDetails)
+run_cmake(DirectIgnoresDetails)
+run_cmake(FirstDetailsWin)
+run_cmake(DownloadTwice)
+run_cmake(SameGenerator)
+run_cmake(VarDefinitions)
+run_cmake(GetProperties)
+run_cmake(DirOverrides)
+
+# We need to pass through CMAKE_GENERATOR and CMAKE_MAKE_PROGRAM
+# to ensure the test can run on machines where the build tool
+# isn't on the PATH. Some build slaves explicitly test with such
+# an arrangement (e.g. to test with spaces in the path). We also
+# pass through the platform and toolset for completeness, even
+# though we don't build anything, just in case this somehow affects
+# the way the build tool is invoked.
+run_cmake_command(ScriptMode
+ ${CMAKE_COMMAND}
+ -DCMAKE_GENERATOR=${RunCMake_GENERATOR}
+ -DCMAKE_GENERATOR_PLATFORM=${RunCMake_GENERATOR_PLATFORM}
+ -DCMAKE_GENERATOR_TOOLSET=${RunCMake_GENERATOR_TOOLSET}
+ -DCMAKE_MAKE_PROGRAM=${RunCMake_MAKE_PROGRAM}
+ -P ${CMAKE_CURRENT_LIST_DIR}/ScriptMode.cmake
+)
diff --git a/Tests/RunCMake/FetchContent/SameGenerator.cmake b/Tests/RunCMake/FetchContent/SameGenerator.cmake
new file mode 100644
index 0000000..58204ef
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/SameGenerator.cmake
@@ -0,0 +1,17 @@
+include(FetchContent)
+
+FetchContent_Declare(
+ t1
+ DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "Download command executed"
+)
+
+FetchContent_Populate(t1)
+
+file(STRINGS "${FETCHCONTENT_BASE_DIR}/t1-subbuild/CMakeCache.txt"
+ matchLine REGEX "^CMAKE_GENERATOR:.*="
+ LIMIT_COUNT 1
+)
+if(NOT matchLine MATCHES "${CMAKE_GENERATOR}")
+ message(FATAL_ERROR "Generator line mismatch: ${matchLine}\n"
+ " Expected type: ${CMAKE_GENERATOR}")
+endif()
diff --git a/Tests/RunCMake/FetchContent/ScriptMode.cmake b/Tests/RunCMake/FetchContent/ScriptMode.cmake
new file mode 100644
index 0000000..0a93d62
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/ScriptMode.cmake
@@ -0,0 +1,35 @@
+include(FetchContent)
+
+file(WRITE tmpFile.txt "Generated contents, not important")
+
+FetchContent_Populate(
+ t1
+ DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_BINARY_DIR}/tmpFile.txt
+ <SOURCE_DIR>/done1.txt
+)
+if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/t1-src/done1.txt)
+ message(FATAL_ERROR "Default SOURCE_DIR doesn't contain done1.txt")
+endif()
+
+FetchContent_Populate(
+ t2
+ SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/mysrc
+ DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_BINARY_DIR}/tmpFile.txt
+ <SOURCE_DIR>/done2.txt
+)
+if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/mysrc/done2.txt)
+ message(FATAL_ERROR "Specified SOURCE_DIR doesn't contain done2.txt")
+endif()
+
+FetchContent_Populate(
+ t3
+ SOURCE_DIR myrelsrc
+ DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_BINARY_DIR}/tmpFile.txt
+ <SOURCE_DIR>/done3.txt
+)
+if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/myrelsrc/done3.txt)
+ message(FATAL_ERROR "Relative SOURCE_DIR doesn't contain done3.txt")
+endif()
diff --git a/Tests/RunCMake/FetchContent/VarDefinitions.cmake b/Tests/RunCMake/FetchContent/VarDefinitions.cmake
new file mode 100644
index 0000000..4d2a929
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/VarDefinitions.cmake
@@ -0,0 +1,75 @@
+unset(FETCHCONTENT_FULLY_DISCONNECTED CACHE)
+unset(FETCHCONTENT_UPDATES_DISCONNECTED CACHE)
+unset(FETCHCONTENT_QUIET CACHE)
+unset(FETCHCONTENT_BASE_DIR CACHE)
+
+include(FetchContent)
+
+# Each of the cache entries should be defined and have the
+# expected value. Be careful to check unset separately from a
+# false value, since unset also equates to false.
+if(FETCHCONTENT_FULLY_DISCONNECTED STREQUAL "")
+ message(FATAL_ERROR "FETCHCONTENT_FULLY_DISCONNECTED not defined")
+elseif(FETCHCONTENT_FULLY_DISCONNECTED)
+ message(FATAL_ERROR "FETCHCONTENT_FULLY_DISCONNECTED not defaulted to OFF")
+endif()
+
+if(FETCHCONTENT_UPDATES_DISCONNECTED STREQUAL "")
+ message(FATAL_ERROR "FETCHCONTENT_UPDATES_DISCONNECTED not defined")
+elseif(FETCHCONTENT_UPDATES_DISCONNECTED)
+ message(FATAL_ERROR "FETCHCONTENT_UPDATES_DISCONNECTED not defaulted to OFF")
+endif()
+
+if(FETCHCONTENT_QUIET STREQUAL "")
+ message(FATAL_ERROR "FETCHCONTENT_QUIET not defined")
+elseif(NOT FETCHCONTENT_QUIET)
+ message(FATAL_ERROR "FETCHCONTENT_QUIET not defaulted to ON")
+endif()
+
+if(NOT FETCHCONTENT_BASE_DIR STREQUAL "${CMAKE_BINARY_DIR}/_deps")
+ message(FATAL_ERROR "FETCHCONTENT_BASE_DIR has default value: "
+ "${FETCHCONTENT_BASE_DIR}\n Expected: ${CMAKE_BINARY_DIR}/_deps")
+endif()
+
+file(REMOVE_RECURSE ${FETCHCONTENT_BASE_DIR}/t1-subbuild)
+
+# Use uppercase T1 test name to confirm conversion to lowercase
+# for the t1_... variable names that get set
+FetchContent_Declare(
+ T1
+ DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "Download command executed"
+)
+FetchContent_Populate(T1)
+
+# Be careful to check both regular and cache variables. Since they have
+# the same name, we can only confirm them separately by using get_property().
+get_property(srcRegVarSet VARIABLE PROPERTY t1_SOURCE_DIR SET)
+get_property(bldRegVarSet VARIABLE PROPERTY t1_BINARY_DIR SET)
+
+get_property(srcCacheVarSet CACHE t1_SOURCE_DIR PROPERTY VALUE SET)
+get_property(bldCacheVarSet CACHE t1_BINARY_DIR PROPERTY VALUE SET)
+
+if(NOT srcRegVarSet)
+ message(FATAL_ERROR "t1_SOURCE_DIR regular variable not set")
+endif()
+if(NOT bldRegVarSet)
+ message(FATAL_ERROR "t1_BINARY_DIR regular variable not set")
+endif()
+if(srcCacheVarSet)
+ message(FATAL_ERROR "t1_SOURCE_DIR cache variable unexpectedly set")
+endif()
+if(bldCacheVarSet)
+ message(FATAL_ERROR "t1_BINARY_DIR cache variable unexpectedly set")
+endif()
+
+set(srcRegVar ${t1_SOURCE_DIR})
+set(bldRegVar ${t1_BINARY_DIR})
+
+if(NOT srcRegVar STREQUAL "${CMAKE_BINARY_DIR}/_deps/t1-src")
+ message(FATAL_ERROR "Unexpected t1_SOURCE_DIR value: ${srcRegVar}\n"
+ " Expected: ${CMAKE_BINARY_DIR}/_deps/t1-src")
+endif()
+if(NOT bldRegVar STREQUAL "${CMAKE_BINARY_DIR}/_deps/t1-build")
+ message(FATAL_ERROR "Unexpected t1_BINARY_DIR value: ${bldRegVar}\n"
+ " Expected: ${CMAKE_BINARY_DIR}/_deps/t1-build")
+endif()
diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake
index 26312c4..4fd816e 100644
--- a/Tests/RunCMake/RunCMake.cmake
+++ b/Tests/RunCMake/RunCMake.cmake
@@ -99,9 +99,21 @@ function(run_cmake test)
if(NOT "${actual_result}" MATCHES "${expect_result}")
string(APPEND msg "Result is [${actual_result}], not [${expect_result}].\n")
endif()
+ string(CONCAT ignore_line_regex
+ "(^|\n)((==[0-9]+=="
+ "|BullseyeCoverage"
+ "|[a-z]+\\([0-9]+\\) malloc:"
+ "|clang[^:]*: warning: the object size sanitizer has no effect at -O0, but is explicitly enabled:"
+ "|Error kstat returned"
+ "|Hit xcodebuild bug"
+ "|[^\n]*is a member of multiple groups"
+ "|[^\n]*from Time Machine by path"
+ "|[^\n]*Bullseye Testing Technology"
+ ")[^\n]*\n)+"
+ )
foreach(o out err)
string(REGEX REPLACE "\r\n" "\n" actual_std${o} "${actual_std${o}}")
- string(REGEX REPLACE "(^|\n)((==[0-9]+==|BullseyeCoverage|[a-z]+\\([0-9]+\\) malloc:|Error kstat returned|Hit xcodebuild bug|[^\n]*is a member of multiple groups|[^\n]*from Time Machine by path|[^\n]*Bullseye Testing Technology)[^\n]*\n)+" "\\1" actual_std${o} "${actual_std${o}}")
+ string(REGEX REPLACE "${ignore_line_regex}" "\\1" actual_std${o} "${actual_std${o}}")
string(REGEX REPLACE "\n+$" "" actual_std${o} "${actual_std${o}}")
set(expect_${o} "")
if(DEFINED expect_std${o})