diff options
95 files changed, 2065 insertions, 897 deletions
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index eb9af27..c81ba59 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst @@ -57,6 +57,7 @@ Policies Introduced by CMake 3.10 .. toctree:: :maxdepth: 1 + CMP0071: Let AUTOMOC and AUTOUIC process GENERATED files. </policy/CMP0071> CMP0070: Define file(GENERATE) behavior for relative paths. </policy/CMP0070> Policies Introduced by CMake 3.9 diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst index ba925e8..8aece23 100644 --- a/Help/manual/cmake.1.rst +++ b/Help/manual/cmake.1.rst @@ -262,6 +262,36 @@ Available commands are: 351abe79cd3800b38cdfb25d45015a15 file1.txt 052f86c15bbde68af55c7f7b340ab639 file2.txt +``sha1sum <file>...`` + Create SHA1 checksum of files in ``sha1sum`` compatible format:: + + 4bb7932a29e6f73c97bb9272f2bdc393122f86e0 file1.txt + 1df4c8f318665f9a5f2ed38f55adadb7ef9f559c file2.txt + +``sha224sum <file>...`` + Create SHA224 checksum of files in ``sha224sum`` compatible format:: + + b9b9346bc8437bbda630b0b7ddfc5ea9ca157546dbbf4c613192f930 file1.txt + 6dfbe55f4d2edc5fe5c9197bca51ceaaf824e48eba0cc453088aee24 file2.txt + +``sha256sum <file>...`` + Create SHA256 checksum of files in ``sha256sum`` compatible format:: + + 76713b23615d31680afeb0e9efe94d47d3d4229191198bb46d7485f9cb191acc file1.txt + 15b682ead6c12dedb1baf91231e1e89cfc7974b3787c1e2e01b986bffadae0ea file2.txt + +``sha384sum <file>...`` + Create SHA384 checksum of files in ``sha384sum`` compatible format:: + + acc049fedc091a22f5f2ce39a43b9057fd93c910e9afd76a6411a28a8f2b8a12c73d7129e292f94fc0329c309df49434 file1.txt + 668ddeb108710d271ee21c0f3acbd6a7517e2b78f9181c6a2ff3b8943af92b0195dcb7cce48aa3e17893173c0a39e23d file2.txt + +``sha512sum <file>...`` + Create SHA512 checksum of files in ``sha512sum`` compatible format:: + + 2a78d7a6c5328cfb1467c63beac8ff21794213901eaadafd48e7800289afbc08e5fb3e86aa31116c945ee3d7bf2a6194489ec6101051083d1108defc8e1dba89 file1.txt + 7a0b54896fe5e70cca6dd643ad6f672614b189bf26f8153061c4d219474b05dad08c4e729af9f4b009f1a1a280cb625454bf587c690f4617c27e3aebdf3b7a2d file2.txt + ``remove [-f] <file>...`` Remove the file(s). If any of the listed files already do not exist, the command returns a non-zero exit code, but no message diff --git a/Help/policy/CMP0071.rst b/Help/policy/CMP0071.rst new file mode 100644 index 0000000..61f14dc --- /dev/null +++ b/Help/policy/CMP0071.rst @@ -0,0 +1,34 @@ +CMP0071 +------- + +Let :prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC` process +:prop_sf:`GENERATED` files. + +CMake 3.10 and newer process regular *and* :prop_sf:`GENERATED` source files +in :prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC`. +In CMake 3.9 and lower, only regular source files were processed in +:prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC`, +:prop_sf:`GENERATED` source files were ignored. + +This policy affects how :prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC` process +source files that are :prop_sf:`GENERATED`. + +The ``OLD`` behavior for this policy is to *ignore* :prop_sf:`GENERATED` +source files in :prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC`. + +The ``NEW`` behavior for this policy is to process :prop_sf:`GENERATED` +source files in :prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC` just like regular +source files. + +.. note:: + To exclude source files from :prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC` + processing, the boolean source file properties + :prop_sf:`SKIP_AUTOMOC`, :prop_sf:`SKIP_AUTOUIC` and :prop_sf:`SKIP_AUTOGEN` + can be set accordingly. + +This policy was introduced in CMake version 3.10. CMake version +|release| warns when the policy is not set and uses ``OLD`` behavior. +Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` +explicitly. + +.. include:: DEPRECATED.txt diff --git a/Help/release/dev/autogen-generated-files.rst b/Help/release/dev/autogen-generated-files.rst new file mode 100644 index 0000000..da2fc4e --- /dev/null +++ b/Help/release/dev/autogen-generated-files.rst @@ -0,0 +1,8 @@ +autogen-generated-files +----------------------- + +* When using :prop_tgt:`AUTOMOC` or :prop_tgt:`AUTOUIC`, + source files that are :prop_sf:`GENERATED` will be processed as well. + They were ignored by :prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC` + in earlier releases. + See policy :policy:`CMP0071`. diff --git a/Help/release/dev/cmake-command-mode-shasum.rst b/Help/release/dev/cmake-command-mode-shasum.rst new file mode 100644 index 0000000..681e0c0 --- /dev/null +++ b/Help/release/dev/cmake-command-mode-shasum.rst @@ -0,0 +1,5 @@ +cmake-command-mode-shasum +------------------------- + +* Added sha1sum, sha224sum, sha256sum, sha384sum and sha512sum + as an equivalent to existing md5sum to cmake command mode. diff --git a/Modules/CMakeCXXCompilerId.cpp.in b/Modules/CMakeCXXCompilerId.cpp.in index 9aa096d..6572bb3 100644 --- a/Modules/CMakeCXXCompilerId.cpp.in +++ b/Modules/CMakeCXXCompilerId.cpp.in @@ -27,12 +27,18 @@ char const *info_cray = "INFO" ":" "compiler_wrapper[CrayPrgEnv]"; @CMAKE_CXX_COMPILER_ID_PLATFORM_CONTENT@ @CMAKE_CXX_COMPILER_ID_ERROR_FOR_TEST@ +#if defined(_MSC_VER) && defined(_MSVC_LANG) +#define CXX_STD _MSVC_LANG +#else +#define CXX_STD __cplusplus +#endif + const char* info_language_dialect_default = "INFO" ":" "dialect_default[" -#if __cplusplus > 201402L +#if CXX_STD > 201402L "17" -#elif __cplusplus >= 201402L +#elif CXX_STD >= 201402L "14" -#elif __cplusplus >= 201103L +#elif CXX_STD >= 201103L "11" #else "98" diff --git a/Modules/Compiler/MSVC-CXX.cmake b/Modules/Compiler/MSVC-CXX.cmake index 9371301..f478b85 100644 --- a/Modules/Compiler/MSVC-CXX.cmake +++ b/Modules/Compiler/MSVC-CXX.cmake @@ -3,7 +3,25 @@ include(Compiler/CMakeCommonCompilerMacros) -if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0) +if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.10.25017) + # VS 2015 Update 3 and above support language standard level flags, + # with the default and minimum level being C++14. + set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "") + set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION "") + set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "") + set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "") + set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std:c++14") + set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std:c++14") + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.11.25505) + set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std:c++17") + set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std:c++17") + else() + set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std:c++latest") + set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std:c++latest") + endif() + + __compiler_check_default_language_standard(CXX 19.0 14) +elseif (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0) # MSVC has no specific options to set language standards, but set them as # empty strings anyways so the feature test infrastructure can at least check # to see if they are defined. diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index 2495736..d92eb5f 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -5,413 +5,860 @@ ExternalProject --------------- -Create custom targets to build projects in external trees +.. only:: html + + .. contents:: + +External Project Definition +^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. command:: ExternalProject_Add - The ``ExternalProject_Add`` function creates a custom target to drive + The ``ExternalProject_Add()`` function creates a custom target to drive download, update/patch, configure, build, install and test steps of an external project:: - ExternalProject_Add(<name> [<option>...]) - - General options are: - - ``DEPENDS <projects>...`` - Targets on which the project depends - ``PREFIX <dir>`` - Root dir for entire project - ``LIST_SEPARATOR <sep>`` - Sep to be replaced by ; in cmd lines - ``TMP_DIR <dir>`` - Directory to store temporary files - ``STAMP_DIR <dir>`` - Directory to store step timestamps - ``EXCLUDE_FROM_ALL 1`` - The "all" target does not depend on this - - Download step options are: - - ``DOWNLOAD_NAME <fname>`` - File name to store (if not end of URL) - ``DOWNLOAD_DIR <dir>`` - Directory to store downloaded files - ``DOWNLOAD_COMMAND <cmd>...`` - Command to download source tree - ``DOWNLOAD_NO_PROGRESS 1`` - Disable download progress reports - ``CVS_REPOSITORY <cvsroot>`` - CVSROOT of CVS repository - ``CVS_MODULE <mod>`` - Module to checkout from CVS repo - ``CVS_TAG <tag>`` - Tag to checkout from CVS repo - ``SVN_REPOSITORY <url>`` - URL of Subversion repo - ``SVN_REVISION -r<rev>`` - Revision to checkout from Subversion repo - ``SVN_USERNAME <username>`` - Username for Subversion checkout and update - ``SVN_PASSWORD <password>`` - Password for Subversion checkout and update - ``SVN_TRUST_CERT 1`` - Trust the Subversion server site certificate - ``GIT_REPOSITORY <url>`` - URL of git repo - ``GIT_TAG <tag>`` - Git branch name, commit id or tag - ``GIT_REMOTE_NAME <name>`` - The optional name of the remote, default to ``origin`` - ``GIT_SUBMODULES <module>...`` - Git submodules that shall be updated, all if empty - ``GIT_SHALLOW 1`` - Tell Git to clone with ``--depth 1``. Use when ``GIT_TAG`` is not - specified or when it names a branch in order to download only the - tip of the branch without the rest of its history. - ``GIT_PROGRESS 1`` - Tell Git to clone with ``--progress``. For large projects, the clone step - does not output anything which can make the build appear to have stalled. - This option forces Git to output progress information during the clone step - so that forward progress is indicated. - ``GIT_CONFIG <option>...`` - Tell Git to clone with ``--config <option>``. Use additional configuration - parameters when cloning the project (``key=value`` as expected by ``git - config``). - ``HG_REPOSITORY <url>`` - URL of mercurial repo - ``HG_TAG <tag>`` - Mercurial branch name, commit id or tag - ``URL /.../src.tgz [/.../src.tgz]...`` - Full path or URL(s) of source. Multiple URLs are allowed as mirrors. - ``URL_HASH ALGO=value`` - Hash of file at URL - ``URL_MD5 md5`` - Equivalent to URL_HASH MD5=md5 - ``HTTP_USERNAME <username>`` - Username for download operation - ``HTTP_PASSWORD <username>`` - Password for download operation - ``HTTP_HEADER <header>`` - HTTP header for download operation. Suboption can be repeated several times. - ``TLS_VERIFY <bool>`` - Should certificate for https be checked - ``TLS_CAINFO <file>`` - Path to a certificate authority file - ``TIMEOUT <seconds>`` - Time allowed for file download operations - ``DOWNLOAD_NO_EXTRACT 1`` - Just download the file and do not extract it; the full path to the - downloaded file is available as ``<DOWNLOADED_FILE>``. - - Update/Patch step options are: - - ``UPDATE_COMMAND <cmd>...`` - Source work-tree update command - ``UPDATE_DISCONNECTED 1`` - Never update automatically from the remote repository - ``PATCH_COMMAND <cmd>...`` - Command to patch downloaded source - - Configure step options are: - - ``SOURCE_DIR <dir>`` - Source dir to be used for build - ``SOURCE_SUBDIR <dir>`` - Path to source CMakeLists.txt relative to ``SOURCE_DIR`` - ``CONFIGURE_COMMAND <cmd>...`` - Build tree configuration command - ``CMAKE_COMMAND /.../cmake`` - Specify alternative cmake executable - ``CMAKE_GENERATOR <gen>`` - Specify generator for native build - ``CMAKE_GENERATOR_PLATFORM <platform>`` - Generator-specific platform name - ``CMAKE_GENERATOR_TOOLSET <toolset>`` - Generator-specific toolset name - ``CMAKE_ARGS <arg>...`` - Arguments to CMake command line. - These arguments are passed to CMake command line, and can contain - arguments other than cache values, see also - :manual:`CMake Options <cmake(1)>`. Arguments in the form - ``-Dvar:string=on`` are always passed to the command line, and - therefore cannot be changed by the user. - Arguments may use - :manual:`generator expressions <cmake-generator-expressions(7)>`. - ``CMAKE_CACHE_ARGS <arg>...`` - Initial cache arguments, of the form ``-Dvar:string=on``. - These arguments are written in a pre-load a script that populates - CMake cache, see also :manual:`cmake -C <cmake(1)>`. This allows one to - overcome command line length limits. - These arguments are :command:`set` using the ``FORCE`` argument, - and therefore cannot be changed by the user. - Arguments may use - :manual:`generator expressions <cmake-generator-expressions(7)>`. - ``CMAKE_CACHE_DEFAULT_ARGS <arg>...`` - Initial default cache arguments, of the form ``-Dvar:string=on``. - These arguments are written in a pre-load a script that populates - CMake cache, see also :manual:`cmake -C <cmake(1)>`. This allows one to - overcome command line length limits. - These arguments can be used as default value that will be set if no - previous value is found in the cache, and that the user can change - later. - Arguments may use - :manual:`generator expressions <cmake-generator-expressions(7)>`. - - Build step options are: - - ``BINARY_DIR <dir>`` - Specify build dir location - ``BUILD_COMMAND <cmd>...`` - Command to drive the native build - ``BUILD_IN_SOURCE 1`` - Use source dir for build dir - ``BUILD_ALWAYS 1`` - No stamp file, build step always runs - ``BUILD_BYPRODUCTS <file>...`` - Files that will be generated by the build command but may or may - not have their modification time updated by subsequent builds. - - Install step options are: - - ``INSTALL_DIR <dir>`` - Installation prefix to be placed in the ``<INSTALL_DIR>`` placeholder. - This does not actually configure the external project to install to - the given prefix. That must be done by passing appropriate arguments - to the external project configuration step, e.g. using ``<INSTALL_DIR>``. - ``INSTALL_COMMAND <cmd>...`` - Command to drive installation of the external project after it has been - built. This only happens at the *build* time of the calling project. - In order to install files from the external project alongside the - locally-built files, a separate local :command:`install` call must be - added to pick the files up from one of the external project trees. - - Test step options are: - - ``TEST_BEFORE_INSTALL 1`` - Add test step executed before install step - ``TEST_AFTER_INSTALL 1`` - Add test step executed after install step - ``TEST_EXCLUDE_FROM_MAIN 1`` - Main target does not depend on the test step - ``TEST_COMMAND <cmd>...`` - Command to drive test - - Output logging options are: - - ``LOG_DOWNLOAD 1`` - Wrap download in script to log output - ``LOG_UPDATE 1`` - Wrap update in script to log output - ``LOG_CONFIGURE 1`` - Wrap configure in script to log output - ``LOG_BUILD 1`` - Wrap build in script to log output - ``LOG_TEST 1`` - Wrap test in script to log output - ``LOG_INSTALL 1`` - Wrap install in script to log output - - Steps can be given direct access to the terminal if possible. With - the :generator:`Ninja` generator, this places the steps in the - ``console`` :prop_gbl:`pool <JOB_POOLS>`. Options are: - - ``USES_TERMINAL_DOWNLOAD 1`` - Give download terminal access. - ``USES_TERMINAL_UPDATE 1`` - Give update terminal access. - ``USES_TERMINAL_CONFIGURE 1`` - Give configure terminal access. - ``USES_TERMINAL_BUILD 1`` - Give build terminal access. - ``USES_TERMINAL_TEST 1`` - Give test terminal access. - ``USES_TERMINAL_INSTALL 1`` - Give install terminal access. - - Other options are: - - ``STEP_TARGETS <step-target>...`` - Generate custom targets for these steps - ``INDEPENDENT_STEP_TARGETS <step-target>...`` - Generate custom targets for these steps that do not depend on other - external projects even if a dependency is set - - The ``*_DIR`` options specify directories for the project, with default - directories computed as follows. If the ``PREFIX`` option is given to - ``ExternalProject_Add()`` or the ``EP_PREFIX`` directory property is set, - then an external project is built and installed under the specified prefix:: - - TMP_DIR = <prefix>/tmp - STAMP_DIR = <prefix>/src/<name>-stamp - DOWNLOAD_DIR = <prefix>/src - SOURCE_DIR = <prefix>/src/<name> - BINARY_DIR = <prefix>/src/<name>-build - INSTALL_DIR = <prefix> - - Otherwise, if the ``EP_BASE`` directory property is set then components - of an external project are stored under the specified base:: - - TMP_DIR = <base>/tmp/<name> - STAMP_DIR = <base>/Stamp/<name> - DOWNLOAD_DIR = <base>/Download/<name> - SOURCE_DIR = <base>/Source/<name> - BINARY_DIR = <base>/Build/<name> - INSTALL_DIR = <base>/Install/<name> - - If no ``PREFIX``, ``EP_PREFIX``, or ``EP_BASE`` is specified then the - default is to set ``PREFIX`` to ``<name>-prefix``. Relative paths are - interpreted with respect to the build directory corresponding to the - source directory in which ``ExternalProject_Add`` is invoked. - - If ``SOURCE_SUBDIR`` is set and no ``CONFIGURE_COMMAND`` is specified, the - configure command will run CMake using the ``CMakeLists.txt`` located in the - relative path specified by ``SOURCE_SUBDIR``, relative to the ``SOURCE_DIR``. - If no ``SOURCE_SUBDIR`` is given, ``SOURCE_DIR`` is used. - - If ``SOURCE_DIR`` is explicitly set to an existing directory the project - will be built from it. Otherwise a download step must be specified - using one of the ``DOWNLOAD_COMMAND``, ``CVS_*``, ``SVN_*``, or ``URL`` - options. The ``URL`` option may refer locally to a directory or source - tarball, or refer to a remote tarball (e.g. ``http://.../src.tgz``). - - If ``UPDATE_DISCONNECTED`` is set, the update step is not executed - automatically when building the main target. The update step can still - be added as a step target and called manually. This is useful if you - want to allow one to build the project when you are disconnected from the - network (you might still need the network for the download step). - This is disabled by default. - The directory property ``EP_UPDATE_DISCONNECTED`` can be used to change - the default value for all the external projects in the current - directory and its subdirectories. + ExternalProject_Add(<name> [<option>...]) + + The individual steps within the process can be driven independently if + required (e.g. for CDash submission) and extra custom steps can be defined, + along with the ability to control the step dependencies. The directory + structure used for the management of the external project can also be + customized. The function supports a large number of options which can be used + to tailor the external project behavior. + + **Directory Options:** + Most of the time, the default directory layout is sufficient. It is largely + an implementation detail that the main project usually doesn't need to + change. In some circumstances, however, control over the directory layout + can be useful or necessary. The directory options are potentially more + useful from the point of view that the main build can use the + :command:`ExternalProject_Get_Property` command to retrieve their values, + thereby allowing the main project to refer to build artifacts of the + external project. + + ``PREFIX <dir>`` + Root directory for the external project. Unless otherwise noted below, + all other directories associated with the external project will be + created under here. + + ``TMP_DIR <dir>`` + Directory in which to store temporary files. + + ``STAMP_DIR <dir>`` + Directory in which to store the timestamps of each step. Log files from + individual steps are also created in here (see *Logging Options* below). + + ``DOWNLOAD_DIR <dir>`` + Directory in which to store downloaded files before unpacking them. This + directory is only used by the URL download method, all other download + methods use ``SOURCE_DIR`` directly instead. + + ``SOURCE_DIR <dir>`` + Source directory into which downloaded contents will be unpacked, or for + non-URL download methods, the directory in which the repository should be + checked out, cloned, etc. If no download method is specified, this must + point to an existing directory where the external project has already + been unpacked or cloned/checked out. + + .. note:: + If a download method is specified, any existing contents of the source + directory may be deleted. Only the URL download method checks whether + this directory is either missing or empty before initiating the + download, stopping with an error if it is not empty. All other + download methods silently discard any previous contents of the source + directory. + + ``BINARY_DIR <dir>`` + Specify the build directory location. This option is ignored if + ``BUILD_IN_SOURCE`` is enabled. + + ``INSTALL_DIR <dir>`` + Installation prefix to be placed in the ``<INSTALL_DIR>`` placeholder. + This does not actually configure the external project to install to + the given prefix. That must be done by passing appropriate arguments + to the external project configuration step, e.g. using ``<INSTALL_DIR>``. + + If any of the above ``..._DIR`` options are not specified, their defaults + are computed as follows. If the ``PREFIX`` option is given or the + ``EP_PREFIX`` directory property is set, then an external project is built + and installed under the specified prefix:: + + TMP_DIR = <prefix>/tmp + STAMP_DIR = <prefix>/src/<name>-stamp + DOWNLOAD_DIR = <prefix>/src + SOURCE_DIR = <prefix>/src/<name> + BINARY_DIR = <prefix>/src/<name>-build + INSTALL_DIR = <prefix> + + Otherwise, if the ``EP_BASE`` directory property is set then components + of an external project are stored under the specified base:: + + TMP_DIR = <base>/tmp/<name> + STAMP_DIR = <base>/Stamp/<name> + DOWNLOAD_DIR = <base>/Download/<name> + SOURCE_DIR = <base>/Source/<name> + BINARY_DIR = <base>/Build/<name> + INSTALL_DIR = <base>/Install/<name> + + If no ``PREFIX``, ``EP_PREFIX``, or ``EP_BASE`` is specified, then the + default is to set ``PREFIX`` to ``<name>-prefix``. Relative paths are + interpreted with respect to :variable:`CMAKE_CURRENT_BINARY_DIR` at the + point where ``ExternalProject_Add()`` is called. + + **Download Step Options:** + A download method can be omitted if the ``SOURCE_DIR`` option is used to + point to an existing non-empty directory. Otherwise, one of the download + methods below must be specified (multiple download methods should not be + given) or a custom ``DOWNLOAD_COMMAND`` provided. + + ``DOWNLOAD_COMMAND <cmd>...`` + Overrides the command used for the download step + (:manual:`generator expressions <cmake-generator-expressions(7)>` are + supported). If this option is specified, all other download options will + be ignored. Providing an empty string for ``<cmd>`` effectively disables + the download step. + + *URL Download* + ``URL <url1> [<url2>...]`` + List of paths and/or URL(s) of the external project's source. When more + than one URL is given, they are tried in turn until one succeeds. A URL + may be an ordinary path in the local file system (in which case it + must be the only URL provided) or any downloadable URL supported by the + :command:`file(DOWNLOAD)` command. A local filesystem path may refer to + either an existing directory or to an archive file, whereas a URL is + expected to point to a file which can be treated as an archive. When an + archive is used, it will be unpacked automatically unless the + ``DOWNLOAD_NO_EXTRACT`` option is set to prevent it. The archive type + is determined by inspecting the actual content rather than using logic + based on the file extension. + + ``URL_HASH ALGO=<value>`` + Hash of the archive file to be downloaded. The ``<value>`` should be of + the form ``algo=hashValue`` where ``algo`` can be any of the hashing + algorithms supported by the :command:`file()` command. Specifying this + option is strongly recommended for URL downloads, as it ensures the + integrity of the downloaded content. It is also used as a check for a + previously downloaded file, allowing connection to the remote location + to be avoided altogether if the local directory already has a file from + an earlier download that matches the specified hash. + + ``URL_MD5 <md5>`` + Equivalent to ``URL_HASH MD5=<md5>``. + + ``DOWNLOAD_NAME <fname>`` + File name to use for the downloaded file. If not given, the end of the + URL is used to determine the file name. This option is rarely needed, + the default name is generally suitable and is not normally used outside + of code internal to the ``ExternalProject`` module. + + ``DOWNLOAD_NO_EXTRACT <bool>`` + Allows the extraction part of the download step to be disabled by + passing a boolean true value for this option. If this option is not + given, the downloaded contents will be unpacked automatically if + required. If extraction has been disabled, the full path to the + downloaded file is available as ``<DOWNLOADED_FILE>`` in subsequent + steps or as the property ``DOWNLOADED_FILE`` with the + :command:`ExternalProject_Get_Property` command. + + ``DOWNLOAD_NO_PROGRESS <bool>`` + Can be used to disable logging the download progress. If this option is + not given, download progress messages will be logged. + + ``TIMEOUT <seconds>`` + Maximum time allowed for file download operations. + + ``HTTP_USERNAME <username>`` + Username for the download operation if authentication is required. + + ``HTTP_PASSWORD <password>`` + Password for the download operation if authentication is required. + + ``HTTP_HEADER <header1> [<header2>...]`` + Provides an arbitrary list of HTTP headers for the download operation. + This can be useful for accessing content in systems like AWS, etc. + + ``TLS_VERIFY <bool>`` + Specifies whether certificate verification should be performed for + https URLs. If this option is not provided, the default behavior is + determined by the ``CMAKE_TLS_VERIFY`` variable (see + :command:`file(DOWNLOAD)`). If that is also not set, certificate + verification will not be performed. In situations where ``URL_HASH`` + cannot be provided, this option can be an alternative verification + measure. + + ``TLS_CAINFO <file>`` + Specify a custom certificate authority file to use if ``TLS_VERIFY`` + is enabled. If this option is not specified, the value of the + ``CMAKE_TLS_CAINFO`` variable will be used instead (see + :command:`file(DOWNLOAD)`) + + *Git* + NOTE: A git version of 1.6.5 or later is required if this download method + is used. + + ``GIT_REPOSITORY <url>`` + URL of the git repository. Any URL understood by the ``git`` command + may be used. + + ``GIT_TAG <tag>`` + Git branch name, tag or commit hash. Note that branch names and tags + should generally be specified as remote names (i.e. ``origin/myBranch`` + rather than simply ``myBranch``). This ensures that if the remote end + has its tag moved or branch rebased or history rewritten, the local + clone will still be updated correctly. In general, however, specifying + a commit hash should be preferred for a number of reasons: + + - If the local clone already has the commit corresponding to the hash, + no ``git fetch`` needs to be performed to check for changes each time + CMake is re-run. This can result in a significant speed up if many + external projects are being used. + - Using a specific git hash ensures that the main project's own history + is fully traceable to a specific point in the external project's + evolution. If a branch or tag name is used instead, then checking out + a specific commit of the main project doesn't necessarily pin the + whole build to a specific point in the life of the external project. + The lack of such deterministic behavior makes the main project lose + traceability and repeatability. + + ``GIT_REMOTE_NAME <name>`` + The optional name of the remote. If this option is not specified, it + defaults to ``origin``. + + ``GIT_SUBMODULES <module>...`` + Specific git submodules that should also be updated. If this option is + not provided, all git submodules will be updated. + + ``GIT_SHALLOW <bool>`` + When this option is enabled, the ``git clone`` operation will be given + the ``--depth 1`` option. This performs a shallow clone, which avoids + downloading the whole history and instead retrieves just the commit + denoted by the ``GIT_TAG`` option. + + ``GIT_PROGRESS <bool>`` + When enabled, this option instructs the ``git clone`` operation to + report its progress by passing it the ``--progress`` option. Without + this option, the clone step for large projects may appear to make the + build stall, since nothing will be logged until the clone operation + finishes. While this option can be used to provide progress to prevent + the appearance of the build having stalled, it may also make the build + overly noisy if lots of external projects are used. + + ``GIT_CONFIG <option1> [<option2>...]`` + Specify a list of config options to pass to ``git clone``. Each option + listed will be transformed into its own ``--config <option>`` on the + ``git clone`` command line, with each option required to be in the + form ``key=value``. + + *Subversion* + ``SVN_REPOSITORY <url>`` + URL of the Subversion repository. + + ``SVN_REVISION -r<rev>`` + Revision to checkout from the Subversion repository. + + ``SVN_USERNAME <username>`` + Username for the Subversion checkout and update. + + ``SVN_PASSWORD <password>`` + Password for the Subversion checkout and update. + + ``SVN_TRUST_CERT <bool>`` + Specifies whether to trust the Subversion server site certificate. If + enabled, the ``--trust-server-cert`` option is passed to the ``svn`` + checkout and update commands. + + *Mercurial* + ``HG_REPOSITORY <url>`` + URL of the mercurial repository. + + ``HG_TAG <tag>`` + Mercurial branch name, tag or commit id. + + *CVS* + ``CVS_REPOSITORY <cvsroot>`` + CVSROOT of the CVS repository. + + ``CVS_MODULE <mod>`` + Module to checkout from the CVS repository. + + ``CVS_TAG <tag>`` + Tag to checkout from the CVS repository. + + **Update/Patch Step Options:** + Whenever CMake is re-run, by default the external project's sources will be + updated if the download method supports updates (e.g. a git repository + would be checked if the ``GIT_TAG`` does not refer to a specific commit). + + ``UPDATE_COMMAND <cmd>...`` + Overrides the download method's update step with a custom command. + The command may use + :manual:`generator expressions <cmake-generator-expressions(7)>`. + + ``UPDATE_DISCONNECTED <bool>`` + When enabled, this option causes the update step to be skipped. It does + not, however, prevent the download step. The update step can still be + added as a step target (see :command:`ExternalProject_Add_StepTargets`) + and called manually. This is useful if you want to allow developers to + build the project when disconnected from the network (the network may + still be needed for the download step though). + + When this option is present, it is generally advisable to make the value + a cache variable under the developer's control rather than hard-coding + it. If this option is not present, the default value is taken from the + ``EP_UPDATE_DISCONNECTED`` directory property. If that is also not + defined, updates are performed as normal. The ``EP_UPDATE_DISCONNECTED`` + directory property is intended as a convenience for controlling the + ``UPDATE_DISCONNECTED`` behavior for an entire section of a project's + directory hierarchy and may be a more convenient method of giving + developers control over whether or not to perform updates (assuming the + project also provides a cache variable or some other convenient method + for setting the directory property). + + ``PATCH_COMMAND <cmd>...`` + Specifies a custom command to patch the sources after an update. By + default, no patch command is defined. Note that it can be quite difficult + to define an appropriate patch command that performs robustly, especially + for download methods such as git where changing the ``GIT_TAG`` will not + discard changes from a previous patch, but the patch command will be + called again after updating to the new tag. + + **Configure Step Options:** + The configure step is run after the download and update steps. By default, + the external project is assumed to be a CMake project, but this can be + overridden if required. + + ``CONFIGURE_COMMAND <cmd>...`` + The default configure command runs CMake with options based on the main + project. For non-CMake external projects, the ``CONFIGURE_COMMAND`` + option must be used to override this behavior + (:manual:`generator expressions <cmake-generator-expressions(7)>` are + supported). For projects that require no configure step, specify this + option with an empty string as the command to execute. + + ``CMAKE_COMMAND /.../cmake`` + Specify an alternative cmake executable for the configure step (use an + absolute path). This is generally not recommended, since it is + usually desirable to use the same CMake version throughout the whole + build. This option is ignored if a custom configure command has been + specified with ``CONFIGURE_COMMAND``. + + ``CMAKE_GENERATOR <gen>`` + Override the CMake generator used for the configure step. Without this + option, the same generator as the main build will be used. This option is + ignored if a custom configure command has been specified with the + ``CONFIGURE_COMMAND`` option. + + ``CMAKE_GENERATOR_PLATFORM <platform>`` + Pass a generator-specific platform name to the CMake command (see + :variable:`CMAKE_GENERATOR_PLATFORM`). It is an error to provide this + option without the ``CMAKE_GENERATOR`` option. + + ``CMAKE_GENERATOR_TOOLSET <toolset>`` + Pass a generator-specific toolset name to the CMake command (see + :variable:`CMAKE_GENERATOR_TOOLSET`). It is an error to provide this + option without the ``CMAKE_GENERATOR`` option. + + ``CMAKE_ARGS <arg>...`` + The specified arguments are passed to the ``cmake`` command line. They + can be any argument the ``cmake`` command understands, not just cache + values defined by ``-D...`` arguments (see also + :manual:`CMake Options <cmake(1)>`). In addition, arguments may use + :manual:`generator expressions <cmake-generator-expressions(7)>`. + + ``CMAKE_CACHE_ARGS <arg>...`` + This is an alternate way of specifying cache variables where command line + length issues may become a problem. The arguments are expected to be in + the form ``-Dvar:STRING=value``, which are then transformed into + CMake :command:`set` commands with the ``FORCE`` option used. These + ``set()`` commands are written to a pre-load script which is then applied + using the :manual:`cmake -C <cmake(1)>` command line option. Arguments + may use :manual:`generator expressions <cmake-generator-expressions(7)>`. + + ``CMAKE_CACHE_DEFAULT_ARGS <arg>...`` + This is the same as the ``CMAKE_CACHE_ARGS`` option except the ``set()`` + commands do not include the ``FORCE`` keyword. This means the values act + as initial defaults only and will not override any variables already set + from a previous run. Use this option with care, as it can lead to + different behavior depending on whether the build starts from a fresh + build directory or re-uses previous build contents. + + ``SOURCE_SUBDIR <dir>`` + When no ``CONFIGURE_COMMAND`` option is specified, the configure step + assumes the external project has a ``CMakeLists.txt`` file at the top of + its source tree (i.e. in ``SOURCE_DIR``). The ``SOURCE_SUBDIR`` option + can be used to point to an alternative directory within the source tree + to use as the top of the CMake source tree instead. This must be a + relative path and it will be interpreted as being relative to + ``SOURCE_DIR``. + + **Build Step Options:** + If the configure step assumed the external project uses CMake as its build + system, the build step will also. Otherwise, the build step will assume a + Makefile-based build and simply run ``make`` with no arguments as the + default build step. This can be overridden with custom build commands if + required. + + ``BUILD_COMMAND <cmd>...`` + Overrides the default build command + (:manual:`generator expressions <cmake-generator-expressions(7)>` are + supported). If this option is not given, the default build command will + be chosen to integrate with the main build in the most appropriate way + (e.g. using recursive ``make`` for Makefile generators or + ``cmake --build`` if the project uses a CMake build). This option can be + specified with an empty string as the command to make the build step do + nothing. + + ``BUILD_IN_SOURCE <bool>`` + When this option is enabled, the build will be done directly within the + external project's source tree. This should generally be avoided, the use + of a separate build directory is usually preferred, but it can be useful + when the external project assumes an in-source build. The ``BINARY_DIR`` + option should not be specified if building in-source. + + ``BUILD_ALWAYS <bool>`` + Enabling this option forces the build step to always be run. This can be + the easiest way to robustly ensure that the external project's own build + dependencies are evaluated rather than relying on the default + success timestamp-based method. This option is not normally needed unless + developers are expected to modify something the external project's build + depends on in a way that is not detectable via the step target + dependencies (e.g. ``SOURCE_DIR`` is used without a download method and + developers might modify the sources in ``SOURCE_DIR``). + + ``BUILD_BYPRODUCTS <file>...`` + Specifies files that will be generated by the build command but which + might or might not have their modification time updated by subsequent + builds. These ultimately get passed through as ``BYPRODUCTS`` to the + build step's own underlying call to :command:`add_custom_command`. + + **Install Step Options:** + If the configure step assumed the external project uses CMake as its build + system, the install step will also. Otherwise, the install step will assume + a Makefile-based build and simply run ``make install`` as the default build + step. This can be overridden with custom install commands if required. + + ``INSTALL_COMMAND <cmd>...`` + The external project's own install step is invoked as part of the main + project's *build*. It is done after the external project's build step + and may be before or after the external project's test step (see the + ``TEST_BEFORE_INSTALL`` option below). The external project's install + rules are not part of the main project's install rules, so if anything + from the external project should be installed as part of the main build, + these need to be specified in the main build as additional + :command:`install` commands. The default install step builds the + ``install`` target of the external project, but this can be overridden + with a custom command using this option + (:manual:`generator expressions <cmake-generator-expressions(7)>` are + supported). Passing an empty string as the ``<cmd>`` makes the install + step do nothing. + + **Test Step Options:** + The test step is only defined if at least one of the following ``TEST_...`` + options are provided. + + ``TEST_COMMAND <cmd>...`` + Overrides the default test command + (:manual:`generator expressions <cmake-generator-expressions(7)>` are + supported). If this option is not given, the default behavior of the test + step is to build the external project's own ``test`` target. This option + can be specified with ``<cmd>`` as an empty string, which allows the test + step to still be defined, but it will do nothing. Do not specify any of + the other ``TEST_...`` options if providing an empty string as the test + command, but prefer to omit all ``TEST_...`` options altogether if the + test step target is not needed. + + ``TEST_BEFORE_INSTALL <bool>`` + When this option is enabled, the test step will be executed before the + install step. The default behavior is for the test step to run after the + install step. + + ``TEST_AFTER_INSTALL <bool>`` + This option is mainly useful as a way to indicate that the test step is + desired but all default behavior is sufficient. Specifying this option + with a boolean true value ensures the test step is defined and that it + comes after the install step. If both ``TEST_BEFORE_INSTALL`` and + ``TEST_AFTER_INSTALL`` are enabled, the latter is silently ignored. + + ``TEST_EXCLUDE_FROM_MAIN <bool>`` + If enabled, the main build's default ALL target will not depend on the + test step. This can be a useful way of ensuring the test step is defined + but only gets invoked when manually requested. + + **Output Logging Options:** + Each of the following ``LOG_...`` options can be used to wrap the relevant + step in a script to capture its output to files. The log files will be + created in the ``STAMP_DIR`` directory with step-specific file names. + + ``LOG_DOWNLOAD <bool>`` + When enabled, the output of the download step is logged to files. + + ``LOG_UPDATE <bool>`` + When enabled, the output of the update step is logged to files. + + ``LOG_CONFIGURE <bool>`` + When enabled, the output of the configure step is logged to files. + + ``LOG_BUILD <bool>`` + When enabled, the output of the build step is logged to files. + + ``LOG_INSTALL <bool>`` + When enabled, the output of the install step is logged to files. + + ``LOG_TEST <bool>`` + When enabled, the output of the test step is logged to files. + + **Terminal Access Options:** + Steps can be given direct access to the terminal in some cases. Giving a + step access to the terminal may allow it to receive terminal input if + required, such as for authentication details not provided by other options. + With the :generator:`Ninja` generator, these options place the steps in the + ``console`` :prop_gbl:`job pool <JOB_POOLS>`. Each step can be given access + to the terminal individually via the following options: + + ``USES_TERMINAL_DOWNLOAD <bool>`` + Give the download step access to the terminal. + + ``USES_TERMINAL_UPDATE <bool>`` + Give the update step access to the terminal. + + ``USES_TERMINAL_CONFIGURE <bool>`` + Give the configure step access to the terminal. + + ``USES_TERMINAL_BUILD <bool>`` + Give the build step access to the terminal. + + ``USES_TERMINAL_INSTALL <bool>`` + Give the install step access to the terminal. + + ``USES_TERMINAL_TEST <bool>`` + Give the test step access to the terminal. + + **Target Options:** + ``DEPENDS <targets>...`` + Specify other targets on which the external project depends. The other + targets will be brought up to date before any of the external project's + steps are executed. Because the external project uses additional custom + targets internally for each step, the ``DEPENDS`` option is the most + convenient way to ensure all of those steps depend on the other targets. + Simply doing + :command:`add_dependencies(\<name\> \<targets\>) <add_dependencies>` will + not make any of the steps dependent on ``<targets>``. + + ``EXCLUDE_FROM_ALL <bool>`` + When enabled, this option excludes the external project from the default + ALL target of the main build. + + ``STEP_TARGETS <step-target>...`` + Generate custom targets for the specified steps. This is required if the + steps need to be triggered manually or if they need to be used as + dependencies of other targets. If this option is not specified, the + default value is taken from the ``EP_STEP_TARGETS`` directory property. + See :command:`ExternalProject_Add_Step` below for further discussion of + the effects of this option. + + ``INDEPENDENT_STEP_TARGETS <step-target>...`` + Generate custom targets for the specified steps and prevent these targets + from having the usual dependencies applied to them. If this option is not + specified, the default value is taken from the + ``EP_INDEPENDENT_STEP_TARGETS`` directory property. This option is mostly + useful for allowing individual steps to be driven independently, such as + for a CDash setup where each step should be initiated and reported + individually rather than as one whole build. See + :command:`ExternalProject_Add_Step` below for further discussion of the + effects of this option. + + **Miscellaneous Options:** + ``LIST_SEPARATOR <sep>`` + For any of the various ``..._COMMAND`` options, replace ``;`` with + ``<sep>`` in the specified command lines. This can be useful where list + variables may be given in commands where they should end up as + space-separated arguments (``<sep>`` would be a single space character + string in this case). + + ``COMMAND <cmd>...`` + Any of the other ``..._COMMAND`` options can have additional commands + appended to them by following them with as many ``COMMAND ...`` options + as needed + (:manual:`generator expressions <cmake-generator-expressions(7)>` are + supported). For example:: + + ExternalProject_Add(example + ... # Download options, etc. + BUILD_COMMAND ${CMAKE_COMMAND} -E echo "Starting $<CONFIG> build" + COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --config $<CONFIG> + COMMAND ${CMAKE_COMMAND} -E echo "$<CONFIG> build complete" + ) + + It should also be noted that each build step is created via a call to + :command:`ExternalProject_Add_Step`. See that command's documentation for the + automatic substitutions that are supported for some options. + +Obtaining Project Properties +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. command:: ExternalProject_Get_Property + + The ``ExternalProject_Get_Property()`` function retrieves external project + target properties:: + + ExternalProject_Get_Property(<name> <prop1> [<prop2>...]) + + The function stores property values in variables of the same name. Property + names correspond to the keyword argument names of ``ExternalProject_Add()``. + For example, the source directory might be retrieved like so: + + .. code-block:: cmake + + ExternalProject_Get_property(myExtProj SOURCE_DIR) + message("Source dir of myExtProj = ${SOURCE_DIR}") + +Explicit Step Management +^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``ExternalProject_Add()`` function on its own is often sufficient for +incorporating an external project into the main build. Certain scenarios +require additional work to implement desired behavior, such as adding in a +custom step or making steps available as manually triggerable targets. The +``ExternalProject_Add_Step()``, ``ExternalProject_Add_StepTargets()`` and +``ExternalProject_Add_StepDependencies`` functions provide the lower level +control needed to implement such step-level capabilities. .. command:: ExternalProject_Add_Step - The ``ExternalProject_Add_Step`` function adds a custom step to an - external project:: + The ``ExternalProject_Add_Step()`` function specifies an additional custom + step for an external project defined by an earlier call to + :command:`ExternalProject_Add`:: - ExternalProject_Add_Step(<name> <step> [<option>...]) + ExternalProject_Add_Step(<name> <step> [<option>...]) - Options are: + ``<name>`` is the same as the name passed to the original call to + :command:`ExternalProject_Add`. The specified ``<step>`` must not be one of + the pre-defined steps (``mkdir``, ``download``, ``update``, ``skip-update``, + ``patch``, ``configure``, ``build``, ``install`` or ``test``). The supported + options are: ``COMMAND <cmd>...`` - Command line invoked by this step + The command line to be executed by this custom step + (:manual:`generator expressions <cmake-generator-expressions(7)>` are + supported). This option can be repeated multiple times to specify multiple + commands to be executed in order. + ``COMMENT "<text>..."`` - Text printed when step executes + Text to be printed when the custom step executes. + ``DEPENDEES <step>...`` - Steps on which this step depends + Other steps (custom or pre-defined) on which this step depends. + ``DEPENDERS <step>...`` - Steps that depend on this step + Other steps (custom or pre-defined) that depend on this new custom step. + ``DEPENDS <file>...`` - Files on which this step depends + Files on which this custom step depends. + ``BYPRODUCTS <file>...`` - Files that will be generated by this step but may or may not - have their modification time updated by subsequent builds. - ``ALWAYS 1`` - No stamp file, step always runs - ``EXCLUDE_FROM_MAIN 1`` - Main target does not depend on this step + Files that will be generated by this custom step but which might or might + not have their modification time updated by subsequent builds. This list of + files will ultimately be passed through as the ``BYPRODUCTS`` option to the + :command:`add_custom_command` used to implement the custom step internally. + + ``ALWAYS <bool>`` + When enabled, this option specifies that the custom step should always be + run (i.e. that it is always considered out of date). + + ``EXCLUDE_FROM_MAIN <bool>`` + When enabled, this option specifies that the external project's main target + does not depend on the custom step. + ``WORKING_DIRECTORY <dir>`` - Working directory for command - ``LOG 1`` - Wrap step in script to log output - ``USES_TERMINAL 1`` - Give the step direct access to the terminal if possible. + Specifies the working directory to set before running the custom step's + command. If this option is not specified, the directory will be the value + of the :variable:`CMAKE_CURRENT_BINARY_DIR` at the point where + ``ExternalProject_Add_Step()`` was called. - The command line, comment, working directory, and byproducts of every - standard and custom step are processed to replace tokens ``<SOURCE_DIR>``, - ``<SOURCE_SUBDIR>``, ``<BINARY_DIR>``, ``<INSTALL_DIR>``, and ``<TMP_DIR>`` - with corresponding property values. + ``LOG <bool>`` + If set, this causes the output from the custom step to be captured to files + in the external project's ``STAMP_DIR``. -Any builtin step that specifies a ``<step>_COMMAND cmd...`` or custom -step that specifies a ``COMMAND cmd...`` may specify additional command -lines using the form ``COMMAND cmd...``. At build time the commands -will be executed in order and aborted if any one fails. For example:: + ``USES_TERMINAL <bool>`` + If enabled, this gives the custom step direct access to the terminal if + possible. - ... BUILD_COMMAND make COMMAND echo done ... + The command line, comment, working directory and byproducts of every + standard and custom step are processed to replace the tokens + ``<SOURCE_DIR>``, ``<SOURCE_SUBDIR>``, ``<BINARY_DIR>``, ``<INSTALL_DIR>`` + and ``<TMP_DIR>`` with their corresponding property values defined in the + original call to :command:`ExternalProject_Add`. -specifies to run ``make`` and then ``echo done`` during the build step. -Whether the current working directory is preserved between commands is -not defined. Behavior of shell operators like ``&&`` is not defined. +.. command:: ExternalProject_Add_StepTargets -Arguments to ``<step>_COMMAND`` or ``COMMAND`` options may use -:manual:`generator expressions <cmake-generator-expressions(7)>`. + The ``ExternalProject_Add_StepTargets()`` function generates targets for the + steps listed. The name of each created target will be of the form + ``<name>-<step>``:: + + ExternalProject_Add_StepTargets(<name> [NO_DEPENDS] <step1> [<step2>...]) + + Creating a target for a step allows it to be used as a dependency of another + target or to be triggered manually. Having targets for specific steps also + allows them to be driven independently of each other by specifying targets on + build command lines. For example, you may be submitting to a sub-project + based dashboard where you want to drive the configure portion of the build, + then submit to the dashboard, followed by the build portion, followed + by tests. If you invoke a custom target that depends on a step halfway + through the step dependency chain, then all the previous steps will also run + to ensure everything is up to date. + + If the ``NO_DEPENDS`` option is specified, the step target will not depend on + the dependencies of the external project (i.e. on any dependencies of the + ``<name>`` custom target created by :command:`ExternalProject_Add`). This is + usually safe for the ``download``, ``update`` and ``patch`` steps, since they + do not typically require that the dependencies are updated and built. Using + ``NO_DEPENDS`` for any of the other pre-defined steps, however, may break + parallel builds. Only use ``NO_DEPENDS`` where it is certain that the named + steps genuinely do not have dependencies. For custom steps, consider whether + or not the custom commands require the dependencies to be configured, built + and installed. + + Internally, :command:`ExternalProject_Add` calls + :command:`ExternalProject_Add_Step` to create each step. If any + ``STEP_TARGETS`` or ``INDEPENDENT_STEP_TARGETS`` were specified, then + ``ExternalProject_Add_StepTargets()`` will also be called after + :command:`ExternalProject_Add_Step`. ``INDEPENDENT_STEP_TARGETS`` have the + ``NO_DEPENDS`` option set, whereas ``STEP_TARGETS`` do not. Other than that, + the two options result in ``ExternalProject_Add_StepTargets()`` being called + in the same way. Even if a step is not mentioned in either of those two + options, ``ExternalProject_Add_StepTargets()`` can still be called later to + manually define a target for the step. + + The ``STEP_TARGETS`` and ``INDEPENDENT_STEP_TARGETS`` options for + :command:`ExternalProject_Add` are generally the easiest way to ensure + targets are created for specific steps of interest. For custom steps, + ``ExternalProject_Add_StepTargets()`` must be called explicitly if a target + should also be created for that custom step. An alternative to these two + options is to populate the ``EP_STEP_TARGETS`` and + ``EP_INDEPENDENT_STEP_TARGETS`` directory properties. These act as defaults + for the step target options and can save having to repeatedly specify the + same set of step targets when multiple external projects are being defined. -.. command:: ExternalProject_Get_Property +.. command:: ExternalProject_Add_StepDependencies - The ``ExternalProject_Get_Property`` function retrieves external project - target properties:: + The ``ExternalProject_Add_StepDependencies()`` function can be used to add + dependencies to a step. The dependencies added must be targets CMake already + knows about (these can be ordinary executable or library targets, custom + targets or even step targets of another external project):: - ExternalProject_Get_Property(<name> [prop1 [prop2 [...]]]) + ExternalProject_Add_StepDependencies(<name> <step> <target1> [<target2>...]) - It stores property values in variables of the same name. Property - names correspond to the keyword argument names of - ``ExternalProject_Add``. + This function takes care to set both target and file level dependencies and + will ensure that parallel builds will not break. It should be used instead of + :command:`add_dependencies` whenever adding a dependency for some of the step + targets generated by the ``ExternalProject`` module. -.. command:: ExternalProject_Add_StepTargets +Examples +^^^^^^^^ - The ``ExternalProject_Add_StepTargets`` function generates custom - targets for the steps listed:: - - ExternalProject_Add_StepTargets(<name> [NO_DEPENDS] [step1 [step2 [...]]]) - -If ``NO_DEPENDS`` is set, the target will not depend on the -dependencies of the complete project. This is usually safe to use for -the download, update, and patch steps that do not require that all the -dependencies are updated and built. Using ``NO_DEPENDS`` for other -of the default steps might break parallel builds, so you should avoid, -it. For custom steps, you should consider whether or not the custom -commands requires that the dependencies are configured, built and -installed. - -If ``STEP_TARGETS`` or ``INDEPENDENT_STEP_TARGETS`` is set then -``ExternalProject_Add_StepTargets`` is automatically called at the end -of matching calls to ``ExternalProject_Add_Step``. Pass -``STEP_TARGETS`` or ``INDEPENDENT_STEP_TARGETS`` explicitly to -individual ``ExternalProject_Add`` calls, or implicitly to all -``ExternalProject_Add`` calls by setting the directory properties -``EP_STEP_TARGETS`` and ``EP_INDEPENDENT_STEP_TARGETS``. The -``INDEPENDENT`` version of the argument and of the property will call -``ExternalProject_Add_StepTargets`` with the ``NO_DEPENDS`` argument. - -If ``STEP_TARGETS`` and ``INDEPENDENT_STEP_TARGETS`` are not set, -clients may still manually call ``ExternalProject_Add_StepTargets`` -after calling ``ExternalProject_Add`` or ``ExternalProject_Add_Step``. - -This functionality is provided to make it easy to drive the steps -independently of each other by specifying targets on build command -lines. For example, you may be submitting to a sub-project based -dashboard, where you want to drive the configure portion of the build, -then submit to the dashboard, followed by the build portion, followed -by tests. If you invoke a custom target that depends on a step -halfway through the step dependency chain, then all the previous steps -will also run to ensure everything is up to date. - -For example, to drive configure, build and test steps independently -for each ``ExternalProject_Add`` call in your project, write the following -line prior to any ``ExternalProject_Add`` calls in your ``CMakeLists.txt`` -file:: - - set_property(DIRECTORY PROPERTY EP_STEP_TARGETS configure build test) +The following example shows how to download and build a hypothetical project +called *FooBar* from github: -.. command:: ExternalProject_Add_StepDependencies +.. code-block:: cmake + + include(ExternalProject) + ExternalProject_Add(foobar + GIT_REPOSITORY git@github.com:FooCo/FooBar.git + GIT_TAG origin/release/1.2.3 + ) + +For the sake of the example, also define a second hypothetical external project +called *SecretSauce*, which is downloaded from a web server. Two URLs are given +to take advantage of a faster internal network if available, with a fallback to +a slower external server. The project is a typical ``Makefile`` project with no +configure step, so some of the default commands are overridden. The build is +only required to build the *sauce* target: + +.. code-block:: cmake + + find_program(MAKE_EXE NAMES gmake nmake make) + ExternalProject_Add(secretsauce + URL http://intranet.somecompany.com/artifacts/sauce-2.7.tgz + https://www.somecompany.com/downloads/sauce-2.7.zip + URL_HASH MD5=d41d8cd98f00b204e9800998ecf8427e + CONFIGURE_COMMAND "" + BUILD_COMMAND ${MAKE_EXE} sauce + ) + +Suppose the build step of ``secretsauce`` requires that ``foobar`` must already +be built. This could be enforced like so: + +.. code-block:: cmake + + ExternalProject_Add_StepDependencies(secretsauce build foobar) + +Another alternative would be to create a custom target for ``foobar``'s build +step and make ``secretsauce`` depend on that rather than the whole ``foobar`` +project. This would mean ``foobar`` only needs to be built, it doesn't need to +run its install or test steps before ``secretsauce`` can be built. The +dependency can also be defined along with the ``secretsauce`` project: + +.. code-block:: cmake + + ExternalProject_Add_StepTargets(foobar build) + ExternalProject_Add(secretsauce + URL http://intranet.somecompany.com/artifacts/sauce-2.7.tgz + https://www.somecompany.com/downloads/sauce-2.7.zip + URL_HASH MD5=d41d8cd98f00b204e9800998ecf8427e + CONFIGURE_COMMAND "" + BUILD_COMMAND ${MAKE_EXE} sauce + DEPENDS foobar-build + ) + +Instead of calling :command:`ExternalProject_Add_StepTargets`, the target could +be defined along with the ``foobar`` project itself: + +.. code-block:: cmake + + ExternalProject_Add(foobar + GIT_REPOSITORY git@github.com:FooCo/FooBar.git + GIT_TAG origin/release/1.2.3 + STEP_TARGETS build + ) + +If many external projects should have the same set of step targets, setting a +directory property may be more convenient. The ``build`` step target could be +created automatically by setting the ``EP_STEP_TARGETS`` directory property +before creating the external projects with :command:`ExternalProject_Add`: + +.. code-block:: cmake + + set_property(DIRECTORY PROPERTY EP_STEP_TARGETS build) + +Lastly, suppose that ``secretsauce`` provides a script called ``makedoc`` which +can be used to generate its own documentation. Further suppose that the script +expects the output directory to be provided as the only parameter and that it +should be run from the ``secretsauce`` source directory. A custom step and a +custom target to trigger the script can be defined like so: + +.. code-block:: cmake + + ExternalProject_Add_Step(secretsauce docs + COMMAND <SOURCE_DIR>/makedoc <BINARY_DIR> + WORKING_DIRECTORY <SOURCE_DIR> + COMMENT "Building secretsauce docs" + ALWAYS TRUE + EXCLUDE_FROM_MAIN TRUE + ) + ExternalProject_Add_StepTargets(secretsauce docs) - The ``ExternalProject_Add_StepDependencies`` function add some - dependencies for some external project step:: +The custom step could then be triggered from the main build like so:: - ExternalProject_Add_StepDependencies(<name> <step> [target1 [target2 [...]]]) + cmake --build . --target secretsauce-docs - This function takes care to set both target and file level - dependencies, and will ensure that parallel builds will not break. - It should be used instead of :command:`add_dependencies()` when adding - a dependency for some of the step targets generated by - ``ExternalProject``. #]=======================================================================] # Pre-compute a regex to match documented keywords for each command. -math(EXPR _ep_documentation_line_count "${CMAKE_CURRENT_LIST_LINE} - 16") +math(EXPR _ep_documentation_line_count "${CMAKE_CURRENT_LIST_LINE} - 4") file(STRINGS "${CMAKE_CURRENT_LIST_FILE}" lines LIMIT_COUNT ${_ep_documentation_line_count} - REGEX "^\\.\\. command:: [A-Za-z0-9_]+|^ ``[A-Z0-9_]+ .*``$") + REGEX "^\\.\\. command:: [A-Za-z0-9_]+|^ +``[A-Z0-9_]+ [^`]*``$") foreach(line IN LISTS lines) if("${line}" MATCHES "^\\.\\. command:: ([A-Za-z0-9_]+)") if(_ep_func) @@ -421,7 +868,7 @@ foreach(line IN LISTS lines) #message("function [${_ep_func}]") set(_ep_keywords_${_ep_func} "^(") set(_ep_keyword_sep) - elseif("${line}" MATCHES "^ ``([A-Z0-9_]+) .*``$") + elseif("${line}" MATCHES "^ +``([A-Z0-9_]+) [^`]*``$") set(_ep_key "${CMAKE_MATCH_1}") #message(" keyword [${_ep_key}]") string(APPEND _ep_keywords_${_ep_func} @@ -504,7 +951,7 @@ define_property(DIRECTORY PROPERTY "EP_STEP_TARGETS" INHERITED BRIEF_DOCS "List of ExternalProject steps that automatically get corresponding targets" FULL_DOCS - "These targets will be dependent on the main target dependencies" + "These targets will be dependent on the main target dependencies. " "See documentation of the ExternalProject_Add_StepTargets() function in the " "ExternalProject module." ) @@ -513,7 +960,7 @@ define_property(DIRECTORY PROPERTY "EP_INDEPENDENT_STEP_TARGETS" INHERITED BRIEF_DOCS "List of ExternalProject steps that automatically get corresponding targets" FULL_DOCS - "These targets will not be dependent on the main target dependencies" + "These targets will not be dependent on the main target dependencies. " "See documentation of the ExternalProject_Add_StepTargets() function in the " "ExternalProject module." ) @@ -2012,12 +2459,12 @@ function(_ep_add_download_command name) " ${source_dir}\n" "is not an existing non-empty directory. Please specify one of:\n" " * SOURCE_DIR with an existing non-empty directory\n" + " * DOWNLOAD_COMMAND\n" " * URL\n" " * GIT_REPOSITORY\n" + " * SVN_REPOSITORY\n" " * HG_REPOSITORY\n" - " * CVS_REPOSITORY and CVS_MODULE\n" - " * SVN_REVISION\n" - " * DOWNLOAD_COMMAND" + " * CVS_REPOSITORY and CVS_MODULE" ) endif() endif() diff --git a/Modules/Platform/GHS-MULTI-Initialize.cmake b/Modules/Platform/GHS-MULTI-Initialize.cmake index fcda6f6..bf61d7b 100644 --- a/Modules/Platform/GHS-MULTI-Initialize.cmake +++ b/Modules/Platform/GHS-MULTI-Initialize.cmake @@ -10,7 +10,7 @@ if (NOT GHS_INT_DIRECTORY) if (EXISTS ${GHS_EXPECTED_ROOT}) FILE(GLOB GHS_CANDIDATE_INT_DIRS RELATIVE ${GHS_EXPECTED_ROOT} ${GHS_EXPECTED_ROOT}/*) - string(REGEX MATCHALL "int[0-9][0-9][0-9][0-9]" GHS_CANDIDATE_INT_DIRS + string(REGEX MATCHALL "int[0-9][0-9][0-9][0-9a-z]" GHS_CANDIDATE_INT_DIRS ${GHS_CANDIDATE_INT_DIRS}) if (GHS_CANDIDATE_INT_DIRS) list(SORT GHS_CANDIDATE_INT_DIRS) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index e556ca6..d35b7fb 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -1012,7 +1012,9 @@ target_link_libraries(cmake CMakeLib) if(CMake_ENABLE_SERVER_MODE) add_library(CMakeServerLib + cmConnection.h cmConnection.cxx cmFileMonitor.cxx cmFileMonitor.h + cmPipeConnection.cxx cmPipeConnection.h cmServer.cxx cmServer.h cmServerConnection.cxx cmServerConnection.h cmServerProtocol.cxx cmServerProtocol.h diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 7a10ae1..33c363b 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 9) -set(CMake_VERSION_PATCH 20170717) +set(CMake_VERSION_PATCH 20170721) #set(CMake_VERSION_RC 1) diff --git a/Source/CPack/cmCPackDebGenerator.cxx b/Source/CPack/cmCPackDebGenerator.cxx index 00d017e..0705460 100644 --- a/Source/CPack/cmCPackDebGenerator.cxx +++ b/Source/CPack/cmCPackDebGenerator.cxx @@ -6,6 +6,7 @@ #include "cmCPackComponentGroup.h" #include "cmCPackGenerator.h" #include "cmCPackLog.h" +#include "cmCryptoHash.h" #include "cmGeneratedFileStream.h" #include "cmSystemTools.h" #include "cm_sys_stat.h" @@ -527,15 +528,13 @@ int cmCPackDebGenerator::createDeb() continue; } - char md5sum[33]; - if (!cmSystemTools::ComputeFileMD5(*fileIt, md5sum)) { + std::string output = + cmSystemTools::ComputeFileHash(*fileIt, cmCryptoHash::AlgoMD5); + if (output.empty()) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem computing the md5 of " << *fileIt << std::endl); } - md5sum[32] = 0; - - std::string output(md5sum); output += " " + *fileIt + "\n"; // debian md5sums entries are like this: // 014f3604694729f3bf19263bac599765 usr/bin/ccmake diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx index 8d62fa1..689668d 100644 --- a/Source/CTest/cmCTestSubmitHandler.cxx +++ b/Source/CTest/cmCTestSubmitHandler.cxx @@ -13,6 +13,7 @@ #include "cmCTest.h" #include "cmCTestCurl.h" #include "cmCTestScriptHandler.h" +#include "cmCryptoHash.h" #include "cmCurl.h" #include "cmGeneratedFileStream.h" #include "cmProcessOutput.h" @@ -428,10 +429,8 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const std::string& localprefix, if (cmSystemTools::IsOn(this->GetOption("InternalTest"))) { upload_as += "bad_md5sum"; } else { - char md5[33]; - cmSystemTools::ComputeFileMD5(local_file, md5); - md5[32] = 0; - upload_as += md5; + upload_as += + cmSystemTools::ComputeFileHash(local_file, cmCryptoHash::AlgoMD5); } if (!cmSystemTools::FileExists(local_file.c_str())) { @@ -1058,9 +1057,8 @@ int cmCTestSubmitHandler::HandleCDashUploadFile(std::string const& file, } } - char md5sum[33]; - md5sum[32] = 0; - cmSystemTools::ComputeFileMD5(file, md5sum); + std::string md5sum = + cmSystemTools::ComputeFileHash(file, cmCryptoHash::AlgoMD5); // 1. request the buildid and check to see if the file // has already been uploaded // TODO I added support for subproject. You would need to add diff --git a/Source/cmConnection.cxx b/Source/cmConnection.cxx new file mode 100644 index 0000000..00cf283 --- /dev/null +++ b/Source/cmConnection.cxx @@ -0,0 +1,158 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmConnection.h" + +#include "cmServer.h" +#include "cm_uv.h" + +#include <cassert> +#include <cstring> + +struct write_req_t +{ + uv_write_t req; + uv_buf_t buf; +}; + +void cmEventBasedConnection::on_alloc_buffer(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) +{ + (void)(handle); + char* rawBuffer = new char[suggested_size]; + *buf = uv_buf_init(rawBuffer, static_cast<unsigned int>(suggested_size)); +} + +void cmEventBasedConnection::on_read(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf) +{ + auto conn = reinterpret_cast<cmEventBasedConnection*>(stream->data); + if (conn) { + if (nread >= 0) { + conn->ReadData(std::string(buf->base, buf->base + nread)); + } else { + conn->OnDisconnect((int)nread); + } + } + + delete[](buf->base); +} + +void cmEventBasedConnection::on_close_delete(uv_handle_t* handle) +{ + delete handle; +} + +void cmEventBasedConnection::on_close(uv_handle_t* /*handle*/) +{ +} + +void cmEventBasedConnection::on_write(uv_write_t* req, int status) +{ + (void)(status); + + // Free req and buffer + write_req_t* wr = reinterpret_cast<write_req_t*>(req); + delete[](wr->buf.base); + delete wr; +} + +void cmEventBasedConnection::on_new_connection(uv_stream_t* stream, int status) +{ + (void)(status); + auto conn = reinterpret_cast<cmEventBasedConnection*>(stream->data); + + if (conn) { + conn->Connect(stream); + } +} + +bool cmEventBasedConnection::IsOpen() const +{ + return this->WriteStream != CM_NULLPTR; +} + +void cmEventBasedConnection::WriteData(const std::string& data) +{ + assert(this->WriteStream); + + auto ds = data.size(); + + write_req_t* req = new write_req_t; + req->req.data = this; + req->buf = uv_buf_init(new char[ds], static_cast<unsigned int>(ds)); + memcpy(req->buf.base, data.c_str(), ds); + uv_write(reinterpret_cast<uv_write_t*>(req), + static_cast<uv_stream_t*>(this->WriteStream), &req->buf, 1, + on_write); +} + +void cmEventBasedConnection::ReadData(const std::string& data) +{ + this->RawReadBuffer += data; + if (BufferStrategy) { + std::string packet = BufferStrategy->BufferMessage(this->RawReadBuffer); + do { + ProcessRequest(packet); + packet = BufferStrategy->BufferMessage(this->RawReadBuffer); + } while (!packet.empty()); + + } else { + ProcessRequest(this->RawReadBuffer); + this->RawReadBuffer.clear(); + } +} + +cmEventBasedConnection::cmEventBasedConnection( + cmConnectionBufferStrategy* bufferStrategy) + : BufferStrategy(bufferStrategy) +{ +} + +void cmEventBasedConnection::Connect(uv_stream_t* server) +{ + (void)server; + Server->OnConnected(nullptr); +} + +void cmEventBasedConnection::OnDisconnect(int onerror) +{ + (void)onerror; + this->OnConnectionShuttingDown(); + this->Server->OnDisconnect(this); +} + +cmConnection::~cmConnection() +{ +} + +bool cmConnection::OnConnectionShuttingDown() +{ + return true; +} + +void cmConnection::SetServer(cmServerBase* s) +{ + Server = s; +} + +void cmConnection::ProcessRequest(const std::string& request) +{ + Server->ProcessRequest(this, request); +} + +bool cmConnection::OnServeStart(std::string* errString) +{ + (void)errString; + return true; +} + +bool cmEventBasedConnection::OnConnectionShuttingDown() +{ + this->WriteStream->data = nullptr; + this->ReadStream->data = nullptr; + + this->ReadStream = nullptr; + this->WriteStream = nullptr; + return true; +} diff --git a/Source/cmConnection.h b/Source/cmConnection.h new file mode 100644 index 0000000..4b8fcb3 --- /dev/null +++ b/Source/cmConnection.h @@ -0,0 +1,118 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#pragma once + +#include "cmConfigure.h" + +#include "cm_uv.h" + +#include <cstddef> +#include <memory> +#include <string> + +class cmServerBase; + +/*** + * Given a sequence of bytes with any kind of buffering, instances of this + * class arrange logical chunks according to whatever the use case is for + * the connection. + */ +class cmConnectionBufferStrategy +{ +public: + virtual ~cmConnectionBufferStrategy(); + + /*** + * Called whenever with an active raw buffer. If a logical chunk + * becomes available, that chunk is returned and that portion is + * removed from the rawBuffer + * + * @param rawBuffer in/out parameter. Receive buffer; the buffer strategy is + * free to manipulate this buffer anyway it needs to. + * + * @return Next chunk from the stream. Returns the empty string if a chunk + * isn't ready yet. Users of this interface should repeatedly call this + * function until an empty string is returned since its entirely possible + * multiple chunks come in a single raw buffer. + */ + virtual std::string BufferMessage(std::string& rawBuffer) = 0; + + /*** + * Resets the internal state of the buffering + */ + virtual void clear(); + + // TODO: There should be a callback / flag set for errors +}; + +class cmConnection +{ + CM_DISABLE_COPY(cmConnection) + +public: + cmConnection() {} + + virtual void WriteData(const std::string& data) = 0; + + virtual ~cmConnection(); + + virtual bool OnConnectionShuttingDown(); + + virtual bool IsOpen() const = 0; + + virtual void SetServer(cmServerBase* s); + + virtual void ProcessRequest(const std::string& request); + + virtual bool OnServeStart(std::string* pString); + +protected: + cmServerBase* Server = nullptr; +}; + +/*** + * Abstraction of a connection; ties in event callbacks from libuv and notifies + * the server when appropriate + */ +class cmEventBasedConnection : public cmConnection +{ + +public: + /*** + * @param bufferStrategy If no strategy is given, it will process the raw + * chunks as they come in. The connection + * owns the pointer given. + */ + cmEventBasedConnection(cmConnectionBufferStrategy* bufferStrategy = nullptr); + + virtual void Connect(uv_stream_t* server); + + virtual void ReadData(const std::string& data); + + bool IsOpen() const override; + + void WriteData(const std::string& data) override; + bool OnConnectionShuttingDown() override; + + virtual void OnDisconnect(int errorCode); + uv_stream_t* ReadStream = nullptr; + uv_stream_t* WriteStream = nullptr; + + static void on_close(uv_handle_t* handle); + static void on_close_delete(uv_handle_t* handle); + +protected: + std::string RawReadBuffer; + + std::unique_ptr<cmConnectionBufferStrategy> BufferStrategy; + + static void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf); + + static void on_write(uv_write_t* req, int status); + + static void on_new_connection(uv_stream_t* stream, int status); + + static void on_alloc_buffer(uv_handle_t* handle, size_t suggested_size, + uv_buf_t* buf); +}; diff --git a/Source/cmFileMonitor.cxx b/Source/cmFileMonitor.cxx index 8027535..c0401d7 100644 --- a/Source/cmFileMonitor.cxx +++ b/Source/cmFileMonitor.cxx @@ -171,7 +171,9 @@ public: { if (this->Handle) { uv_fs_event_stop(this->Handle); - uv_close(reinterpret_cast<uv_handle_t*>(this->Handle), &on_fs_close); + if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(this->Handle))) { + uv_close(reinterpret_cast<uv_handle_t*>(this->Handle), &on_fs_close); + } this->Handle = nullptr; } cmVirtualDirectoryWatcher::StopWatching(); diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 1802da4..329c7a9 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -1025,12 +1025,23 @@ cmGeneratorTarget::KindedSources const& cmGeneratorTarget::GetKindedSources( std::string const key = cmSystemTools::UpperCase(config); KindedSourcesMapType::iterator it = this->KindedSourcesMap.find(key); if (it != this->KindedSourcesMap.end()) { + if (!it->second.Initialized) { + std::ostringstream e; + e << "The SOURCES of \"" << this->GetName() + << "\" use a generator expression that depends on the " + "SOURCES themselves."; + this->GlobalGenerator->GetCMakeInstance()->IssueMessage( + cmake::FATAL_ERROR, e.str(), this->GetBacktrace()); + static KindedSources empty; + return empty; + } return it->second; } // Add an entry to the map for this configuration. KindedSources& files = this->KindedSourcesMap[key]; this->ComputeKindedSources(files, config); + files.Initialized = true; return files; } diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index c04d62b..52147e3 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -107,6 +107,11 @@ public: std::set<std::string> ExpectedResxHeaders; std::set<std::string> ExpectedXamlHeaders; std::set<std::string> ExpectedXamlSources; + bool Initialized; + KindedSources() + : Initialized(false) + { + } }; /** Get all sources needed for a configuration with kinds assigned. */ diff --git a/Source/cmPipeConnection.cxx b/Source/cmPipeConnection.cxx new file mode 100644 index 0000000..cc82438 --- /dev/null +++ b/Source/cmPipeConnection.cxx @@ -0,0 +1,80 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmPipeConnection.h" + +#include "cmConfigure.h" +#include "cmServer.h" + +cmPipeConnection::cmPipeConnection(const std::string& name, + cmConnectionBufferStrategy* bufferStrategy) + : cmEventBasedConnection(bufferStrategy) + , PipeName(name) +{ +} + +void cmPipeConnection::Connect(uv_stream_t* server) +{ + if (this->ClientPipe) { + // Accept and close all pipes but the first: + uv_pipe_t* rejectPipe = new uv_pipe_t(); + + uv_pipe_init(this->Server->GetLoop(), rejectPipe, 0); + uv_accept(server, reinterpret_cast<uv_stream_t*>(rejectPipe)); + uv_close(reinterpret_cast<uv_handle_t*>(rejectPipe), &on_close_delete); + return; + } + + this->ClientPipe = new uv_pipe_t(); + uv_pipe_init(this->Server->GetLoop(), this->ClientPipe, 0); + this->ClientPipe->data = static_cast<cmEventBasedConnection*>(this); + auto client = reinterpret_cast<uv_stream_t*>(this->ClientPipe); + if (uv_accept(server, client) != 0) { + uv_close(reinterpret_cast<uv_handle_t*>(client), &on_close_delete); + this->ClientPipe = CM_NULLPTR; + return; + } + this->ReadStream = client; + this->WriteStream = client; + + uv_read_start(this->ReadStream, on_alloc_buffer, on_read); + Server->OnConnected(this); +} + +bool cmPipeConnection::OnServeStart(std::string* errorMessage) +{ + this->ServerPipe = new uv_pipe_t(); + uv_pipe_init(this->Server->GetLoop(), this->ServerPipe, 0); + this->ServerPipe->data = static_cast<cmEventBasedConnection*>(this); + + int r; + if ((r = uv_pipe_bind(this->ServerPipe, this->PipeName.c_str())) != 0) { + *errorMessage = std::string("Internal Error with ") + this->PipeName + + ": " + uv_err_name(r); + return false; + } + auto serverStream = reinterpret_cast<uv_stream_t*>(this->ServerPipe); + if ((r = uv_listen(serverStream, 1, on_new_connection)) != 0) { + *errorMessage = std::string("Internal Error listening on ") + + this->PipeName + ": " + uv_err_name(r); + return false; + } + + return cmConnection::OnServeStart(errorMessage); +} + +bool cmPipeConnection::OnConnectionShuttingDown() +{ + if (this->ClientPipe) { + uv_close(reinterpret_cast<uv_handle_t*>(this->ClientPipe), + &on_close_delete); + this->WriteStream->data = nullptr; + } + uv_close(reinterpret_cast<uv_handle_t*>(this->ServerPipe), &on_close_delete); + + this->ClientPipe = nullptr; + this->ServerPipe = nullptr; + this->WriteStream = nullptr; + this->ReadStream = nullptr; + + return cmConnection::OnConnectionShuttingDown(); +} diff --git a/Source/cmPipeConnection.h b/Source/cmPipeConnection.h new file mode 100644 index 0000000..fea85b5 --- /dev/null +++ b/Source/cmPipeConnection.h @@ -0,0 +1,28 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#pragma once + +#include "cmConnection.h" + +#include "cm_uv.h" + +#include <string> + +class cmPipeConnection : public cmEventBasedConnection +{ +public: + cmPipeConnection(const std::string& name, + cmConnectionBufferStrategy* bufferStrategy = nullptr); + + bool OnServeStart(std::string* pString) override; + + bool OnConnectionShuttingDown() override; + + void Connect(uv_stream_t* server) override; + +private: + const std::string PipeName; + uv_pipe_t* ServerPipe = nullptr; + uv_pipe_t* ClientPipe = nullptr; +}; diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index 0a0178c..354011a 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -209,7 +209,9 @@ class cmMakefile; cmPolicies::WARN) \ SELECT(POLICY, CMP0070, \ "Define file(GENERATE) behavior for relative paths.", 3, 10, 0, \ - cmPolicies::WARN) + cmPolicies::WARN) \ + SELECT(POLICY, CMP0071, "Let AUTOMOC and AUTOUIC process GENERATED files.", \ + 3, 10, 0, cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ diff --git a/Source/cmQtAutoGeneratorInitializer.cxx b/Source/cmQtAutoGeneratorInitializer.cxx index cecf165..92fa1bd 100644 --- a/Source/cmQtAutoGeneratorInitializer.cxx +++ b/Source/cmQtAutoGeneratorInitializer.cxx @@ -11,6 +11,7 @@ #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmOutputConverter.h" +#include "cmPolicies.h" #include "cmSourceFile.h" #include "cmSourceGroup.h" #include "cmState.h" @@ -28,6 +29,7 @@ #include <algorithm> #include <map> #include <set> +#include <sstream> #include <string> #include <utility> #include <vector> @@ -290,6 +292,8 @@ static void AcquireScanFiles(cmGeneratorTarget const* target, { const bool mocTarget = target->GetPropertyAsBool("AUTOMOC"); const bool uicTarget = target->GetPropertyAsBool("AUTOUIC"); + const cmPolicies::PolicyStatus CMP0071_status = + target->Makefile->GetPolicyStatus(cmPolicies::CMP0071); std::vector<cmSourceFile*> srcFiles; target->GetConfigCommonSourceFiles(srcFiles); @@ -298,24 +302,46 @@ static void AcquireScanFiles(cmGeneratorTarget const* target, cmSourceFile* sf = *fileIt; const cmSystemTools::FileFormat fileType = cmSystemTools::GetFileFormat(sf->GetExtension().c_str()); - if (!(fileType == cmSystemTools::CXX_FILE_FORMAT) && !(fileType == cmSystemTools::HEADER_FILE_FORMAT)) { continue; } - if (PropertyEnabled(sf, "GENERATED") && - !target->GetPropertyAsBool("__UNDOCUMENTED_AUTOGEN_GENERATED_FILES")) { - // FIXME: Add a policy whose NEW behavior allows generated files. - // The implementation already works. We disable it here to avoid - // changing behavior for existing projects that do not expect it. - continue; - } + const std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath()); // Skip flags const bool skipAll = PropertyEnabled(sf, "SKIP_AUTOGEN"); const bool mocSkip = skipAll || PropertyEnabled(sf, "SKIP_AUTOMOC"); const bool uicSkip = skipAll || PropertyEnabled(sf, "SKIP_AUTOUIC"); + const bool accept = (mocTarget && !mocSkip) || (uicTarget && !uicSkip); + + // For GENERATED files check status of policy CMP0071 + if (accept && PropertyEnabled(sf, "GENERATED")) { + bool policyAccept = false; + switch (CMP0071_status) { + case cmPolicies::WARN: { + std::ostringstream ost; + ost << cmPolicies::GetPolicyWarning(cmPolicies::CMP0071) << "\n"; + ost << "AUTOMOC/AUTOUIC: Ignoring GENERATED source file:\n"; + ost << " " << cmQtAutoGeneratorCommon::Quoted(absFile) << "\n"; + target->Makefile->IssueMessage(cmake::AUTHOR_WARNING, ost.str()); + } + CM_FALLTHROUGH; + case cmPolicies::OLD: + // Ignore GENERATED file + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + // Process GENERATED file + policyAccept = true; + break; + } + if (!policyAccept) { + continue; + } + } + // Add file name to skip lists. // Do this even when the file is not added to the sources/headers lists // because the file name may be extracted from an other file when @@ -327,7 +353,7 @@ static void AcquireScanFiles(cmGeneratorTarget const* target, uicSkipList.push_back(absFile); } - if ((mocTarget && !mocSkip) || (uicTarget && !uicSkip)) { + if (accept) { // Add file name to sources or headers list switch (fileType) { case cmSystemTools::CXX_FILE_FORMAT: diff --git a/Source/cmServer.cxx b/Source/cmServer.cxx index 5283dfc..ccb9af6 100644 --- a/Source/cmServer.cxx +++ b/Source/cmServer.cxx @@ -2,7 +2,8 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmServer.h" -#include "cmServerConnection.h" +#include "cmConnection.h" +#include "cmFileMonitor.h" #include "cmServerDictionary.h" #include "cmServerProtocol.h" #include "cmSystemTools.h" @@ -14,8 +15,23 @@ #include <algorithm> #include <cassert> #include <cstdint> +#include <memory> #include <utility> +void on_signal(uv_signal_t* signal, int signum) +{ + auto conn = reinterpret_cast<cmServerBase*>(signal->data); + conn->OnSignal(signum); +} + +static void on_walk_to_shutdown(uv_handle_t* handle, void* arg) +{ + (void)arg; + if (!uv_is_closing(handle)) { + uv_close(handle, &cmEventBasedConnection::on_close); + } +} + class cmServer::DebugInfo { public: @@ -30,11 +46,10 @@ public: uint64_t StartTime; }; -cmServer::cmServer(cmServerConnection* conn, bool supportExperimental) - : Connection(conn) +cmServer::cmServer(cmConnection* conn, bool supportExperimental) + : cmServerBase(conn) , SupportExperimental(supportExperimental) { - this->Connection->SetServer(this); // Register supported protocols: this->RegisterProtocol(new cmServerProtocol1); } @@ -48,23 +63,15 @@ cmServer::~cmServer() for (cmServerProtocol* p : this->SupportedProtocols) { delete p; } - - delete this->Connection; } -void cmServer::PopOne() +void cmServer::ProcessRequest(cmConnection* connection, + const std::string& input) { - if (this->Queue.empty()) { - return; - } - Json::Reader reader; Json::Value value; - const std::string input = this->Queue.front(); - this->Queue.erase(this->Queue.begin()); - if (!reader.parse(input, value)) { - this->WriteParseError("Failed to parse JSON input."); + this->WriteParseError(connection, "Failed to parse JSON input."); return; } @@ -76,13 +83,13 @@ void cmServer::PopOne() debug->PrintStatistics = debugValue["showStats"].asBool(); } - const cmServerRequest request(this, value[kTYPE_KEY].asString(), + const cmServerRequest request(this, connection, value[kTYPE_KEY].asString(), value[kCOOKIE_KEY].asString(), value); if (request.Type == "") { cmServerResponse response(request); response.SetError("No type given in request."); - this->WriteResponse(response, nullptr); + this->WriteResponse(connection, response, nullptr); return; } @@ -91,9 +98,11 @@ void cmServer::PopOne() if (this->Protocol) { this->Protocol->CMakeInstance()->SetProgressCallback( reportProgress, const_cast<cmServerRequest*>(&request)); - this->WriteResponse(this->Protocol->Process(request), debug.get()); + this->WriteResponse(connection, this->Protocol->Process(request), + debug.get()); } else { - this->WriteResponse(this->SetProtocolVersion(request), debug.get()); + this->WriteResponse(connection, this->SetProtocolVersion(request), + debug.get()); } } @@ -115,7 +124,7 @@ void cmServer::RegisterProtocol(cmServerProtocol* protocol) } } -void cmServer::PrintHello() const +void cmServer::PrintHello(cmConnection* connection) const { Json::Value hello = Json::objectValue; hello[kTYPE_KEY] = "hello"; @@ -134,13 +143,7 @@ void cmServer::PrintHello() const protocolVersions.append(tmp); } - this->WriteJsonObject(hello, nullptr); -} - -void cmServer::QueueRequest(const std::string& request) -{ - this->Queue.push_back(request); - this->PopOne(); + this->WriteJsonObject(connection, hello, nullptr); } void cmServer::reportProgress(const char* msg, float progress, void* data) @@ -232,17 +235,26 @@ bool cmServer::Serve(std::string* errorMessage) } assert(!this->Protocol); - return Connection->ProcessEvents(errorMessage); + return cmServerBase::Serve(errorMessage); } cmFileMonitor* cmServer::FileMonitor() const { - return Connection->FileMonitor(); + return fileMonitor.get(); } void cmServer::WriteJsonObject(const Json::Value& jsonValue, const DebugInfo* debug) const { + for (auto& connection : this->Connections) { + WriteJsonObject(connection.get(), jsonValue, debug); + } +} + +void cmServer::WriteJsonObject(cmConnection* connection, + const Json::Value& jsonValue, + const DebugInfo* debug) const +{ Json::FastWriter writer; auto beforeJson = uv_hrtime(); @@ -272,7 +284,7 @@ void cmServer::WriteJsonObject(const Json::Value& jsonValue, } } - Connection->WriteData(std::string("\n") + kSTART_MAGIC + std::string("\n") + + connection->WriteData(std::string("\n") + kSTART_MAGIC + std::string("\n") + result + kEND_MAGIC + std::string("\n")); } @@ -311,7 +323,7 @@ void cmServer::WriteProgress(const cmServerRequest& request, int min, obj[kPROGRESS_MAXIMUM_KEY] = max; obj[kPROGRESS_CURRENT_KEY] = current; - this->WriteJsonObject(obj, nullptr); + this->WriteJsonObject(request.Connection, obj, nullptr); } void cmServer::WriteMessage(const cmServerRequest& request, @@ -331,10 +343,11 @@ void cmServer::WriteMessage(const cmServerRequest& request, obj[kTITLE_KEY] = title; } - WriteJsonObject(obj, nullptr); + WriteJsonObject(request.Connection, obj, nullptr); } -void cmServer::WriteParseError(const std::string& message) const +void cmServer::WriteParseError(cmConnection* connection, + const std::string& message) const { Json::Value obj = Json::objectValue; obj[kTYPE_KEY] = kERROR_TYPE; @@ -342,7 +355,7 @@ void cmServer::WriteParseError(const std::string& message) const obj[kREPLY_TO_KEY] = ""; obj[kCOOKIE_KEY] = ""; - this->WriteJsonObject(obj, nullptr); + this->WriteJsonObject(connection, obj, nullptr); } void cmServer::WriteSignal(const std::string& name, @@ -358,7 +371,8 @@ void cmServer::WriteSignal(const std::string& name, WriteJsonObject(obj, nullptr); } -void cmServer::WriteResponse(const cmServerResponse& response, +void cmServer::WriteResponse(cmConnection* connection, + const cmServerResponse& response, const DebugInfo* debug) const { assert(response.IsComplete()); @@ -371,5 +385,161 @@ void cmServer::WriteResponse(const cmServerResponse& response, obj[kERROR_MESSAGE_KEY] = response.ErrorMessage(); } - this->WriteJsonObject(obj, debug); + this->WriteJsonObject(connection, obj, debug); +} + +void cmServer::OnConnected(cmConnection* connection) +{ + PrintHello(connection); +} + +void cmServer::OnServeStart() +{ + cmServerBase::OnServeStart(); + fileMonitor = std::make_shared<cmFileMonitor>(GetLoop()); +} + +void cmServer::StartShutDown() +{ + if (fileMonitor) { + fileMonitor->StopMonitoring(); + fileMonitor.reset(); + } + cmServerBase::StartShutDown(); +} + +static void __start_thread(void* arg) +{ + auto server = reinterpret_cast<cmServerBase*>(arg); + std::string error; + server->Serve(&error); +} + +bool cmServerBase::StartServeThread() +{ + ServeThreadRunning = true; + uv_thread_create(&ServeThread, __start_thread, this); + return true; +} + +bool cmServerBase::Serve(std::string* errorMessage) +{ + errorMessage->clear(); + + uv_signal_init(&Loop, &this->SIGINTHandler); + uv_signal_init(&Loop, &this->SIGHUPHandler); + + this->SIGINTHandler.data = this; + this->SIGHUPHandler.data = this; + + uv_signal_start(&this->SIGINTHandler, &on_signal, SIGINT); + uv_signal_start(&this->SIGHUPHandler, &on_signal, SIGHUP); + + OnServeStart(); + + for (auto& connection : Connections) { + if (!connection->OnServeStart(errorMessage)) { + return false; + } + } + + if (uv_run(&Loop, UV_RUN_DEFAULT) != 0) { + *errorMessage = "Internal Error: Event loop stopped in unclean state."; + StartShutDown(); + return false; + } + + ServeThreadRunning = false; + return true; +} + +void cmServerBase::OnConnected(cmConnection*) +{ +} + +void cmServerBase::OnDisconnect() +{ +} + +void cmServerBase::OnServeStart() +{ + uv_signal_start(&this->SIGINTHandler, &on_signal, SIGINT); + uv_signal_start(&this->SIGHUPHandler, &on_signal, SIGHUP); +} + +void cmServerBase::StartShutDown() +{ + if (!uv_is_closing((const uv_handle_t*)&this->SIGINTHandler)) { + uv_signal_stop(&this->SIGINTHandler); + } + + if (!uv_is_closing((const uv_handle_t*)&this->SIGHUPHandler)) { + uv_signal_stop(&this->SIGHUPHandler); + } + + for (auto& connection : Connections) { + connection->OnConnectionShuttingDown(); + } + Connections.clear(); + + uv_stop(&Loop); + + uv_walk(&Loop, on_walk_to_shutdown, CM_NULLPTR); + + uv_run(&Loop, UV_RUN_DEFAULT); +} + +bool cmServerBase::OnSignal(int signum) +{ + (void)signum; + StartShutDown(); + return true; +} + +cmServerBase::cmServerBase(cmConnection* connection) +{ + uv_loop_init(&Loop); + + uv_signal_init(&Loop, &this->SIGINTHandler); + uv_signal_init(&Loop, &this->SIGHUPHandler); + + this->SIGINTHandler.data = this; + this->SIGHUPHandler.data = this; + + AddNewConnection(connection); +} + +cmServerBase::~cmServerBase() +{ + + if (ServeThreadRunning) { + StartShutDown(); + uv_thread_join(&ServeThread); + } + + uv_loop_close(&Loop); +} + +void cmServerBase::AddNewConnection(cmConnection* ownedConnection) +{ + Connections.emplace_back(ownedConnection); + ownedConnection->SetServer(this); +} + +uv_loop_t* cmServerBase::GetLoop() +{ + return &Loop; +} + +void cmServerBase::OnDisconnect(cmConnection* pConnection) +{ + auto pred = [pConnection](const std::unique_ptr<cmConnection>& m) { + return m.get() == pConnection; + }; + Connections.erase( + std::remove_if(Connections.begin(), Connections.end(), pred), + Connections.end()); + if (Connections.empty()) { + StartShutDown(); + } } diff --git a/Source/cmServer.h b/Source/cmServer.h index b814050..0000704 100644 --- a/Source/cmServer.h +++ b/Source/cmServer.h @@ -7,26 +7,83 @@ #include "cm_jsoncpp_value.h" #include "cm_uv.h" +#include <memory> // IWYU pragma: keep #include <string> #include <vector> +class cmConnection; class cmFileMonitor; -class cmServerConnection; class cmServerProtocol; class cmServerRequest; class cmServerResponse; -class cmServer +/*** + * This essentially hold and manages a libuv event queue and responds to + * messages + * on any of its connections. + */ +class cmServerBase +{ +public: + cmServerBase(cmConnection* connection); + virtual ~cmServerBase(); + + virtual void AddNewConnection(cmConnection* ownedConnection); + + /*** + * The main override responsible for tailoring behavior towards + * whatever the given server is supposed to do + * + * This should almost always be called by the given connections + * directly. + * + * @param connection The connectiont the request was received on + * @param request The actual request + */ + virtual void ProcessRequest(cmConnection* connection, + const std::string& request) = 0; + virtual void OnConnected(cmConnection* connection); + virtual void OnDisconnect(); + + /*** + * Start a dedicated thread. If this is used to start the server, it will + * join on the + * servers dtor. + */ + virtual bool StartServeThread(); + virtual bool Serve(std::string* errorMessage); + + virtual void OnServeStart(); + virtual void StartShutDown(); + + virtual bool OnSignal(int signum); + uv_loop_t* GetLoop(); + + void OnDisconnect(cmConnection* pConnection); + +protected: + std::vector<std::unique_ptr<cmConnection> > Connections; + + bool ServeThreadRunning = false; + uv_thread_t ServeThread; + + uv_loop_t Loop; + + uv_signal_t SIGINTHandler; + uv_signal_t SIGHUPHandler; +}; + +class cmServer : public cmServerBase { CM_DISABLE_COPY(cmServer) public: class DebugInfo; - cmServer(cmServerConnection* conn, bool supportExperimental); - ~cmServer(); + cmServer(cmConnection* conn, bool supportExperimental); + ~cmServer() override; - bool Serve(std::string* errorMessage); + bool Serve(std::string* errorMessage) override; cmFileMonitor* FileMonitor() const; @@ -34,9 +91,20 @@ private: void RegisterProtocol(cmServerProtocol* protocol); // Callbacks from cmServerConnection: - void PopOne(); - void QueueRequest(const std::string& request); + void ProcessRequest(cmConnection* connection, + const std::string& request) override; + std::shared_ptr<cmFileMonitor> fileMonitor; + +public: + void OnServeStart() override; + + void StartShutDown() override; + +public: + void OnConnected(cmConnection* connection) override; + +private: static void reportProgress(const char* msg, float progress, void* data); static void reportMessage(const char* msg, const char* title, bool& cancel, void* data); @@ -44,36 +112,37 @@ private: // Handle requests: cmServerResponse SetProtocolVersion(const cmServerRequest& request); - void PrintHello() const; + void PrintHello(cmConnection* connection) const; // Write responses: void WriteProgress(const cmServerRequest& request, int min, int current, int max, const std::string& message) const; void WriteMessage(const cmServerRequest& request, const std::string& message, const std::string& title) const; - void WriteResponse(const cmServerResponse& response, + void WriteResponse(cmConnection* connection, + const cmServerResponse& response, const DebugInfo* debug) const; - void WriteParseError(const std::string& message) const; + void WriteParseError(cmConnection* connection, + const std::string& message) const; void WriteSignal(const std::string& name, const Json::Value& obj) const; void WriteJsonObject(Json::Value const& jsonValue, const DebugInfo* debug) const; + void WriteJsonObject(cmConnection* connection, Json::Value const& jsonValue, + const DebugInfo* debug) const; + static cmServerProtocol* FindMatchingProtocol( const std::vector<cmServerProtocol*>& protocols, int major, int minor); - cmServerConnection* Connection = nullptr; const bool SupportExperimental; cmServerProtocol* Protocol = nullptr; std::vector<cmServerProtocol*> SupportedProtocols; - std::vector<std::string> Queue; std::string DataBuffer; std::string JsonData; - uv_loop_t* Loop = nullptr; - typedef union { uv_tty_t tty; @@ -87,7 +156,6 @@ private: mutable bool Writing = false; - friend class cmServerConnection; friend class cmServerProtocol; friend class cmServerRequest; }; diff --git a/Source/cmServerConnection.cxx b/Source/cmServerConnection.cxx index 36312ed..4891131 100644 --- a/Source/cmServerConnection.cxx +++ b/Source/cmServerConnection.cxx @@ -2,376 +2,123 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmServerConnection.h" -#include "cmFileMonitor.h" #include "cmServer.h" #include "cmServerDictionary.h" -#include <assert.h> -#include <string.h> - -namespace { - -struct write_req_t +cmStdIoConnection::cmStdIoConnection( + cmConnectionBufferStrategy* bufferStrategy) + : cmEventBasedConnection(bufferStrategy) + , Input() + , Output() { - uv_write_t req; - uv_buf_t buf; -}; - -void on_alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) -{ - (void)(handle); - char* rawBuffer = new char[suggested_size]; - *buf = uv_buf_init(rawBuffer, static_cast<unsigned int>(suggested_size)); -} - -void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) -{ - auto conn = reinterpret_cast<cmServerConnection*>(stream->data); - if (nread >= 0) { - conn->ReadData(std::string(buf->base, buf->base + nread)); - } else { - conn->TriggerShutdown(); - } - - delete[](buf->base); } -void on_write(uv_write_t* req, int status) +void cmStdIoConnection::SetServer(cmServerBase* s) { - (void)(status); - auto conn = reinterpret_cast<cmServerConnection*>(req->data); - - // Free req and buffer - write_req_t* wr = reinterpret_cast<write_req_t*>(req); - delete[](wr->buf.base); - delete wr; + cmConnection::SetServer(s); - conn->ProcessNextRequest(); -} + if (uv_guess_handle(1) == UV_TTY) { + usesTty = true; -void on_new_connection(uv_stream_t* stream, int status) -{ - (void)(status); - auto conn = reinterpret_cast<cmServerConnection*>(stream->data); - conn->Connect(stream); -} + this->Input.tty = new uv_tty_t(); + uv_tty_init(this->Server->GetLoop(), this->Input.tty, 0, 1); + uv_tty_set_mode(this->Input.tty, UV_TTY_MODE_NORMAL); + this->Input.tty->data = static_cast<cmEventBasedConnection*>(this); + this->ReadStream = reinterpret_cast<uv_stream_t*>(this->Input.tty); -void on_signal(uv_signal_t* signal, int signum) -{ - auto conn = reinterpret_cast<cmServerConnection*>(signal->data); - (void)(signum); - conn->TriggerShutdown(); -} + this->Output.tty = new uv_tty_t(); + uv_tty_init(this->Server->GetLoop(), this->Output.tty, 1, 0); + uv_tty_set_mode(this->Output.tty, UV_TTY_MODE_NORMAL); + this->Output.tty->data = static_cast<cmEventBasedConnection*>(this); + this->WriteStream = reinterpret_cast<uv_stream_t*>(this->Output.tty); + } else { + usesTty = false; -void on_signal_close(uv_handle_t* handle) -{ - delete reinterpret_cast<uv_signal_t*>(handle); -} + this->Input.pipe = new uv_pipe_t(); + uv_pipe_init(this->Server->GetLoop(), this->Input.pipe, 0); + uv_pipe_open(this->Input.pipe, 0); + this->Input.pipe->data = static_cast<cmEventBasedConnection*>(this); + this->ReadStream = reinterpret_cast<uv_stream_t*>(this->Input.pipe); -void on_pipe_close(uv_handle_t* handle) -{ - delete reinterpret_cast<uv_pipe_t*>(handle); + this->Output.pipe = new uv_pipe_t(); + uv_pipe_init(this->Server->GetLoop(), this->Output.pipe, 0); + uv_pipe_open(this->Output.pipe, 1); + this->Output.pipe->data = static_cast<cmEventBasedConnection*>(this); + this->WriteStream = reinterpret_cast<uv_stream_t*>(this->Output.pipe); + } } -void on_tty_close(uv_handle_t* handle) +bool cmStdIoConnection::OnServeStart(std::string* pString) { - delete reinterpret_cast<uv_tty_t*>(handle); + uv_read_start(this->ReadStream, on_alloc_buffer, on_read); + Server->OnConnected(this); + return cmConnection::OnServeStart(pString); } -} // namespace - -class LoopGuard +bool cmStdIoConnection::OnConnectionShuttingDown() { -public: - LoopGuard(cmServerConnection* connection) - : Connection(connection) - { - this->Connection->mLoop = uv_default_loop(); - if (!this->Connection->mLoop) { - return; - } - this->Connection->mFileMonitor = - new cmFileMonitor(this->Connection->mLoop); - } - - ~LoopGuard() - { - if (!this->Connection->mLoop) { - return; - } + cmEventBasedConnection::OnConnectionShuttingDown(); - if (this->Connection->mFileMonitor) { - delete this->Connection->mFileMonitor; - } - uv_loop_close(this->Connection->mLoop); - this->Connection->mLoop = nullptr; + if (usesTty) { + uv_read_stop(reinterpret_cast<uv_stream_t*>(this->Input.tty)); + uv_close(reinterpret_cast<uv_handle_t*>(this->Input.tty), + &on_close_delete); + uv_close(reinterpret_cast<uv_handle_t*>(this->Output.tty), + &on_close_delete); + } else { + uv_close(reinterpret_cast<uv_handle_t*>(this->Input.pipe), + &on_close_delete); + uv_close(reinterpret_cast<uv_handle_t*>(this->Output.pipe), + &on_close_delete); } -private: - cmServerConnection* Connection; -}; + return true; +} -cmServerConnection::cmServerConnection() +cmServerPipeConnection::cmServerPipeConnection(const std::string& name) + : cmPipeConnection(name, new cmServerBufferStrategy) { } -cmServerConnection::~cmServerConnection() +cmServerStdIoConnection::cmServerStdIoConnection() + : cmStdIoConnection(new cmServerBufferStrategy) { } -void cmServerConnection::SetServer(cmServer* s) +cmConnectionBufferStrategy::~cmConnectionBufferStrategy() { - this->Server = s; } -bool cmServerConnection::ProcessEvents(std::string* errorMessage) +void cmConnectionBufferStrategy::clear() { - assert(this->Server); - errorMessage->clear(); - - this->RawReadBuffer.clear(); - this->RequestBuffer.clear(); - - LoopGuard guard(this); - (void)(guard); - if (!this->mLoop) { - *errorMessage = "Internal Error: Failed to create event loop."; - return false; - } - - this->SIGINTHandler = new uv_signal_t; - uv_signal_init(this->mLoop, this->SIGINTHandler); - this->SIGINTHandler->data = static_cast<void*>(this); - uv_signal_start(this->SIGINTHandler, &on_signal, SIGINT); - - this->SIGHUPHandler = new uv_signal_t; - uv_signal_init(this->mLoop, this->SIGHUPHandler); - this->SIGHUPHandler->data = static_cast<void*>(this); - uv_signal_start(this->SIGHUPHandler, &on_signal, SIGHUP); - - if (!DoSetup(errorMessage)) { - return false; - } - - if (uv_run(this->mLoop, UV_RUN_DEFAULT) != 0) { - *errorMessage = "Internal Error: Event loop stopped in unclean state."; - return false; - } - - // These need to be cleaned up by now: - assert(!this->ReadStream); - assert(!this->WriteStream); - - this->RawReadBuffer.clear(); - this->RequestBuffer.clear(); - - return true; } -void cmServerConnection::ReadData(const std::string& data) +std::string cmServerBufferStrategy::BufferMessage(std::string& RawReadBuffer) { - this->RawReadBuffer += data; - for (;;) { - auto needle = this->RawReadBuffer.find('\n'); + auto needle = RawReadBuffer.find('\n'); if (needle == std::string::npos) { - return; + return ""; } - std::string line = this->RawReadBuffer.substr(0, needle); + std::string line = RawReadBuffer.substr(0, needle); const auto ls = line.size(); if (ls > 1 && line.at(ls - 1) == '\r') { line.erase(ls - 1, 1); } - this->RawReadBuffer.erase(this->RawReadBuffer.begin(), - this->RawReadBuffer.begin() + - static_cast<long>(needle) + 1); + RawReadBuffer.erase(RawReadBuffer.begin(), + RawReadBuffer.begin() + static_cast<long>(needle) + 1); if (line == kSTART_MAGIC) { - this->RequestBuffer.clear(); + RequestBuffer.clear(); continue; } if (line == kEND_MAGIC) { - this->Server->QueueRequest(this->RequestBuffer); - this->RequestBuffer.clear(); - } else { - this->RequestBuffer += line; - this->RequestBuffer += "\n"; + std::string rtn; + rtn.swap(this->RequestBuffer); + return rtn; } - } -} -void cmServerConnection::TriggerShutdown() -{ - this->FileMonitor()->StopMonitoring(); - - uv_signal_stop(this->SIGINTHandler); - uv_signal_stop(this->SIGHUPHandler); - - uv_close(reinterpret_cast<uv_handle_t*>(this->SIGINTHandler), - &on_signal_close); // delete handle - uv_close(reinterpret_cast<uv_handle_t*>(this->SIGHUPHandler), - &on_signal_close); // delete handle - - this->SIGINTHandler = nullptr; - this->SIGHUPHandler = nullptr; - - this->TearDown(); -} - -void cmServerConnection::WriteData(const std::string& data) -{ - assert(this->WriteStream); - - auto ds = data.size(); - - write_req_t* req = new write_req_t; - req->req.data = this; - req->buf = uv_buf_init(new char[ds], static_cast<unsigned int>(ds)); - memcpy(req->buf.base, data.c_str(), ds); - - uv_write(reinterpret_cast<uv_write_t*>(req), - static_cast<uv_stream_t*>(this->WriteStream), &req->buf, 1, - on_write); -} - -void cmServerConnection::ProcessNextRequest() -{ - Server->PopOne(); -} - -void cmServerConnection::SendGreetings() -{ - Server->PrintHello(); -} - -cmServerStdIoConnection::cmServerStdIoConnection() -{ - this->Input.tty = nullptr; - this->Output.tty = nullptr; -} - -bool cmServerStdIoConnection::DoSetup(std::string* errorMessage) -{ - (void)(errorMessage); - - if (uv_guess_handle(1) == UV_TTY) { - usesTty = true; - this->Input.tty = new uv_tty_t; - uv_tty_init(this->Loop(), this->Input.tty, 0, 1); - uv_tty_set_mode(this->Input.tty, UV_TTY_MODE_NORMAL); - Input.tty->data = this; - this->ReadStream = reinterpret_cast<uv_stream_t*>(this->Input.tty); - - this->Output.tty = new uv_tty_t; - uv_tty_init(this->Loop(), this->Output.tty, 1, 0); - uv_tty_set_mode(this->Output.tty, UV_TTY_MODE_NORMAL); - Output.tty->data = this; - this->WriteStream = reinterpret_cast<uv_stream_t*>(this->Output.tty); - } else { - usesTty = false; - this->Input.pipe = new uv_pipe_t; - uv_pipe_init(this->Loop(), this->Input.pipe, 0); - uv_pipe_open(this->Input.pipe, 0); - Input.pipe->data = this; - this->ReadStream = reinterpret_cast<uv_stream_t*>(this->Input.pipe); - - this->Output.pipe = new uv_pipe_t; - uv_pipe_init(this->Loop(), this->Output.pipe, 0); - uv_pipe_open(this->Output.pipe, 1); - Output.pipe->data = this; - this->WriteStream = reinterpret_cast<uv_stream_t*>(this->Output.pipe); + this->RequestBuffer += line; + this->RequestBuffer += "\n"; } - - SendGreetings(); - uv_read_start(this->ReadStream, on_alloc_buffer, on_read); - - return true; -} - -void cmServerStdIoConnection::TearDown() -{ - if (usesTty) { - uv_close(reinterpret_cast<uv_handle_t*>(this->Input.tty), &on_tty_close); - uv_close(reinterpret_cast<uv_handle_t*>(this->Output.tty), &on_tty_close); - this->Input.tty = nullptr; - this->Output.tty = nullptr; - } else { - uv_close(reinterpret_cast<uv_handle_t*>(this->Input.pipe), &on_pipe_close); - uv_close(reinterpret_cast<uv_handle_t*>(this->Output.pipe), - &on_pipe_close); - this->Input.pipe = nullptr; - this->Input.pipe = nullptr; - } - this->ReadStream = nullptr; - this->WriteStream = nullptr; -} - -cmServerPipeConnection::cmServerPipeConnection(const std::string& name) - : PipeName(name) -{ -} - -bool cmServerPipeConnection::DoSetup(std::string* errorMessage) -{ - this->ServerPipe = new uv_pipe_t; - uv_pipe_init(this->Loop(), this->ServerPipe, 0); - this->ServerPipe->data = this; - - int r; - if ((r = uv_pipe_bind(this->ServerPipe, this->PipeName.c_str())) != 0) { - *errorMessage = std::string("Internal Error with ") + this->PipeName + - ": " + uv_err_name(r); - return false; - } - auto serverStream = reinterpret_cast<uv_stream_t*>(this->ServerPipe); - if ((r = uv_listen(serverStream, 1, on_new_connection)) != 0) { - *errorMessage = std::string("Internal Error listening on ") + - this->PipeName + ": " + uv_err_name(r); - return false; - } - - return true; -} - -void cmServerPipeConnection::TearDown() -{ - if (this->ClientPipe) { - uv_close(reinterpret_cast<uv_handle_t*>(this->ClientPipe), &on_pipe_close); - this->WriteStream->data = nullptr; - } - uv_close(reinterpret_cast<uv_handle_t*>(this->ServerPipe), &on_pipe_close); - - this->ClientPipe = nullptr; - this->ServerPipe = nullptr; - this->WriteStream = nullptr; - this->ReadStream = nullptr; -} - -void cmServerPipeConnection::Connect(uv_stream_t* server) -{ - if (this->ClientPipe) { - // Accept and close all pipes but the first: - uv_pipe_t* rejectPipe = new uv_pipe_t; - - uv_pipe_init(this->Loop(), rejectPipe, 0); - auto rejecter = reinterpret_cast<uv_stream_t*>(rejectPipe); - uv_accept(server, rejecter); - uv_close(reinterpret_cast<uv_handle_t*>(rejecter), &on_pipe_close); - return; - } - - this->ClientPipe = new uv_pipe_t; - uv_pipe_init(this->Loop(), this->ClientPipe, 0); - this->ClientPipe->data = this; - auto client = reinterpret_cast<uv_stream_t*>(this->ClientPipe); - if (uv_accept(server, client) != 0) { - uv_close(reinterpret_cast<uv_handle_t*>(client), nullptr); - return; - } - this->ReadStream = client; - this->WriteStream = client; - - uv_read_start(this->ReadStream, on_alloc_buffer, on_read); - - this->SendGreetings(); } diff --git a/Source/cmServerConnection.h b/Source/cmServerConnection.h index b96bf3c..0804f0e 100644 --- a/Source/cmServerConnection.h +++ b/Source/cmServerConnection.h @@ -2,68 +2,46 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #pragma once -#include "cmConfigure.h" +#include "cmConnection.h" +#include "cmPipeConnection.h" #include "cm_uv.h" #include <string> -class cmFileMonitor; -class cmServer; +class cmServerBase; -class cmServerConnection +/*** + * This connection buffer strategy accepts messages in the form of + * [== "CMake Server" ==[ +{ + ... some JSON message ... +} +]== "CMake Server" ==] + * and only passes on the core json; it discards the envelope. + */ +class cmServerBufferStrategy : public cmConnectionBufferStrategy { - CM_DISABLE_COPY(cmServerConnection) - public: - cmServerConnection(); - virtual ~cmServerConnection(); - - void SetServer(cmServer* s); - - bool ProcessEvents(std::string* errorMessage); - - void ReadData(const std::string& data); - void TriggerShutdown(); - void WriteData(const std::string& data); - void ProcessNextRequest(); - - virtual void Connect(uv_stream_t* server) { (void)(server); } - - cmFileMonitor* FileMonitor() const { return this->mFileMonitor; } - -protected: - virtual bool DoSetup(std::string* errorMessage) = 0; - virtual void TearDown() = 0; - - void SendGreetings(); - - uv_loop_t* Loop() const { return mLoop; } - -protected: - std::string RawReadBuffer; - std::string RequestBuffer; - - uv_stream_t* ReadStream = nullptr; - uv_stream_t* WriteStream = nullptr; + std::string BufferMessage(std::string& rawBuffer) override; private: - uv_loop_t* mLoop = nullptr; - cmFileMonitor* mFileMonitor = nullptr; - cmServer* Server = nullptr; - uv_signal_t* SIGINTHandler = nullptr; - uv_signal_t* SIGHUPHandler = nullptr; - - friend class LoopGuard; + std::string RequestBuffer; }; -class cmServerStdIoConnection : public cmServerConnection +/*** + * Generic connection over std io interfaces -- tty + */ +class cmStdIoConnection : public cmEventBasedConnection { public: - cmServerStdIoConnection(); - bool DoSetup(std::string* errorMessage) override; + cmStdIoConnection(cmConnectionBufferStrategy* bufferStrategy); + + void SetServer(cmServerBase* s) override; + + bool OnConnectionShuttingDown() override; - void TearDown() override; + bool OnServeStart(std::string* pString) override; private: typedef union @@ -78,18 +56,18 @@ private: InOutUnion Output; }; -class cmServerPipeConnection : public cmServerConnection +/*** + * These specific connections use the cmake server + * buffering strategy. + */ +class cmServerStdIoConnection : public cmStdIoConnection { public: - cmServerPipeConnection(const std::string& name); - bool DoSetup(std::string* errorMessage) override; - - void TearDown() override; - - void Connect(uv_stream_t* server) override; + cmServerStdIoConnection(); +}; -private: - const std::string PipeName; - uv_pipe_t* ServerPipe = nullptr; - uv_pipe_t* ClientPipe = nullptr; +class cmServerPipeConnection : public cmPipeConnection +{ +public: + cmServerPipeConnection(const std::string& name); }; diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx index 21b69cf..8ad4916 100644 --- a/Source/cmServerProtocol.cxx +++ b/Source/cmServerProtocol.cxx @@ -128,11 +128,13 @@ void getCMakeInputs(const cmGlobalGenerator* gg, const std::string& sourceDir, } // namespace -cmServerRequest::cmServerRequest(cmServer* server, const std::string& t, - const std::string& c, const Json::Value& d) +cmServerRequest::cmServerRequest(cmServer* server, cmConnection* connection, + const std::string& t, const std::string& c, + const Json::Value& d) : Type(t) , Cookie(c) , Data(d) + , Connection(connection) , m_Server(server) { } diff --git a/Source/cmServerProtocol.h b/Source/cmServerProtocol.h index 4e57bb6..145708c 100644 --- a/Source/cmServerProtocol.h +++ b/Source/cmServerProtocol.h @@ -11,6 +11,7 @@ #include <string> #include <utility> +class cmConnection; class cmFileMonitor; class cmServer; class cmServerRequest; @@ -52,9 +53,11 @@ public: const std::string Type; const std::string Cookie; const Json::Value Data; + cmConnection* Connection; private: - cmServerRequest(cmServer* server, const std::string& t, const std::string& c, + cmServerRequest(cmServer* server, cmConnection* connection, + const std::string& t, const std::string& c, const Json::Value& d); void ReportProgress(int min, int current, int max, diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index f7192e0..9f214c3 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -934,19 +934,17 @@ bool cmSystemTools::RenameFile(const char* oldname, const char* newname) #endif } -bool cmSystemTools::ComputeFileMD5(const std::string& source, char* md5out) +std::string cmSystemTools::ComputeFileHash(const std::string& source, + cmCryptoHash::Algo algo) { #if defined(CMAKE_BUILD_WITH_CMAKE) - cmCryptoHash md5(cmCryptoHash::AlgoMD5); - std::string const str = md5.HashFile(source); - strncpy(md5out, str.c_str(), 32); - return !str.empty(); + cmCryptoHash hash(algo); + return hash.HashFile(source); #else (void)source; - (void)md5out; - cmSystemTools::Message("md5sum not supported in bootstrapping mode", + cmSystemTools::Message("hashsum not supported in bootstrapping mode", "Error"); - return false; + return std::string(); #endif } diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index 9de7967..e163c91 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -5,6 +5,7 @@ #include "cmConfigure.h" +#include "cmCryptoHash.h" #include "cmProcessOutput.h" #include "cmsys/Process.h" #include "cmsys/SystemTools.hxx" // IWYU pragma: export @@ -179,8 +180,9 @@ public: if possible). */ static bool RenameFile(const char* oldname, const char* newname); - ///! Compute the md5sum of a file - static bool ComputeFileMD5(const std::string& source, char* md5out); + ///! Compute the hash of a file + static std::string ComputeFileHash(const std::string& source, + cmCryptoHash::Algo algo); /** Compute the md5sum of a string. */ static std::string ComputeStringMD5(const std::string& input); diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index d5b0861..c546c7e 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -44,6 +44,8 @@ #include <stdlib.h> #include <time.h> +class cmConnection; + int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, std::vector<std::string>::const_iterator argEnd); int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, @@ -88,6 +90,11 @@ void CMakeCommandUsage(const char* program) << " environment - display the current environment\n" << " make_directory <dir>... - create parent and <dir> directories\n" << " md5sum <file>... - create MD5 checksum of files\n" + << " sha1sum <file>... - create SHA1 checksum of files\n" + << " sha224sum <file>... - create SHA224 checksum of files\n" + << " sha256sum <file>... - create SHA256 checksum of files\n" + << " sha384sum <file>... - create SHA384 checksum of files\n" + << " sha512sum <file>... - create SHA512 checksum of files\n" << " remove [-f] <file>... - remove the file(s), use -f to force " "it\n" << " remove_directory dir - remove a directory and its contents\n" @@ -641,24 +648,28 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) // Command to calculate the md5sum of a file if (args[1] == "md5sum" && args.size() >= 3) { - char md5out[32]; - int retval = 0; - for (std::string::size_type cc = 2; cc < args.size(); cc++) { - const char* filename = args[cc].c_str(); - // Cannot compute md5sum of a directory - if (cmSystemTools::FileIsDirectory(filename)) { - std::cerr << "Error: " << filename << " is a directory" << std::endl; - retval++; - } else if (!cmSystemTools::ComputeFileMD5(filename, md5out)) { - // To mimic md5sum behavior in a shell: - std::cerr << filename << ": No such file or directory" << std::endl; - retval++; - } else { - std::cout << std::string(md5out, 32) << " " << filename - << std::endl; - } - } - return retval; + return HashSumFile(args, cmCryptoHash::AlgoMD5); + } + + // Command to calculate the sha1sum of a file + if (args[1] == "sha1sum" && args.size() >= 3) { + return HashSumFile(args, cmCryptoHash::AlgoSHA1); + } + + if (args[1] == "sha224sum" && args.size() >= 3) { + return HashSumFile(args, cmCryptoHash::AlgoSHA224); + } + + if (args[1] == "sha256sum" && args.size() >= 3) { + return HashSumFile(args, cmCryptoHash::AlgoSHA256); + } + + if (args[1] == "sha384sum" && args.size() >= 3) { + return HashSumFile(args, cmCryptoHash::AlgoSHA384); + } + + if (args[1] == "sha512sum" && args.size() >= 3) { + return HashSumFile(args, cmCryptoHash::AlgoSHA512); } // Command to change directory and run a program. @@ -1013,7 +1024,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) } } #if defined(HAVE_SERVER_MODE) && HAVE_SERVER_MODE - cmServerConnection* conn; + cmConnection* conn; if (isDebug) { conn = new cmServerStdIoConnection; } else { @@ -1074,6 +1085,33 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) return 1; } +int cmcmd::HashSumFile(std::vector<std::string>& args, cmCryptoHash::Algo algo) +{ + if (args.size() < 3) { + return -1; + } + int retval = 0; + + for (std::string::size_type cc = 2; cc < args.size(); cc++) { + const char* filename = args[cc].c_str(); + // Cannot compute sum of a directory + if (cmSystemTools::FileIsDirectory(filename)) { + std::cerr << "Error: " << filename << " is a directory" << std::endl; + retval++; + } else { + std::string value = cmSystemTools::ComputeFileHash(filename, algo); + if (value.empty()) { + // To mimic "md5sum/shasum" behavior in a shell: + std::cerr << filename << ": No such file or directory" << std::endl; + retval++; + } else { + std::cout << value << " " << filename << std::endl; + } + } + } + return retval; +} + int cmcmd::SymlinkLibrary(std::vector<std::string>& args) { int result = 0; diff --git a/Source/cmcmd.h b/Source/cmcmd.h index 929f1ae..faac1d2 100644 --- a/Source/cmcmd.h +++ b/Source/cmcmd.h @@ -4,6 +4,7 @@ #define cmcmd_h #include "cmConfigure.h" // IWYU pragma: keep +#include "cmCryptoHash.h" #include <string> #include <vector> @@ -18,6 +19,8 @@ public: static int ExecuteCMakeCommand(std::vector<std::string>&); protected: + static int HashSumFile(std::vector<std::string>& args, + cmCryptoHash::Algo algo); static int SymlinkLibrary(std::vector<std::string>& args); static int SymlinkExecutable(std::vector<std::string>& args); static bool SymlinkInternal(std::string const& file, diff --git a/Tests/CompileFeatures/default_dialect.cpp b/Tests/CompileFeatures/default_dialect.cpp index 9b65b42..0de1125 100644 --- a/Tests/CompileFeatures/default_dialect.cpp +++ b/Tests/CompileFeatures/default_dialect.cpp @@ -2,25 +2,30 @@ template <long l> struct Outputter; +#if defined(_MSC_VER) && defined(_MSVC_LANG) +#define CXX_STD _MSVC_LANG +#else +#define CXX_STD __cplusplus +#endif + #if DEFAULT_CXX17 -#if __cplusplus <= 201402L -Outputter<__cplusplus> o; +#if CXX_STD <= 201402L +Outputter<CXX_STD> o; #endif #elif DEFAULT_CXX14 -#if __cplusplus != 201402L -Outputter<__cplusplus> o; +#if CXX_STD != 201402L +Outputter<CXX_STD> o; #endif #elif DEFAULT_CXX11 -#if __cplusplus != 201103L -Outputter<__cplusplus> o; +#if CXX_STD != 201103L +Outputter<CXX_STD> o; #endif #else #if !DEFAULT_CXX98 #error Buildsystem error #endif -#if __cplusplus != 199711L && __cplusplus != 1 && \ - !defined(__GXX_EXPERIMENTAL_CXX0X__) -Outputter<__cplusplus> o; +#if CXX_STD != 199711L && CXX_STD != 1 && !defined(__GXX_EXPERIMENTAL_CXX0X__) +Outputter<CXX_STD> o; #endif #endif diff --git a/Tests/QtAutogen/CMakeLists.txt b/Tests/QtAutogen/CMakeLists.txt index 89d2b80..9393f1e 100644 --- a/Tests/QtAutogen/CMakeLists.txt +++ b/Tests/QtAutogen/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.7) - +cmake_minimum_required(VERSION 3.9) +cmake_policy(SET CMP0071 NEW) project(QtAutogen) # Tell find_package(Qt5) where to find Qt. @@ -304,11 +304,16 @@ add_subdirectory(mocDepends) # -- Test # Tests various include moc patterns -add_subdirectory(mocIncludeStrict) +if(ALLOW_WRAP_CPP) + add_subdirectory(mocIncludeStrict) + add_subdirectory(mocIncludeRelaxed) +endif() # -- Test -# Tests various include moc patterns -add_subdirectory(mocIncludeRelaxed) +# Tests policy 0071 +if(ALLOW_WRAP_CPP) + add_subdirectory(mocCMP0071) +endif() # -- Test # Tests Q_PLUGIN_METADATA json file change detection diff --git a/Tests/QtAutogen/complex/CMakeLists.txt b/Tests/QtAutogen/complex/CMakeLists.txt index d48f6cc..2043ccf 100644 --- a/Tests/QtAutogen/complex/CMakeLists.txt +++ b/Tests/QtAutogen/complex/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.9) # -- Test: AUTOMOC AUTORCC AUTOUIC add_definitions(-DFOO -DSomeDefine="Barx") diff --git a/Tests/QtAutogen/mocCMP0071/CMakeLists.txt b/Tests/QtAutogen/mocCMP0071/CMakeLists.txt new file mode 100644 index 0000000..003fa08 --- /dev/null +++ b/Tests/QtAutogen/mocCMP0071/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.9) +project(mocCMP0071 CXX) +add_subdirectory(OLD) +add_subdirectory(NEW) diff --git a/Tests/QtAutogen/mocCMP0071/NEW/CMakeLists.txt b/Tests/QtAutogen/mocCMP0071/NEW/CMakeLists.txt new file mode 100644 index 0000000..0237afc --- /dev/null +++ b/Tests/QtAutogen/mocCMP0071/NEW/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.9) +cmake_policy(SET CMP0071 NEW) + +# *Generate* files +set(CSD ${CMAKE_CURRENT_SOURCE_DIR}) +set(CBD ${CMAKE_CURRENT_BINARY_DIR}) +add_custom_command( + OUTPUT ${CBD}/Obj_p.h ${CBD}/Obj.hpp ${CBD}/Obj.cpp ${CBD}/main.cpp + COMMAND ${CMAKE_COMMAND} -E copy ${CSD}/../Obj_p.h ${CBD}/Obj_p.h + COMMAND ${CMAKE_COMMAND} -E copy ${CSD}/../Obj.hpp ${CBD}/Obj.hpp + COMMAND ${CMAKE_COMMAND} -E copy ${CSD}/../Obj.cpp ${CBD}/Obj.cpp + COMMAND ${CMAKE_COMMAND} -E copy ${CSD}/../main.cpp ${CBD}/main.cpp) + +add_executable(mocCMP0071New ${CBD}/Obj.cpp ${CBD}/main.cpp) +target_link_libraries(mocCMP0071New ${QT_LIBRARIES}) +set_target_properties(mocCMP0071New PROPERTIES AUTOMOC ON) diff --git a/Tests/QtAutogen/mocCMP0071/OLD/CMakeLists.txt b/Tests/QtAutogen/mocCMP0071/OLD/CMakeLists.txt new file mode 100644 index 0000000..5699433 --- /dev/null +++ b/Tests/QtAutogen/mocCMP0071/OLD/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.9) +cmake_policy(SET CMP0071 OLD) + +# *Generate* files +set(CSD ${CMAKE_CURRENT_SOURCE_DIR}) +set(CBD ${CMAKE_CURRENT_BINARY_DIR}) +add_custom_command( + OUTPUT ${CBD}/Obj_p.h ${CBD}/Obj.hpp ${CBD}/Obj.cpp ${CBD}/main.cpp + COMMAND ${CMAKE_COMMAND} -E copy ${CSD}/../Obj_p.h ${CBD}/Obj_p.h + COMMAND ${CMAKE_COMMAND} -E copy ${CSD}/../Obj.hpp ${CBD}/Obj.hpp + COMMAND ${CMAKE_COMMAND} -E copy ${CSD}/../Obj.cpp ${CBD}/Obj.cpp + COMMAND ${CMAKE_COMMAND} -E copy ${CSD}/../main.cpp ${CBD}/main.cpp) + +# Generate moc files externally +qtx_wrap_cpp(mocCMP0071OldMoc ${CBD}/Obj.hpp ${CBD}/Obj_p.h) +add_executable(mocCMP0071Old ${CBD}/Obj.cpp ${CBD}/main.cpp ${mocCMP0071OldMoc}) +target_link_libraries(mocCMP0071Old ${QT_LIBRARIES}) +set_target_properties(mocCMP0071Old PROPERTIES AUTOMOC ON) diff --git a/Tests/QtAutogen/mocCMP0071/Obj.cpp b/Tests/QtAutogen/mocCMP0071/Obj.cpp new file mode 100644 index 0000000..1ae50ed --- /dev/null +++ b/Tests/QtAutogen/mocCMP0071/Obj.cpp @@ -0,0 +1,20 @@ +#include "Obj.hpp" +#include "Obj_p.h" + +ObjPrivate::ObjPrivate() +{ +} + +ObjPrivate::~ObjPrivate() +{ +} + +Obj::Obj() + : d(new ObjPrivate) +{ +} + +Obj::~Obj() +{ + delete d; +} diff --git a/Tests/QtAutogen/mocCMP0071/Obj.hpp b/Tests/QtAutogen/mocCMP0071/Obj.hpp new file mode 100644 index 0000000..f064e47 --- /dev/null +++ b/Tests/QtAutogen/mocCMP0071/Obj.hpp @@ -0,0 +1,19 @@ +#ifndef OBJ_HPP +#define OBJ_HPP + +#include <QObject> + +// Object source comes without any _moc/.moc includes +class ObjPrivate; +class Obj : public QObject +{ + Q_OBJECT +public: + Obj(); + ~Obj(); + +private: + ObjPrivate* const d; +}; + +#endif diff --git a/Tests/QtAutogen/mocCMP0071/Obj_p.h b/Tests/QtAutogen/mocCMP0071/Obj_p.h new file mode 100644 index 0000000..cb1e5df --- /dev/null +++ b/Tests/QtAutogen/mocCMP0071/Obj_p.h @@ -0,0 +1,14 @@ +#ifndef OBJ_P_HPP +#define OBJ_P_HPP + +#include <QObject> + +class ObjPrivate : public QObject +{ + Q_OBJECT +public: + ObjPrivate(); + ~ObjPrivate(); +}; + +#endif diff --git a/Tests/QtAutogen/mocCMP0071/main.cpp b/Tests/QtAutogen/mocCMP0071/main.cpp new file mode 100644 index 0000000..3887840 --- /dev/null +++ b/Tests/QtAutogen/mocCMP0071/main.cpp @@ -0,0 +1,7 @@ +#include "Obj.hpp" + +int main(int argv, char** args) +{ + Obj obj; + return 0; +} diff --git a/Tests/QtAutogen/mocDepends/CMakeLists.txt b/Tests/QtAutogen/mocDepends/CMakeLists.txt index a67dcfe..cd5adf5 100644 --- a/Tests/QtAutogen/mocDepends/CMakeLists.txt +++ b/Tests/QtAutogen/mocDepends/CMakeLists.txt @@ -1,5 +1,6 @@ -cmake_minimum_required(VERSION 3.7) -project(mocDepends) +cmake_minimum_required(VERSION 3.9) +cmake_policy(SET CMP0071 NEW) +project(mocDepends CXX) if (QT_TEST_VERSION STREQUAL 4) find_package(Qt4 REQUIRED) @@ -28,7 +29,6 @@ add_executable(mocDepends1 test1.cpp ) target_link_libraries(mocDepends1 ${QT_CORE_TARGET}) set_target_properties(mocDepends1 PROPERTIES AUTOMOC TRUE) -set_property(TARGET mocDepends1 PROPERTY __UNDOCUMENTED_AUTOGEN_GENERATED_FILES 1) # -- Test 2 using generated library # This tests the dependency of AUTOMOC of mocDepends2 to the @@ -44,4 +44,3 @@ add_library(SimpleLib STATIC simpleLib.hpp simpleLib.cpp) add_executable(mocDepends2 test2.cpp ) target_link_libraries(mocDepends2 SimpleLib ${QT_CORE_TARGET}) set_target_properties(mocDepends2 PROPERTIES AUTOMOC TRUE) -set_property(TARGET mocDepends2 PROPERTY __UNDOCUMENTED_AUTOGEN_GENERATED_FILES 1) diff --git a/Tests/QtAutogen/mocPlugin/CMakeLists.txt b/Tests/QtAutogen/mocPlugin/CMakeLists.txt index f80aa29..9b224fb 100644 --- a/Tests/QtAutogen/mocPlugin/CMakeLists.txt +++ b/Tests/QtAutogen/mocPlugin/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.9) project(mocPlugin CXX) set(CMAKE_AUTOMOC_DEPEND_FILTERS diff --git a/Tests/QtAutogen/mocRerun/CMakeLists.txt b/Tests/QtAutogen/mocRerun/CMakeLists.txt index 14b077b..7380bdd 100644 --- a/Tests/QtAutogen/mocRerun/CMakeLists.txt +++ b/Tests/QtAutogen/mocRerun/CMakeLists.txt @@ -1,4 +1,5 @@ -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.9) +cmake_policy(SET CMP0071 NEW) project(mocRerun CXX) if (QT_TEST_VERSION STREQUAL 4) @@ -27,7 +28,6 @@ add_executable(mocRerun ${CMAKE_CURRENT_BINARY_DIR}/main.cpp res1.qrc ) -set_property(TARGET mocRerun PROPERTY __UNDOCUMENTED_AUTOGEN_GENERATED_FILES 1) target_include_directories(mocRerun PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mocRerun ${QT_CORE_TARGET}) # Write target name to text file diff --git a/Tests/QtAutogen/rccDepends/CMakeLists.txt b/Tests/QtAutogen/rccDepends/CMakeLists.txt index 878ae5d..edc0ac3 100644 --- a/Tests/QtAutogen/rccDepends/CMakeLists.txt +++ b/Tests/QtAutogen/rccDepends/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.7) -project(rccDepends) +cmake_minimum_required(VERSION 3.9) +project(rccDepends CXX) set(CMAKE_AUTORCC ON) diff --git a/Tests/RunCMake/CommandLine/E_md5sum-dir-result.txt b/Tests/RunCMake/CommandLine/E_md5sum-dir-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_md5sum-dir-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CommandLine/E_md5sum-dir-stderr.txt b/Tests/RunCMake/CommandLine/E_md5sum-dir-stderr.txt new file mode 100644 index 0000000..061fd64 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_md5sum-dir-stderr.txt @@ -0,0 +1 @@ +Error: . is a directory diff --git a/Tests/RunCMake/CommandLine/E_md5sum-mixed-result.txt b/Tests/RunCMake/CommandLine/E_md5sum-mixed-result.txt new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_md5sum-mixed-result.txt @@ -0,0 +1 @@ +2 diff --git a/Tests/RunCMake/CommandLine/E_md5sum-mixed-stderr.txt b/Tests/RunCMake/CommandLine/E_md5sum-mixed-stderr.txt new file mode 100644 index 0000000..b6b84c3 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_md5sum-mixed-stderr.txt @@ -0,0 +1,2 @@ +Error: . is a directory +nonexisting: No such file or directory diff --git a/Tests/RunCMake/CommandLine/E_md5sum-mixed-stdout.txt b/Tests/RunCMake/CommandLine/E_md5sum-mixed-stdout.txt new file mode 100644 index 0000000..18e49be --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_md5sum-mixed-stdout.txt @@ -0,0 +1 @@ +275876e34cf609db118f3d84b799a790 ../dummy diff --git a/Tests/RunCMake/CommandLine/E_md5sum-no-file-result.txt b/Tests/RunCMake/CommandLine/E_md5sum-no-file-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_md5sum-no-file-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CommandLine/E_md5sum-no-file-stderr.txt b/Tests/RunCMake/CommandLine/E_md5sum-no-file-stderr.txt new file mode 100644 index 0000000..732e8c4 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_md5sum-no-file-stderr.txt @@ -0,0 +1 @@ +nonexisting: No such file or directory diff --git a/Tests/RunCMake/CommandLine/E_md5sum-result.txt b/Tests/RunCMake/CommandLine/E_md5sum-result.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_md5sum-result.txt @@ -0,0 +1 @@ +0 diff --git a/Tests/RunCMake/CommandLine/E_md5sum-stdout.txt b/Tests/RunCMake/CommandLine/E_md5sum-stdout.txt new file mode 100644 index 0000000..18e49be --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_md5sum-stdout.txt @@ -0,0 +1 @@ +275876e34cf609db118f3d84b799a790 ../dummy diff --git a/Tests/RunCMake/CommandLine/E_sha1sum-dir-result.txt b/Tests/RunCMake/CommandLine/E_sha1sum-dir-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha1sum-dir-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CommandLine/E_sha1sum-dir-stderr.txt b/Tests/RunCMake/CommandLine/E_sha1sum-dir-stderr.txt new file mode 100644 index 0000000..061fd64 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha1sum-dir-stderr.txt @@ -0,0 +1 @@ +Error: . is a directory diff --git a/Tests/RunCMake/CommandLine/E_sha1sum-no-file-result.txt b/Tests/RunCMake/CommandLine/E_sha1sum-no-file-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha1sum-no-file-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CommandLine/E_sha1sum-no-file-stderr.txt b/Tests/RunCMake/CommandLine/E_sha1sum-no-file-stderr.txt new file mode 100644 index 0000000..732e8c4 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha1sum-no-file-stderr.txt @@ -0,0 +1 @@ +nonexisting: No such file or directory diff --git a/Tests/RunCMake/CommandLine/E_sha1sum-result.txt b/Tests/RunCMake/CommandLine/E_sha1sum-result.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha1sum-result.txt @@ -0,0 +1 @@ +0 diff --git a/Tests/RunCMake/CommandLine/E_sha1sum-stdout.txt b/Tests/RunCMake/CommandLine/E_sha1sum-stdout.txt new file mode 100644 index 0000000..689b85b --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha1sum-stdout.txt @@ -0,0 +1 @@ +829c3804401b0727f70f73d4415e162400cbe57b ../dummy diff --git a/Tests/RunCMake/CommandLine/E_sha224sum-dir-result.txt b/Tests/RunCMake/CommandLine/E_sha224sum-dir-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha224sum-dir-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CommandLine/E_sha224sum-dir-stderr.txt b/Tests/RunCMake/CommandLine/E_sha224sum-dir-stderr.txt new file mode 100644 index 0000000..061fd64 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha224sum-dir-stderr.txt @@ -0,0 +1 @@ +Error: . is a directory diff --git a/Tests/RunCMake/CommandLine/E_sha224sum-no-file-result.txt b/Tests/RunCMake/CommandLine/E_sha224sum-no-file-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha224sum-no-file-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CommandLine/E_sha224sum-no-file-stderr.txt b/Tests/RunCMake/CommandLine/E_sha224sum-no-file-stderr.txt new file mode 100644 index 0000000..732e8c4 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha224sum-no-file-stderr.txt @@ -0,0 +1 @@ +nonexisting: No such file or directory diff --git a/Tests/RunCMake/CommandLine/E_sha224sum-result.txt b/Tests/RunCMake/CommandLine/E_sha224sum-result.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha224sum-result.txt @@ -0,0 +1 @@ +0 diff --git a/Tests/RunCMake/CommandLine/E_sha224sum-stdout.txt b/Tests/RunCMake/CommandLine/E_sha224sum-stdout.txt new file mode 100644 index 0000000..5b3e217 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha224sum-stdout.txt @@ -0,0 +1 @@ +37d32c6dbabed711cb1d4620b64090fef0ef63ab16a4a51d668259e6 ../dummy diff --git a/Tests/RunCMake/CommandLine/E_sha256sum-dir-result.txt b/Tests/RunCMake/CommandLine/E_sha256sum-dir-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha256sum-dir-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CommandLine/E_sha256sum-dir-stderr.txt b/Tests/RunCMake/CommandLine/E_sha256sum-dir-stderr.txt new file mode 100644 index 0000000..061fd64 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha256sum-dir-stderr.txt @@ -0,0 +1 @@ +Error: . is a directory diff --git a/Tests/RunCMake/CommandLine/E_sha256sum-no-file-result.txt b/Tests/RunCMake/CommandLine/E_sha256sum-no-file-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha256sum-no-file-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CommandLine/E_sha256sum-no-file-stderr.txt b/Tests/RunCMake/CommandLine/E_sha256sum-no-file-stderr.txt new file mode 100644 index 0000000..732e8c4 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha256sum-no-file-stderr.txt @@ -0,0 +1 @@ +nonexisting: No such file or directory diff --git a/Tests/RunCMake/CommandLine/E_sha256sum-result.txt b/Tests/RunCMake/CommandLine/E_sha256sum-result.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha256sum-result.txt @@ -0,0 +1 @@ +0 diff --git a/Tests/RunCMake/CommandLine/E_sha256sum-stdout.txt b/Tests/RunCMake/CommandLine/E_sha256sum-stdout.txt new file mode 100644 index 0000000..9a18770 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha256sum-stdout.txt @@ -0,0 +1 @@ +b5a2c96250612366ea272ffac6d9744aaf4b45aacd96aa7cfcb931ee3b558259 ../dummy diff --git a/Tests/RunCMake/CommandLine/E_sha384sum-dir-result.txt b/Tests/RunCMake/CommandLine/E_sha384sum-dir-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha384sum-dir-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CommandLine/E_sha384sum-dir-stderr.txt b/Tests/RunCMake/CommandLine/E_sha384sum-dir-stderr.txt new file mode 100644 index 0000000..061fd64 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha384sum-dir-stderr.txt @@ -0,0 +1 @@ +Error: . is a directory diff --git a/Tests/RunCMake/CommandLine/E_sha384sum-no-file-result.txt b/Tests/RunCMake/CommandLine/E_sha384sum-no-file-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha384sum-no-file-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CommandLine/E_sha384sum-no-file-stderr.txt b/Tests/RunCMake/CommandLine/E_sha384sum-no-file-stderr.txt new file mode 100644 index 0000000..732e8c4 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha384sum-no-file-stderr.txt @@ -0,0 +1 @@ +nonexisting: No such file or directory diff --git a/Tests/RunCMake/CommandLine/E_sha384sum-result.txt b/Tests/RunCMake/CommandLine/E_sha384sum-result.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha384sum-result.txt @@ -0,0 +1 @@ +0 diff --git a/Tests/RunCMake/CommandLine/E_sha384sum-stdout.txt b/Tests/RunCMake/CommandLine/E_sha384sum-stdout.txt new file mode 100644 index 0000000..b706ac5 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha384sum-stdout.txt @@ -0,0 +1 @@ +43c1835ceba2e29596f05e3859d4fe2b6d124a181ed670f68e914bd3ed251b02b4be609608a13f23ec3d98da6c4eb8cd ../dummy diff --git a/Tests/RunCMake/CommandLine/E_sha512sum-dir-result.txt b/Tests/RunCMake/CommandLine/E_sha512sum-dir-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha512sum-dir-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CommandLine/E_sha512sum-dir-stderr.txt b/Tests/RunCMake/CommandLine/E_sha512sum-dir-stderr.txt new file mode 100644 index 0000000..061fd64 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha512sum-dir-stderr.txt @@ -0,0 +1 @@ +Error: . is a directory diff --git a/Tests/RunCMake/CommandLine/E_sha512sum-no-file-result.txt b/Tests/RunCMake/CommandLine/E_sha512sum-no-file-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha512sum-no-file-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CommandLine/E_sha512sum-no-file-stderr.txt b/Tests/RunCMake/CommandLine/E_sha512sum-no-file-stderr.txt new file mode 100644 index 0000000..732e8c4 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha512sum-no-file-stderr.txt @@ -0,0 +1 @@ +nonexisting: No such file or directory diff --git a/Tests/RunCMake/CommandLine/E_sha512sum-result.txt b/Tests/RunCMake/CommandLine/E_sha512sum-result.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha512sum-result.txt @@ -0,0 +1 @@ +0 diff --git a/Tests/RunCMake/CommandLine/E_sha512sum-stdout.txt b/Tests/RunCMake/CommandLine/E_sha512sum-stdout.txt new file mode 100644 index 0000000..4305383 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_sha512sum-stdout.txt @@ -0,0 +1 @@ +1692526aab84461a8aebcefddcba2b33fb5897ab180c53e8b345ae125484d0aaa35baf60487050be21ed8909a48eace93851bf139087ce1f7a87d97b6120a651 ../dummy diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake index f94b10a..6efcc12 100644 --- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake +++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake @@ -172,6 +172,30 @@ run_cmake_command(E_env-bad-arg1 ${CMAKE_COMMAND} -E env -bad-arg1) run_cmake_command(E_env-set ${CMAKE_COMMAND} -E env TEST_ENV=1 ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/E_env-set.cmake) run_cmake_command(E_env-unset ${CMAKE_COMMAND} -E env TEST_ENV=1 ${CMAKE_COMMAND} -E env --unset=TEST_ENV ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/E_env-unset.cmake) +run_cmake_command(E_md5sum-dir ${CMAKE_COMMAND} -E md5sum .) +run_cmake_command(E_sha1sum-dir ${CMAKE_COMMAND} -E sha1sum .) +run_cmake_command(E_sha224sum-dir ${CMAKE_COMMAND} -E sha224sum .) +run_cmake_command(E_sha256sum-dir ${CMAKE_COMMAND} -E sha256sum .) +run_cmake_command(E_sha384sum-dir ${CMAKE_COMMAND} -E sha384sum .) +run_cmake_command(E_sha512sum-dir ${CMAKE_COMMAND} -E sha512sum .) + +run_cmake_command(E_md5sum-no-file ${CMAKE_COMMAND} -E md5sum nonexisting) +run_cmake_command(E_sha1sum-no-file ${CMAKE_COMMAND} -E sha1sum nonexisting) +run_cmake_command(E_sha224sum-no-file ${CMAKE_COMMAND} -E sha224sum nonexisting) +run_cmake_command(E_sha256sum-no-file ${CMAKE_COMMAND} -E sha256sum nonexisting) +run_cmake_command(E_sha384sum-no-file ${CMAKE_COMMAND} -E sha384sum nonexisting) +run_cmake_command(E_sha512sum-no-file ${CMAKE_COMMAND} -E sha512sum nonexisting) + +file(WRITE "${RunCMake_BINARY_DIR}/dummy" "dummy") +run_cmake_command(E_md5sum ${CMAKE_COMMAND} -E md5sum ../dummy) +run_cmake_command(E_md5sum-mixed ${CMAKE_COMMAND} -E md5sum . ../dummy nonexisting) +run_cmake_command(E_sha1sum ${CMAKE_COMMAND} -E sha1sum ../dummy) +run_cmake_command(E_sha224sum ${CMAKE_COMMAND} -E sha224sum ../dummy) +run_cmake_command(E_sha256sum ${CMAKE_COMMAND} -E sha256sum ../dummy) +run_cmake_command(E_sha384sum ${CMAKE_COMMAND} -E sha384sum ../dummy) +run_cmake_command(E_sha512sum ${CMAKE_COMMAND} -E sha512sum ../dummy) +file(REMOVE "${RunCMake_BINARY_DIR}/dummy") + set(RunCMake_DEFAULT_stderr ".") run_cmake_command(E_sleep-no-args ${CMAKE_COMMAND} -E sleep) unset(RunCMake_DEFAULT_stderr) diff --git a/Tests/RunCMake/ExternalProject/NoOptions-stderr.txt b/Tests/RunCMake/ExternalProject/NoOptions-stderr.txt index 12a76c5..2fc7d29 100644 --- a/Tests/RunCMake/ExternalProject/NoOptions-stderr.txt +++ b/Tests/RunCMake/ExternalProject/NoOptions-stderr.txt @@ -6,12 +6,12 @@ is not an existing non-empty directory. Please specify one of: \* SOURCE_DIR with an existing non-empty directory + \* DOWNLOAD_COMMAND \* URL \* GIT_REPOSITORY + \* SVN_REPOSITORY \* HG_REPOSITORY \* CVS_REPOSITORY and CVS_MODULE - \* SVN_REVISION - \* DOWNLOAD_COMMAND Call Stack \(most recent call first\): .*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_add_download_command\) NoOptions.cmake:[0-9]+ \(ExternalProject_Add\) diff --git a/Tests/RunCMake/ExternalProject/SourceEmpty-stderr.txt b/Tests/RunCMake/ExternalProject/SourceEmpty-stderr.txt index 58a343c..07c6e87 100644 --- a/Tests/RunCMake/ExternalProject/SourceEmpty-stderr.txt +++ b/Tests/RunCMake/ExternalProject/SourceEmpty-stderr.txt @@ -6,12 +6,12 @@ is not an existing non-empty directory. Please specify one of: \* SOURCE_DIR with an existing non-empty directory + \* DOWNLOAD_COMMAND \* URL \* GIT_REPOSITORY + \* SVN_REPOSITORY \* HG_REPOSITORY \* CVS_REPOSITORY and CVS_MODULE - \* SVN_REVISION - \* DOWNLOAD_COMMAND Call Stack \(most recent call first\): .*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_add_download_command\) SourceEmpty.cmake:[0-9]+ \(ExternalProject_Add\) diff --git a/Tests/RunCMake/ExternalProject/SourceMissing-stderr.txt b/Tests/RunCMake/ExternalProject/SourceMissing-stderr.txt index e62f7cf..373f6e3 100644 --- a/Tests/RunCMake/ExternalProject/SourceMissing-stderr.txt +++ b/Tests/RunCMake/ExternalProject/SourceMissing-stderr.txt @@ -6,12 +6,12 @@ is not an existing non-empty directory. Please specify one of: \* SOURCE_DIR with an existing non-empty directory + \* DOWNLOAD_COMMAND \* URL \* GIT_REPOSITORY + \* SVN_REPOSITORY \* HG_REPOSITORY \* CVS_REPOSITORY and CVS_MODULE - \* SVN_REVISION - \* DOWNLOAD_COMMAND Call Stack \(most recent call first\): .*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_add_download_command\) SourceMissing.cmake:[0-9]+ \(ExternalProject_Add\) diff --git a/Tests/RunCMake/ObjectLibrary/OwnSources-result.txt b/Tests/RunCMake/ObjectLibrary/OwnSources-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/ObjectLibrary/OwnSources-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/ObjectLibrary/OwnSources-stderr.txt b/Tests/RunCMake/ObjectLibrary/OwnSources-stderr.txt new file mode 100644 index 0000000..40d650e --- /dev/null +++ b/Tests/RunCMake/ObjectLibrary/OwnSources-stderr.txt @@ -0,0 +1,5 @@ +^CMake Error at OwnSources.cmake:[0-9]+ \(add_library\): + The SOURCES of "A" use a generator expression that depends on the SOURCES + themselves. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/ObjectLibrary/OwnSources.cmake b/Tests/RunCMake/ObjectLibrary/OwnSources.cmake new file mode 100644 index 0000000..e7bdf8d --- /dev/null +++ b/Tests/RunCMake/ObjectLibrary/OwnSources.cmake @@ -0,0 +1,2 @@ +add_library(A OBJECT a.c) +target_sources(A PRIVATE $<TARGET_OBJECTS:A>) diff --git a/Tests/RunCMake/ObjectLibrary/RunCMakeTest.cmake b/Tests/RunCMake/ObjectLibrary/RunCMakeTest.cmake index fe708ce..b8eed73 100644 --- a/Tests/RunCMake/ObjectLibrary/RunCMakeTest.cmake +++ b/Tests/RunCMake/ObjectLibrary/RunCMakeTest.cmake @@ -19,6 +19,7 @@ run_cmake(LinkObjRHS1) run_cmake(LinkObjRHS2) run_cmake(MissingSource) run_cmake(ObjWithObj) +run_cmake(OwnSources) run_cmake(PostBuild) run_cmake(PreBuild) run_cmake(PreLink) diff --git a/Tests/Server/server-test.py b/Tests/Server/server-test.py index 62d9008..5621111 100644 --- a/Tests/Server/server-test.py +++ b/Tests/Server/server-test.py @@ -117,4 +117,4 @@ except: proc.terminate() raise -sys.exit(0) +sys.exit(proc.returncode) diff --git a/Utilities/IWYU/mapping.imp b/Utilities/IWYU/mapping.imp index fe0f7df..23ca091 100644 --- a/Utilities/IWYU/mapping.imp +++ b/Utilities/IWYU/mapping.imp @@ -21,6 +21,7 @@ { include: [ "<wctype.h>", public, "<cwctype>", public ] }, # HACK: check whether this can be removed with next iwyu release. + { include: [ "<bits/shared_ptr.h>", private, "<memory>", public ] }, { include: [ "<bits/std_function.h>", private, "<functional>", public ] }, { include: [ "<bits/time.h>", private, "<time.h>", public ] }, { include: [ "<bits/types/clock_t.h>", private, "<time.h>", public ] }, |