summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Copyright.txt1
-rw-r--r--Help/manual/cmake-modules.7.rst1
-rw-r--r--Help/module/FindPatch.rst1
-rw-r--r--Help/release/dev/find-patch.rst5
-rw-r--r--Modules/CMakeCSharpInformation.cmake2
-rw-r--r--Modules/ExternalProject.cmake8
-rw-r--r--Modules/FindBoost.cmake14
-rw-r--r--Modules/FindOpenMP.cmake2
-rw-r--r--Modules/FindPatch.cmake68
-rw-r--r--Modules/Platform/Android/Determine-Compiler-NDK.cmake2
-rw-r--r--Source/CMakeVersion.cmake2
-rw-r--r--Source/CTest/cmCTestBuildAndTestHandler.cxx6
-rw-r--r--Source/CTest/cmCTestMemCheckHandler.cxx2
-rw-r--r--Source/cmGlobalVisualStudioGenerator.cxx13
-rw-r--r--Source/cmGlobalVisualStudioGenerator.h3
-rw-r--r--Source/cmLoadCommandCommand.cxx2
-rw-r--r--Source/cmVisualStudio10TargetGenerator.cxx7
-rw-r--r--Source/cmakemain.cxx2
-rw-r--r--Source/kwsys/CMakeLists.txt1
-rw-r--r--Source/kwsys/Directory.cxx4
-rw-r--r--Source/kwsys/SystemTools.cxx8
-rw-r--r--Source/kwsys/testDirectory.cxx79
-rw-r--r--Tests/CMakeLists.txt4
-rw-r--r--Tests/CSharpLinkToCxx/CMakeLists.txt6
-rw-r--r--Tests/CSharpLinkToCxx/cpp_native.cpp10
-rw-r--r--Tests/CSharpLinkToCxx/cpp_native.hpp9
-rw-r--r--Tests/FindPatch/CMakeLists.txt8
-rw-r--r--Tests/FindPatch/Test/CMakeLists.txt77
-rw-r--r--Tests/RunCMake/ExternalProject/MultiCommand-build-stdout.txt15
-rw-r--r--Tests/RunCMake/ExternalProject/MultiCommand.cmake30
-rw-r--r--Tests/RunCMake/ExternalProject/RunCMakeTest.cmake10
31 files changed, 374 insertions, 28 deletions
diff --git a/Copyright.txt b/Copyright.txt
index b7af4c5..978be0a 100644
--- a/Copyright.txt
+++ b/Copyright.txt
@@ -69,6 +69,7 @@ The following individuals and institutions are among the Contributors:
* Matthaeus G. Chajdas
* Matthias Kretz <kretz@kde.org>
* Matthias Maennich <matthias@maennich.net>
+* Michael Stürmer
* Miguel A. Figueroa-Villanueva
* Mike Jackson
* Mike McQuaid <mike@mikemcquaid.com>
diff --git a/Help/manual/cmake-modules.7.rst b/Help/manual/cmake-modules.7.rst
index fa6144c..fdc3597 100644
--- a/Help/manual/cmake-modules.7.rst
+++ b/Help/manual/cmake-modules.7.rst
@@ -182,6 +182,7 @@ All Modules
/module/FindosgWidget
/module/FindPackageHandleStandardArgs
/module/FindPackageMessage
+ /module/FindPatch
/module/FindPerlLibs
/module/FindPerl
/module/FindPHP4
diff --git a/Help/module/FindPatch.rst b/Help/module/FindPatch.rst
new file mode 100644
index 0000000..ba5e910
--- /dev/null
+++ b/Help/module/FindPatch.rst
@@ -0,0 +1 @@
+.. cmake-module:: ../../Modules/FindPatch.cmake
diff --git a/Help/release/dev/find-patch.rst b/Help/release/dev/find-patch.rst
new file mode 100644
index 0000000..d720c81
--- /dev/null
+++ b/Help/release/dev/find-patch.rst
@@ -0,0 +1,5 @@
+find-patch
+----------
+
+* A :module:`FindPatch` module was added to find the ``patch``
+ command-line executable.
diff --git a/Modules/CMakeCSharpInformation.cmake b/Modules/CMakeCSharpInformation.cmake
index d474c29..25f869c 100644
--- a/Modules/CMakeCSharpInformation.cmake
+++ b/Modules/CMakeCSharpInformation.cmake
@@ -43,7 +43,7 @@ endif()
# on the initial values computed in the platform/*.cmake files
# use _INIT variables so that this only happens the first time
# and you can set these flags in the cmake cache
-set(CMAKE_CSharp_FLAGS_INIT "$ENV{CSharpFLAGS} ${CMAKE_CSharp_FLAGS_INIT}")
+set(CMAKE_CSharp_FLAGS_INIT "$ENV{CSFLAGS} ${CMAKE_CSharp_FLAGS_INIT}")
# avoid just having a space as the initial value for the cache
if(CMAKE_CSharp_FLAGS_INIT STREQUAL " ")
set(CMAKE_CSharp_FLAGS_INIT)
diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake
index d92eb5f..912c5ac 100644
--- a/Modules/ExternalProject.cmake
+++ b/Modules/ExternalProject.cmake
@@ -870,6 +870,14 @@ foreach(line IN LISTS lines)
set(_ep_keyword_sep)
elseif("${line}" MATCHES "^ +``([A-Z0-9_]+) [^`]*``$")
set(_ep_key "${CMAKE_MATCH_1}")
+ # COMMAND should never be included as a keyword,
+ # for ExternalProject_Add(), as it is treated as a
+ # special case by argument parsing as an extension
+ # of a previous ..._COMMAND
+ if("x${_ep_key}x" STREQUAL "xCOMMANDx" AND
+ "x${_ep_func}x" STREQUAL "xExternalProject_Addx")
+ continue()
+ endif()
#message(" keyword [${_ep_key}]")
string(APPEND _ep_keywords_${_ep_func}
"${_ep_keyword_sep}${_ep_key}")
diff --git a/Modules/FindBoost.cmake b/Modules/FindBoost.cmake
index b28f2b8..b57d041 100644
--- a/Modules/FindBoost.cmake
+++ b/Modules/FindBoost.cmake
@@ -208,10 +208,6 @@
#
# Set Boost_NO_BOOST_CMAKE to ON to disable the search for boost-cmake.
-# Save project's policies
-cmake_policy(PUSH)
-cmake_policy(SET CMP0057 NEW) # if IN_LIST
-
#-------------------------------------------------------------------------------
# Before we go searching, check whether boost-cmake is available, unless the
# user specifically asked NOT to search for boost-cmake.
@@ -899,7 +895,9 @@ function(_Boost_MISSING_DEPENDENCIES componentvar extravar)
set(_Boost_${uppercomponent}_DEPENDENCIES ${_Boost_${uppercomponent}_DEPENDENCIES} PARENT_SCOPE)
set(_Boost_IMPORTED_TARGETS ${_Boost_IMPORTED_TARGETS} PARENT_SCOPE)
foreach(componentdep ${_Boost_${uppercomponent}_DEPENDENCIES})
- if (NOT ("${componentdep}" IN_LIST _boost_processed_components OR "${componentdep}" IN_LIST _boost_new_components))
+ list(FIND _boost_processed_components "${componentdep}" _boost_component_found)
+ list(FIND _boost_new_components "${componentdep}" _boost_component_new)
+ if (_boost_component_found EQUAL -1 AND _boost_component_new EQUAL -1)
list(APPEND _boost_new_components ${componentdep})
endif()
endforeach()
@@ -1527,7 +1525,8 @@ endif()
_Boost_MISSING_DEPENDENCIES(Boost_FIND_COMPONENTS _Boost_EXTRA_FIND_COMPONENTS)
# If thread is required, get the thread libs as a dependency
-if("thread" IN_LIST Boost_FIND_COMPONENTS)
+list(FIND Boost_FIND_COMPONENTS thread _Boost_THREAD_DEPENDENCY_LIBS)
+if(NOT _Boost_THREAD_DEPENDENCY_LIBS EQUAL -1)
include(CMakeFindDependencyMacro)
find_dependency(Threads)
endif()
@@ -1952,6 +1951,3 @@ list(REMOVE_DUPLICATES _Boost_COMPONENTS_SEARCHED)
list(SORT _Boost_COMPONENTS_SEARCHED)
set(_Boost_COMPONENTS_SEARCHED "${_Boost_COMPONENTS_SEARCHED}"
CACHE INTERNAL "Components requested for this build tree.")
-
-# Restore project's policies
-cmake_policy(POP)
diff --git a/Modules/FindOpenMP.cmake b/Modules/FindOpenMP.cmake
index 7566e4a..8e9ce7a 100644
--- a/Modules/FindOpenMP.cmake
+++ b/Modules/FindOpenMP.cmake
@@ -430,6 +430,8 @@ foreach(LANG IN ITEMS C CXX Fortran)
endif()
endforeach()
+set(OpenMP_FOUND ${OPENMP_FOUND})
+
if(CMAKE_Fortran_COMPILER_LOADED AND OpenMP_Fortran_FOUND)
if(NOT DEFINED OpenMP_Fortran_HAVE_OMPLIB_MODULE)
set(OpenMP_Fortran_HAVE_OMPLIB_MODULE FALSE CACHE BOOL INTERNAL "")
diff --git a/Modules/FindPatch.cmake b/Modules/FindPatch.cmake
new file mode 100644
index 0000000..3ebcae9
--- /dev/null
+++ b/Modules/FindPatch.cmake
@@ -0,0 +1,68 @@
+# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#.rst:
+# FindPatch
+# ---------
+#
+# The module defines the following variables:
+#
+# ``Patch_EXECUTABLE``
+# Path to patch command-line executable.
+# ``Patch_FOUND``
+# True if the patch command-line executable was found.
+#
+# The following :prop_tgt:`IMPORTED` targets are also defined:
+#
+# ``Patch::patch``
+# The command-line executable.
+#
+# Example usage:
+#
+# .. code-block:: cmake
+#
+# find_package(Patch)
+# if(Patch_FOUND)
+# message("Patch found: ${Patch_EXECUTABLE}")
+# endif()
+
+set(_doc "Patch command line executable")
+set(_patch_path )
+
+if(CMAKE_HOST_WIN32)
+ set(_patch_path
+ "$ENV{LOCALAPPDATA}/Programs/Git/bin"
+ "$ENV{LOCALAPPDATA}/Programs/Git/usr/bin"
+ "$ENV{APPDATA}/Programs/Git/bin"
+ "$ENV{APPDATA}/Programs/Git/usr/bin"
+ )
+endif()
+
+# First search the PATH
+find_program(Patch_EXECUTABLE
+ NAME patch
+ PATHS ${_patch_path}
+ DOC ${_doc}
+ )
+
+if(CMAKE_HOST_WIN32)
+ # Now look for installations in Git/ directories under typical installation
+ # prefixes on Windows.
+ find_program(Patch_EXECUTABLE
+ NAMES patch
+ PATH_SUFFIXES Git/usr/bin Git/bin GnuWin32/bin
+ DOC ${_doc}
+ )
+endif()
+
+if(Patch_EXECUTABLE AND NOT TARGET Patch::patch)
+ add_executable(Patch::patch IMPORTED)
+ set_property(TARGET Patch::patch PROPERTY IMPORTED_LOCATION ${Patch_EXECUTABLE})
+endif()
+
+unset(_patch_path)
+unset(_doc)
+
+include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
+find_package_handle_standard_args(Patch
+ REQUIRED_VARS Patch_EXECUTABLE)
diff --git a/Modules/Platform/Android/Determine-Compiler-NDK.cmake b/Modules/Platform/Android/Determine-Compiler-NDK.cmake
index d983dd6..0649925 100644
--- a/Modules/Platform/Android/Determine-Compiler-NDK.cmake
+++ b/Modules/Platform/Android/Determine-Compiler-NDK.cmake
@@ -124,7 +124,7 @@ file(STRINGS "${_ANDROID_TOOL_SETUP_MK}" _ANDROID_TOOL_SETUP REGEX "^(LLVM|TOOLC
unset(_ANDROID_TOOL_SETUP_MK)
set(_ANDROID_TOOL_PREFIX "")
set(_ANDROID_TOOL_NAME_ONLY "")
-set(_ANDROID_TOOL_LLVM_NAME "")
+set(_ANDROID_TOOL_LLVM_NAME "llvm")
set(_ANDROID_TOOL_LLVM_VERS "")
foreach(line IN LISTS _ANDROID_TOOL_SETUP)
if(CMAKE_ANDROID_NDK_TOOLCHAIN_DEBUG)
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index e4b3031..d971faa 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 20170901)
+set(CMake_VERSION_PATCH 20170906)
#set(CMake_VERSION_RC 1)
diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx
index b6c25b8..f9ff2d7 100644
--- a/Source/CTest/cmCTestBuildAndTestHandler.cxx
+++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx
@@ -115,21 +115,21 @@ int cmCTestBuildAndTestHandler::RunCMake(std::string* outstring,
void CMakeMessageCallback(const char* m, const char* /*unused*/,
bool& /*unused*/, void* s)
{
- std::string* out = reinterpret_cast<std::string*>(s);
+ std::string* out = static_cast<std::string*>(s);
*out += m;
*out += "\n";
}
void CMakeProgressCallback(const char* msg, float /*unused*/, void* s)
{
- std::string* out = reinterpret_cast<std::string*>(s);
+ std::string* out = static_cast<std::string*>(s);
*out += msg;
*out += "\n";
}
void CMakeOutputCallback(const char* m, size_t len, void* s)
{
- std::string* out = reinterpret_cast<std::string*>(s);
+ std::string* out = static_cast<std::string*>(s);
out->append(m, len);
}
diff --git a/Source/CTest/cmCTestMemCheckHandler.cxx b/Source/CTest/cmCTestMemCheckHandler.cxx
index 7d11550..3f11543 100644
--- a/Source/CTest/cmCTestMemCheckHandler.cxx
+++ b/Source/CTest/cmCTestMemCheckHandler.cxx
@@ -33,7 +33,7 @@ static CatToErrorType cmCTestMemCheckBoundsChecker[] = {
static void xmlReportError(int line, const char* msg, void* data)
{
- cmCTest* ctest = reinterpret_cast<cmCTest*>(data);
+ cmCTest* ctest = static_cast<cmCTest*>(data);
cmCTestLog(ctest, ERROR_MESSAGE, "Error parsing XML in stream at line "
<< line << ": " << msg << std::endl);
}
diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx
index d7ea35a..ece2a77 100644
--- a/Source/cmGlobalVisualStudioGenerator.cxx
+++ b/Source/cmGlobalVisualStudioGenerator.cxx
@@ -782,6 +782,19 @@ bool cmGlobalVisualStudioGenerator::TargetIsCSharpOnly(
return false;
}
+bool cmGlobalVisualStudioGenerator::TargetCanBeReferenced(
+ cmGeneratorTarget const* gt)
+{
+ if (this->TargetIsCSharpOnly(gt)) {
+ return true;
+ }
+ if (gt->GetType() != cmStateEnums::SHARED_LIBRARY &&
+ gt->GetType() != cmStateEnums::EXECUTABLE) {
+ return false;
+ }
+ return true;
+}
+
bool cmGlobalVisualStudioGenerator::TargetCompare::operator()(
cmGeneratorTarget const* l, cmGeneratorTarget const* r) const
{
diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h
index 04d97c5..399b6e0 100644
--- a/Source/cmGlobalVisualStudioGenerator.h
+++ b/Source/cmGlobalVisualStudioGenerator.h
@@ -85,6 +85,9 @@ public:
// return true if target is C# only
static bool TargetIsCSharpOnly(cmGeneratorTarget const* gt);
+ // return true if target can be referenced by C# targets
+ bool TargetCanBeReferenced(cmGeneratorTarget const* gt);
+
/** Get the top-level registry key for this VS version. */
std::string GetRegistryBase();
diff --git a/Source/cmLoadCommandCommand.cxx b/Source/cmLoadCommandCommand.cxx
index 5ce48e3..a871df9 100644
--- a/Source/cmLoadCommandCommand.cxx
+++ b/Source/cmLoadCommandCommand.cxx
@@ -120,7 +120,7 @@ bool cmLoadedCommand::InitialPass(std::vector<std::string> const& args,
int argc = static_cast<int>(args.size());
char** argv = nullptr;
if (argc) {
- argv = reinterpret_cast<char**>(malloc(argc * sizeof(char*)));
+ argv = static_cast<char**>(malloc(argc * sizeof(char*)));
}
int i;
for (i = 0; i < argc; ++i) {
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 86099eb..7fe2f2a 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -3607,6 +3607,13 @@ void cmVisualStudio10TargetGenerator::WriteProjectReferences()
this->WriteString("<Name>", 3);
(*this->BuildFileStream) << name << "</Name>\n";
this->WriteDotNetReferenceCustomTags(name);
+ if (csproj == this->ProjectType) {
+ if (!static_cast<cmGlobalVisualStudioGenerator*>(this->GlobalGenerator)
+ ->TargetCanBeReferenced(dt)) {
+ this->WriteString(
+ "<ReferenceOutputAssembly>false</ReferenceOutputAssembly>\n", 3);
+ }
+ }
this->WriteString("</ProjectReference>\n", 2);
}
this->WriteString("</ItemGroup>\n", 1);
diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx
index e6f88a7..a60b2b2 100644
--- a/Source/cmakemain.cxx
+++ b/Source/cmakemain.cxx
@@ -102,7 +102,7 @@ static int do_build(int ac, char const* const* av);
static cmMakefile* cmakemainGetMakefile(void* clientdata)
{
- cmake* cm = reinterpret_cast<cmake*>(clientdata);
+ cmake* cm = static_cast<cmake*>(clientdata);
if (cm && cm->GetDebugOutput()) {
cmGlobalGenerator* gg = cm->GetGlobalGenerator();
if (gg) {
diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt
index d7d0c51..5b8ce00 100644
--- a/Source/kwsys/CMakeLists.txt
+++ b/Source/kwsys/CMakeLists.txt
@@ -1032,6 +1032,7 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR)
testSystemTools
testCommandLineArguments
testCommandLineArguments1
+ testDirectory
)
IF(KWSYS_STL_HAS_WSTRING)
SET(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS}
diff --git a/Source/kwsys/Directory.cxx b/Source/kwsys/Directory.cxx
index 5141d45..69068aa 100644
--- a/Source/kwsys/Directory.cxx
+++ b/Source/kwsys/Directory.cxx
@@ -118,8 +118,8 @@ bool Directory::Load(const std::string& name)
struct _wfinddata_t data; // data of current file
// Now put them into the file array
- srchHandle =
- _wfindfirst_func((wchar_t*)Encoding::ToWide(buf).c_str(), &data);
+ srchHandle = _wfindfirst_func(
+ (wchar_t*)Encoding::ToWindowsExtendedPath(buf).c_str(), &data);
delete[] buf;
if (srchHandle == -1) {
diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx
index 11f3b81..560c19c 100644
--- a/Source/kwsys/SystemTools.cxx
+++ b/Source/kwsys/SystemTools.cxx
@@ -2269,11 +2269,7 @@ bool SystemTools::CopyADirectory(const std::string& source,
const std::string& destination, bool always)
{
Directory dir;
-#ifdef _WIN32
- dir.Load(Encoding::ToNarrow(Encoding::ToWindowsExtendedPath(source)));
-#else
dir.Load(source);
-#endif
size_t fileNum;
if (!SystemTools::MakeDirectory(destination)) {
return false;
@@ -2626,11 +2622,7 @@ bool SystemTools::RemoveADirectory(const std::string& source)
}
Directory dir;
-#ifdef _WIN32
- dir.Load(Encoding::ToNarrow(Encoding::ToWindowsExtendedPath(source)));
-#else
dir.Load(source);
-#endif
size_t fileNum;
for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) {
if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), ".") &&
diff --git a/Source/kwsys/testDirectory.cxx b/Source/kwsys/testDirectory.cxx
new file mode 100644
index 0000000..983f2c6
--- /dev/null
+++ b/Source/kwsys/testDirectory.cxx
@@ -0,0 +1,79 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+file Copyright.txt or https://cmake.org/licensing#kwsys for details. */
+#include "kwsysPrivate.h"
+#include KWSYS_HEADER(Directory.hxx)
+#include KWSYS_HEADER(Encoding.hxx)
+#include KWSYS_HEADER(SystemTools.hxx)
+
+// Work-around CMake dependency scanning limitation. This must
+// duplicate the above list of headers.
+#if 0
+#include "Directory.hxx.in"
+#include "Encoding.hxx.in"
+#include "SystemTools.hxx.in"
+#endif
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
+#include <testSystemTools.h>
+
+int _doLongPathTest()
+{
+ using namespace kwsys;
+ static const int LONG_PATH_THRESHOLD = 512;
+ int res = 0;
+ std::string topdir(TEST_SYSTEMTOOLS_BINARY_DIR "/directory_testing/");
+ std::stringstream testpathstrm;
+ std::string testdirpath;
+ std::string extendedtestdirpath;
+
+ testpathstrm << topdir;
+ size_t pathlen = testpathstrm.str().length();
+ testpathstrm.seekp(0, std::ios_base::end);
+ while (pathlen < LONG_PATH_THRESHOLD) {
+ testpathstrm << "0123456789/";
+ pathlen = testpathstrm.str().length();
+ }
+
+ testdirpath = testpathstrm.str();
+#ifdef _WIN32
+ extendedtestdirpath =
+ Encoding::ToNarrow(SystemTools::ConvertToWindowsExtendedPath(testdirpath));
+#else
+ extendedtestdirpath = testdirpath;
+#endif
+
+ if (SystemTools::MakeDirectory(extendedtestdirpath)) {
+ std::ofstream testfile1(
+ (extendedtestdirpath + "longfilepathtest1.txt").c_str());
+ std::ofstream testfile2(
+ (extendedtestdirpath + "longfilepathtest2.txt").c_str());
+ testfile1 << "foo";
+ testfile2 << "bar";
+ testfile1.close();
+ testfile2.close();
+
+ Directory testdir;
+ // Set res to failure if the directory doesn't load
+ res += !testdir.Load(testdirpath);
+ // Increment res failure if the directory appears empty
+ res += testdir.GetNumberOfFiles() == 0;
+ // Increment res failures if the path has changed from
+ // what was provided.
+ res += testdirpath != testdir.GetPath();
+
+ SystemTools::RemoveADirectory(topdir);
+ } else {
+ std::cerr << "Failed to create directory with long path: "
+ << extendedtestdirpath << std::endl;
+ res += 1;
+ }
+ return res;
+}
+
+int testDirectory(int, char* [])
+{
+ return _doLongPathTest();
+}
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 516bc89..3dfc8eb 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -1484,6 +1484,10 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
add_subdirectory(FindPNG)
endif()
+ if(CMake_TEST_FindPatch)
+ add_subdirectory(FindPatch)
+ endif()
+
if(CMake_TEST_FindProtobuf)
add_subdirectory(FindProtobuf)
endif()
diff --git a/Tests/CSharpLinkToCxx/CMakeLists.txt b/Tests/CSharpLinkToCxx/CMakeLists.txt
index c4269e0..153c57c 100644
--- a/Tests/CSharpLinkToCxx/CMakeLists.txt
+++ b/Tests/CSharpLinkToCxx/CMakeLists.txt
@@ -15,3 +15,9 @@ target_compile_options(CLIApp PRIVATE "/clr")
add_executable(CSharpLinkToCxx csharp.cs)
target_link_libraries(CSharpLinkToCxx CLIApp)
+
+# this unmanaged C++ library will be added to the C#/.NET
+# references of CSharpLinkToCxx but it will show a warning
+# because it is unmanaged
+add_library(CppNativeApp SHARED cpp_native.hpp cpp_native.cpp)
+target_link_libraries(CSharpLinkToCxx CppNativeApp)
diff --git a/Tests/CSharpLinkToCxx/cpp_native.cpp b/Tests/CSharpLinkToCxx/cpp_native.cpp
new file mode 100644
index 0000000..dc7670f
--- /dev/null
+++ b/Tests/CSharpLinkToCxx/cpp_native.cpp
@@ -0,0 +1,10 @@
+#include "cpp_native.hpp"
+
+#include <iostream>
+
+namespace CppApp {
+void MyCpp::testMyCpp()
+{
+ std::cout << "#message from CppApp" << std::endl;
+}
+}
diff --git a/Tests/CSharpLinkToCxx/cpp_native.hpp b/Tests/CSharpLinkToCxx/cpp_native.hpp
new file mode 100644
index 0000000..0fa1a3b
--- /dev/null
+++ b/Tests/CSharpLinkToCxx/cpp_native.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+namespace CppApp {
+class MyCpp
+{
+public:
+ void testMyCpp();
+};
+}
diff --git a/Tests/FindPatch/CMakeLists.txt b/Tests/FindPatch/CMakeLists.txt
new file mode 100644
index 0000000..541f5bd
--- /dev/null
+++ b/Tests/FindPatch/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_test(NAME FindPatch.Test COMMAND
+ ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+ --build-and-test
+ "${CMake_SOURCE_DIR}/Tests/FindPatch/Test"
+ "${CMake_BINARY_DIR}/Tests/FindPatch/Test"
+ ${build_generator_args}
+ --build-options ${build_options}
+)
diff --git a/Tests/FindPatch/Test/CMakeLists.txt b/Tests/FindPatch/Test/CMakeLists.txt
new file mode 100644
index 0000000..f4cd621
--- /dev/null
+++ b/Tests/FindPatch/Test/CMakeLists.txt
@@ -0,0 +1,77 @@
+cmake_minimum_required(VERSION 3.8)
+project(TestFindPatch VERSION 1.0 LANGUAGES NONE)
+
+macro(_check)
+ if(NOT EXISTS "${Patch_EXECUTABLE}")
+ message(FATAL_ERROR "Failed to lookup Patch_EXECUTABLE [${Patch_EXECUTABLE}]")
+ endif()
+
+ if(NOT DEFINED PATCH_FOUND)
+ message(FATAL_ERROR "Variable PATCH_FOUND is not defined")
+ endif()
+
+ # Is import target available ?
+ if(NOT TARGET Patch::patch)
+ message(FATAL_ERROR "Target Patch::patch is not defined")
+ endif()
+
+ # Check Patch::patch imported location
+ get_property(_imported_location TARGET Patch::patch PROPERTY IMPORTED_LOCATION)
+ if(NOT "${Patch_EXECUTABLE}" STREQUAL "${_imported_location}")
+ message(FATAL_ERROR "\
+Patch_EXECUTABLE is expected to be equal to Patch::patch IMPORTED_LOCATION
+ Patch_EXECUTABLE [${Patch_EXECUTABLE}]
+ Patch::patch IMPORTED_LOCATION [${_imported_location}]
+")
+ endif()
+
+endmacro()
+
+find_package(Patch REQUIRED)
+_check()
+
+# Calling twice should not fail
+find_package(Patch REQUIRED)
+_check()
+
+add_custom_target(TestPatchVersion ALL
+ COMMAND ${Patch_EXECUTABLE} -v
+ )
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/QUOTE.txt.baseline"
+[=[Because it's there.
+- George Mallory, 1923
+]=]
+)
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/QUOTE.txt" "Because it's there.\n")
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/quote-add-author.patch"
+[=[diff --git a/QUOTE.txt b/QUOTE.txt
+index b36681d..68059b3 100644
+--- a/QUOTE.txt
++++ b/QUOTE.txt
+@@ -1 +1,2 @@
+ Because it's there.
++- George Mallory
+]=]
+)
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/quote-add-date.patch"
+[=[diff --git a/QUOTE.txt b/QUOTE.txt
+index 68059b3..c6f30c2 100644
+--- a/QUOTE.txt
++++ b/QUOTE.txt
+@@ -1,2 +1,2 @@
+ Because it's there.
+-- George Mallory
++- George Mallory, 1923
+]=]
+)
+
+add_custom_target(TestPatch ALL
+ COMMAND ${Patch_EXECUTABLE} -p1 -i quote-add-author.patch
+ COMMAND Patch::patch -p1 -i quote-add-date.patch
+ COMMAND ${CMAKE_COMMAND} -E compare_files QUOTE.txt QUOTE.txt.baseline
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ )
diff --git a/Tests/RunCMake/ExternalProject/MultiCommand-build-stdout.txt b/Tests/RunCMake/ExternalProject/MultiCommand-build-stdout.txt
new file mode 100644
index 0000000..30ebc7d
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/MultiCommand-build-stdout.txt
@@ -0,0 +1,15 @@
+.* *download 1
+.* *download 2
+.* *update 1
+.* *update 2
+.* *patch 1
+.* *patch 2
+.* *configure 1
+.* *configure 2
+.* *build 1
+.* *build 2
+.* *install 1
+.* *install 2
+.* *test 1
+.* *test 2
+.*
diff --git a/Tests/RunCMake/ExternalProject/MultiCommand.cmake b/Tests/RunCMake/ExternalProject/MultiCommand.cmake
new file mode 100644
index 0000000..a8dbfea
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/MultiCommand.cmake
@@ -0,0 +1,30 @@
+cmake_minimum_required(VERSION 3.9)
+
+include(ExternalProject)
+
+# Verify COMMAND keyword is recognised after various *_COMMAND options
+ExternalProject_Add(multiCommand
+ DOWNLOAD_COMMAND "${CMAKE_COMMAND}" -E echo "download 1"
+ COMMAND "${CMAKE_COMMAND}" -E echo "download 2"
+ UPDATE_COMMAND "${CMAKE_COMMAND}" -E echo "update 1"
+ COMMAND "${CMAKE_COMMAND}" -E echo "update 2"
+ PATCH_COMMAND "${CMAKE_COMMAND}" -E echo "patch 1"
+ COMMAND "${CMAKE_COMMAND}" -E echo "patch 2"
+ CONFIGURE_COMMAND "${CMAKE_COMMAND}" -E echo "configure 1"
+ COMMAND "${CMAKE_COMMAND}" -E echo "configure 2"
+ BUILD_COMMAND "${CMAKE_COMMAND}" -E echo "build 1"
+ COMMAND "${CMAKE_COMMAND}" -E echo "build 2"
+ TEST_COMMAND "${CMAKE_COMMAND}" -E echo "test 1"
+ COMMAND "${CMAKE_COMMAND}" -E echo "test 2"
+ INSTALL_COMMAND "${CMAKE_COMMAND}" -E echo "install 1"
+ COMMAND "${CMAKE_COMMAND}" -E echo "install 2"
+)
+
+# Workaround for issue 17229 (missing dependency between update and patch steps)
+ExternalProject_Add_StepTargets(multiCommand NO_DEPENDS update)
+ExternalProject_Add_StepDependencies(multiCommand patch multiCommand-update)
+
+# Force all steps to be re-run by removing timestamps from any previous run
+ExternalProject_Get_Property(multiCommand STAMP_DIR)
+file(REMOVE_RECURSE "${STAMP_DIR}")
+file(MAKE_DIRECTORY "${STAMP_DIR}")
diff --git a/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake b/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake
index 47d6129..994e2aa 100644
--- a/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake
@@ -12,3 +12,13 @@ run_cmake(Add_StepDependencies_iface)
run_cmake(Add_StepDependencies_iface_step)
run_cmake(Add_StepDependencies_no_target)
run_cmake(UsesTerminal)
+
+# Run both cmake and build steps. We always do a clean before the
+# build to ensure that the download step re-runs each time.
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/MultiCommand-build)
+set(RunCMake_TEST_NO_CLEAN 1)
+file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+run_cmake(MultiCommand)
+run_cmake_command(MultiCommand-clean ${CMAKE_COMMAND} --build . --target clean)
+run_cmake_command(MultiCommand-build ${CMAKE_COMMAND} --build .)