summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Help/command/add_custom_command.rst3
-rw-r--r--Help/command/add_custom_target.rst3
-rw-r--r--Help/manual/cmake-variables.7.rst1
-rw-r--r--Help/release/dev/byproducts_make_clean.rst5
-rw-r--r--Help/release/dev/ctest-progress-output.rst5
-rw-r--r--Help/release/dev/custom_command-working_directory-genex.rst5
-rw-r--r--Help/release/dev/vs-global-props-for-all-targets.rst6
-rw-r--r--Help/variable/CMAKE_VS_GLOBALS.rst21
-rw-r--r--Source/CMakeVersion.cmake2
-rw-r--r--Source/CTest/cmCTestBuildAndTestHandler.cxx12
-rw-r--r--Source/CTest/cmCTestMultiProcessHandler.cxx4
-rw-r--r--Source/CTest/cmCTestRunTest.cxx148
-rw-r--r--Source/CTest/cmCTestRunTest.h17
-rw-r--r--Source/cmCTest.cxx80
-rw-r--r--Source/cmCTest.h9
-rw-r--r--Source/cmCustomCommandGenerator.cxx9
-rw-r--r--Source/cmCustomCommandGenerator.h1
-rw-r--r--Source/cmGlobalVisualStudio10Generator.cxx1
-rw-r--r--Source/cmMakefileTargetGenerator.cxx30
-rw-r--r--Source/cmTarget.cxx25
-rw-r--r--Source/cmake.cxx8
-rw-r--r--Source/ctest.cxx1
-rw-r--r--Tests/CMakeLists.txt36
-rw-r--r--Tests/CustomCommandWorkingDirectory/CMakeLists.txt28
-rw-r--r--Tests/RunCMake/Byproducts/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/Byproducts/CleanByproducts.cmake93
-rw-r--r--Tests/RunCMake/Byproducts/RunCMakeTest.cmake58
-rw-r--r--Tests/RunCMake/Byproducts/files.cmake.in2
-rw-r--r--Tests/RunCMake/Byproducts/foo.cpp14
-rw-r--r--Tests/RunCMake/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/VS10Project/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/VS10Project/VsGlobals-check.cmake44
-rw-r--r--Tests/RunCMake/VS10Project/VsGlobals.cmake8
33 files changed, 599 insertions, 87 deletions
diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst
index 5f74c54..71fe494 100644
--- a/Help/command/add_custom_command.rst
+++ b/Help/command/add_custom_command.rst
@@ -182,6 +182,9 @@ The options are:
If it is a relative path it will be interpreted relative to the
build tree directory corresponding to the current source directory.
+ Arguments to ``WORKING_DIRECTORY`` may use
+ :manual:`generator expressions <cmake-generator-expressions(7)>`.
+
``DEPFILE``
Specify a ``.d`` depfile for the :generator:`Ninja` generator.
A ``.d`` file holds dependencies usually emitted by the custom
diff --git a/Help/command/add_custom_target.rst b/Help/command/add_custom_target.rst
index bd61c8b..a6b2f77 100644
--- a/Help/command/add_custom_target.rst
+++ b/Help/command/add_custom_target.rst
@@ -121,3 +121,6 @@ The options are:
Execute the command with the given current working directory.
If it is a relative path it will be interpreted relative to the
build tree directory corresponding to the current source directory.
+
+ Arguments to ``WORKING_DIRECTORY`` may use
+ :manual:`generator expressions <cmake-generator-expressions(7)>`.
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 78353fb..9dd36ed 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -395,6 +395,7 @@ Variables that Control the Build
/variable/CMAKE_TRY_COMPILE_TARGET_TYPE
/variable/CMAKE_USE_RELATIVE_PATHS
/variable/CMAKE_VISIBILITY_INLINES_HIDDEN
+ /variable/CMAKE_VS_GLOBALS
/variable/CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD
/variable/CMAKE_VS_INCLUDE_PACKAGE_TO_DEFAULT_BUILD
/variable/CMAKE_VS_SDK_EXCLUDE_DIRECTORIES
diff --git a/Help/release/dev/byproducts_make_clean.rst b/Help/release/dev/byproducts_make_clean.rst
new file mode 100644
index 0000000..54df77d
--- /dev/null
+++ b/Help/release/dev/byproducts_make_clean.rst
@@ -0,0 +1,5 @@
+byproducts_make_clean
+---------------------
+
+* The :ref:`Makefile Generators` learned to remove custom command and
+ custom target byproducts during ``make clean``.
diff --git a/Help/release/dev/ctest-progress-output.rst b/Help/release/dev/ctest-progress-output.rst
new file mode 100644
index 0000000..a9e946b
--- /dev/null
+++ b/Help/release/dev/ctest-progress-output.rst
@@ -0,0 +1,5 @@
+ctest-progress-output
+---------------------
+
+* :manual:`ctest(1)` gained a ``--progress`` option to enable a live
+ test progress summary when output goes to a terminal.
diff --git a/Help/release/dev/custom_command-working_directory-genex.rst b/Help/release/dev/custom_command-working_directory-genex.rst
new file mode 100644
index 0000000..ae06202
--- /dev/null
+++ b/Help/release/dev/custom_command-working_directory-genex.rst
@@ -0,0 +1,5 @@
+custom_command-working_directory-genex
+--------------------------------------
+
+* The :command:`add_custom_command` and :command:`add_custom_target` commands
+ learned to support generator expressions in ``WORKING_DIRECTORY`` options.
diff --git a/Help/release/dev/vs-global-props-for-all-targets.rst b/Help/release/dev/vs-global-props-for-all-targets.rst
new file mode 100644
index 0000000..647949e
--- /dev/null
+++ b/Help/release/dev/vs-global-props-for-all-targets.rst
@@ -0,0 +1,6 @@
+vs-global-props-for-all-targets
+-------------------------------
+
+* A :variable:`CMAKE_VS_GLOBALS` variable was added to initialize
+ :prop_tgt:`VS_GLOBAL_<variable>` target properties on targets as
+ they are created.
diff --git a/Help/variable/CMAKE_VS_GLOBALS.rst b/Help/variable/CMAKE_VS_GLOBALS.rst
new file mode 100644
index 0000000..83777b6
--- /dev/null
+++ b/Help/variable/CMAKE_VS_GLOBALS.rst
@@ -0,0 +1,21 @@
+CMAKE_VS_GLOBALS
+----------------
+
+List of ``Key=Value`` records to be set per target as target properties
+:prop_tgt:`VS_GLOBAL_<variable>` with ``variable=Key`` and value ``Value``.
+
+For example:
+
+.. code-block:: cmake
+
+ set(CMAKE_VS_GLOBALS
+ "DefaultLanguage=en-US"
+ "MinimumVisualStudioVersion=14.0"
+ )
+
+will set properties ``VS_GLOBAL_DefaultLanguage`` to ``en-US`` and
+``VS_GLOBAL_MinimumVisualStudioVersion`` to ``14.0`` for all targets
+(except for ``INTERFACE`` libraries).
+
+This variable is meant to be set by a
+:variable:`toolchain file <CMAKE_TOOLCHAIN_FILE>`.
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index d9aa818..408cc7d 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 12)
-set(CMake_VERSION_PATCH 20180928)
+set(CMake_VERSION_PATCH 20181001)
#set(CMake_VERSION_RC 1)
diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx
index fccbc95..668a387 100644
--- a/Source/CTest/cmCTestBuildAndTestHandler.cxx
+++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx
@@ -5,6 +5,7 @@
#include "cmCTest.h"
#include "cmCTestTestHandler.h"
#include "cmGlobalGenerator.h"
+#include "cmMakefile.h"
#include "cmSystemTools.h"
#include "cmWorkingDirectory.h"
#include "cmake.h"
@@ -210,9 +211,14 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring)
if (this->BuildNoCMake) {
// Make the generator available for the Build call below.
- cm.SetGlobalGenerator(cm.CreateGlobalGenerator(this->BuildGenerator));
- cm.SetGeneratorPlatform(this->BuildGeneratorPlatform);
- cm.SetGeneratorToolset(this->BuildGeneratorToolset);
+ cmGlobalGenerator* gen = cm.CreateGlobalGenerator(this->BuildGenerator);
+ cm.SetGlobalGenerator(gen);
+ if (!this->BuildGeneratorPlatform.empty()) {
+ cmMakefile mf(gen, cm.GetCurrentSnapshot());
+ if (!gen->SetGeneratorPlatform(this->BuildGeneratorPlatform, &mf)) {
+ return 1;
+ }
+ }
// Load the cache to make CMAKE_MAKE_PROGRAM available.
cm.LoadCache(this->BinaryDir);
diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx
index 3e0c1ac..f026001 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.cxx
+++ b/Source/CTest/cmCTestMultiProcessHandler.cxx
@@ -178,7 +178,7 @@ bool cmCTestMultiProcessHandler::StartTestProcess(int test)
this->Properties[test]->Directory + " : " +
std::strerror(workdir.GetLastResult()));
} else {
- if (testRun->StartTest(this->Total)) {
+ if (testRun->StartTest(this->Completed, this->Total)) {
return true;
}
}
@@ -440,7 +440,7 @@ void cmCTestMultiProcessHandler::FinishTestProcess(cmCTestRunTest* runner,
this->SetStopTimePassed();
}
if (started) {
- if (!this->StopTimePassed && runner->StartAgain()) {
+ if (!this->StopTimePassed && runner->StartAgain(this->Completed)) {
this->Completed--; // remove the completed test because run again
return;
}
diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx
index 23d4616..7f081ef 100644
--- a/Source/CTest/cmCTestRunTest.cxx
+++ b/Source/CTest/cmCTestRunTest.cxx
@@ -181,6 +181,7 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started)
}
}
}
+ std::ostringstream outputStream;
if (res == cmProcess::State::Exited) {
bool success = !forceFail &&
(retVal == 0 ||
@@ -196,36 +197,36 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started)
} else if ((success && !this->TestProperties->WillFail) ||
(!success && this->TestProperties->WillFail)) {
this->TestResult.Status = cmCTestTestHandler::COMPLETED;
- cmCTestLog(this->CTest, HANDLER_OUTPUT, " Passed ");
+ outputStream << " Passed ";
} else {
this->TestResult.Status = cmCTestTestHandler::FAILED;
- cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Failed " << reason);
+ outputStream << "***Failed " << reason;
outputTestErrorsToConsole = this->CTest->OutputTestOutputOnTestFailure;
}
} else if (res == cmProcess::State::Expired) {
- cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Timeout ");
+ outputStream << "***Timeout ";
this->TestResult.Status = cmCTestTestHandler::TIMEOUT;
outputTestErrorsToConsole = this->CTest->OutputTestOutputOnTestFailure;
} else if (res == cmProcess::State::Exception) {
outputTestErrorsToConsole = this->CTest->OutputTestOutputOnTestFailure;
- cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Exception: ");
+ outputStream << "***Exception: ";
this->TestResult.ExceptionStatus =
this->TestProcess->GetExitExceptionString();
switch (this->TestProcess->GetExitException()) {
case cmProcess::Exception::Fault:
- cmCTestLog(this->CTest, HANDLER_OUTPUT, "SegFault");
+ outputStream << "SegFault";
this->TestResult.Status = cmCTestTestHandler::SEGFAULT;
break;
case cmProcess::Exception::Illegal:
- cmCTestLog(this->CTest, HANDLER_OUTPUT, "Illegal");
+ outputStream << "Illegal";
this->TestResult.Status = cmCTestTestHandler::ILLEGAL;
break;
case cmProcess::Exception::Interrupt:
- cmCTestLog(this->CTest, HANDLER_OUTPUT, "Interrupt");
+ outputStream << "Interrupt";
this->TestResult.Status = cmCTestTestHandler::INTERRUPT;
break;
case cmProcess::Exception::Numerical:
- cmCTestLog(this->CTest, HANDLER_OUTPUT, "Numerical");
+ outputStream << "Numerical";
this->TestResult.Status = cmCTestTestHandler::NUMERICAL;
break;
default:
@@ -234,16 +235,41 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started)
this->TestResult.Status = cmCTestTestHandler::OTHER_FAULT;
}
} else if ("Disabled" == this->TestResult.CompletionStatus) {
- cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Not Run (Disabled) ");
+ outputStream << "***Not Run (Disabled) ";
} else // cmProcess::State::Error
{
- cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Not Run ");
+ outputStream << "***Not Run ";
}
passed = this->TestResult.Status == cmCTestTestHandler::COMPLETED;
char buf[1024];
sprintf(buf, "%6.2f sec", this->TestProcess->GetTotalTime().count());
- cmCTestLog(this->CTest, HANDLER_OUTPUT, buf << "\n");
+ outputStream << buf << "\n";
+
+ if (this->CTest->GetTestProgressOutput()) {
+ if (!passed) {
+ // If the test did not pass, reprint test name and error
+ std::string output = GetTestPrefix(completed, total);
+ std::string testName = this->TestProperties->Name;
+ const int maxTestNameWidth = this->CTest->GetMaxTestNameWidth();
+ testName.resize(maxTestNameWidth + 4, '.');
+
+ output += testName;
+ output += outputStream.str();
+ outputStream.str("");
+ outputStream.clear();
+ outputStream << output;
+ cmCTestLog(this->CTest, HANDLER_TEST_PROGRESS_OUTPUT, "\n"); // flush
+ }
+ if (completed == total) {
+ std::string testName =
+ GetTestPrefix(completed, total) + this->TestProperties->Name + "\n";
+ cmCTestLog(this->CTest, HANDLER_TEST_PROGRESS_OUTPUT, testName);
+ }
+ }
+ if (!this->CTest->GetTestProgressOutput() || !passed) {
+ cmCTestLog(this->CTest, HANDLER_OUTPUT, outputStream.str());
+ }
if (outputTestErrorsToConsole) {
cmCTestLog(this->CTest, HANDLER_OUTPUT, this->ProcessOutput << std::endl);
@@ -331,7 +357,7 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started)
return passed || skipped;
}
-bool cmCTestRunTest::StartAgain()
+bool cmCTestRunTest::StartAgain(size_t completed)
{
if (!this->RunAgain) {
return false;
@@ -346,7 +372,7 @@ bool cmCTestRunTest::StartAgain()
return true;
}
- this->StartTest(this->TotalNumberOfTests);
+ this->StartTest(completed, this->TotalNumberOfTests);
return true;
}
@@ -398,12 +424,14 @@ void cmCTestRunTest::StartFailure(std::string const& output)
{
// Still need to log the Start message so the test summary records our
// attempt to start this test
- cmCTestLog(this->CTest, HANDLER_OUTPUT,
- std::setw(2 * getNumWidth(this->TotalNumberOfTests) + 8)
- << "Start "
- << std::setw(getNumWidth(this->TestHandler->GetMaxIndex()))
- << this->TestProperties->Index << ": "
- << this->TestProperties->Name << std::endl);
+ if (!this->CTest->GetTestProgressOutput()) {
+ cmCTestLog(this->CTest, HANDLER_OUTPUT,
+ std::setw(2 * getNumWidth(this->TotalNumberOfTests) + 8)
+ << "Start "
+ << std::setw(getNumWidth(this->TestHandler->GetMaxIndex()))
+ << this->TestProperties->Index << ": "
+ << this->TestProperties->Name << std::endl);
+ }
this->ProcessOutput.clear();
if (!output.empty()) {
@@ -425,16 +453,44 @@ void cmCTestRunTest::StartFailure(std::string const& output)
this->TestProcess = cm::make_unique<cmProcess>(*this);
}
+std::string cmCTestRunTest::GetTestPrefix(size_t completed, size_t total) const
+{
+ std::ostringstream outputStream;
+ outputStream << std::setw(getNumWidth(total)) << completed << "/";
+ outputStream << std::setw(getNumWidth(total)) << total << " ";
+
+ if (this->TestHandler->MemCheck) {
+ outputStream << "MemCheck";
+ } else {
+ outputStream << "Test";
+ }
+
+ std::ostringstream indexStr;
+ indexStr << " #" << this->Index << ":";
+ outputStream << std::setw(3 + getNumWidth(this->TestHandler->GetMaxIndex()))
+ << indexStr.str();
+ outputStream << " ";
+
+ return outputStream.str();
+}
+
// Starts the execution of a test. Returns once it has started
-bool cmCTestRunTest::StartTest(size_t total)
+bool cmCTestRunTest::StartTest(size_t completed, size_t total)
{
this->TotalNumberOfTests = total; // save for rerun case
- cmCTestLog(this->CTest, HANDLER_OUTPUT,
- std::setw(2 * getNumWidth(total) + 8)
- << "Start "
- << std::setw(getNumWidth(this->TestHandler->GetMaxIndex()))
- << this->TestProperties->Index << ": "
- << this->TestProperties->Name << std::endl);
+ if (!this->CTest->GetTestProgressOutput()) {
+ cmCTestLog(this->CTest, HANDLER_OUTPUT,
+ std::setw(2 * getNumWidth(total) + 8)
+ << "Start "
+ << std::setw(getNumWidth(this->TestHandler->GetMaxIndex()))
+ << this->TestProperties->Index << ": "
+ << this->TestProperties->Name << std::endl);
+ } else {
+ std::string testName =
+ GetTestPrefix(completed, total) + this->TestProperties->Name + "\n";
+ cmCTestLog(this->CTest, HANDLER_TEST_PROGRESS_OUTPUT, testName);
+ }
+
this->ProcessOutput.clear();
// Return immediately if test is disabled
@@ -689,44 +745,41 @@ bool cmCTestRunTest::ForkProcess(cmDuration testTimeOut, bool explicitTimeout,
void cmCTestRunTest::WriteLogOutputTop(size_t completed, size_t total)
{
- // if this is the last or only run of this test
- // then print out completed / total
+ std::ostringstream outputStream;
+
+ // If this is the last or only run of this test, or progress output is
+ // requested, then print out completed / total.
// Only issue is if a test fails and we are running until fail
// then it will never print out the completed / total, same would
// got for run until pass. Trick is when this is called we don't
// yet know if we are passing or failing.
- if (this->NumberOfRunsLeft == 1) {
- cmCTestLog(this->CTest, HANDLER_OUTPUT,
- std::setw(getNumWidth(total)) << completed << "/");
- cmCTestLog(this->CTest, HANDLER_OUTPUT,
- std::setw(getNumWidth(total)) << total << " ");
+ if (this->NumberOfRunsLeft == 1 || this->CTest->GetTestProgressOutput()) {
+ outputStream << std::setw(getNumWidth(total)) << completed << "/";
+ outputStream << std::setw(getNumWidth(total)) << total << " ";
}
// if this is one of several runs of a test just print blank space
// to keep things neat
else {
- cmCTestLog(this->CTest, HANDLER_OUTPUT,
- std::setw(getNumWidth(total)) << " "
- << " ");
- cmCTestLog(this->CTest, HANDLER_OUTPUT,
- std::setw(getNumWidth(total)) << " "
- << " ");
+ outputStream << std::setw(getNumWidth(total)) << " ";
+ outputStream << std::setw(getNumWidth(total)) << " ";
}
if (this->TestHandler->MemCheck) {
- cmCTestLog(this->CTest, HANDLER_OUTPUT, "MemCheck");
+ outputStream << "MemCheck";
} else {
- cmCTestLog(this->CTest, HANDLER_OUTPUT, "Test");
+ outputStream << "Test";
}
std::ostringstream indexStr;
indexStr << " #" << this->Index << ":";
- cmCTestLog(this->CTest, HANDLER_OUTPUT,
- std::setw(3 + getNumWidth(this->TestHandler->GetMaxIndex()))
- << indexStr.str());
- cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
+ outputStream << std::setw(3 + getNumWidth(this->TestHandler->GetMaxIndex()))
+ << indexStr.str();
+ outputStream << " ";
+
const int maxTestNameWidth = this->CTest->GetMaxTestNameWidth();
std::string outname = this->TestProperties->Name + " ";
outname.resize(maxTestNameWidth + 4, '.');
+ outputStream << outname;
*this->TestHandler->LogFile << this->TestProperties->Index << "/"
<< this->TestHandler->TotalNumberOfTests
@@ -754,7 +807,10 @@ void cmCTestRunTest::WriteLogOutputTop(size_t completed, size_t total)
*this->TestHandler->LogFile << this->ProcessOutput << "<end of output>"
<< std::endl;
- cmCTestLog(this->CTest, HANDLER_OUTPUT, outname.c_str());
+ if (!this->CTest->GetTestProgressOutput()) {
+ cmCTestLog(this->CTest, HANDLER_OUTPUT, outputStream.str());
+ }
+
cmCTestLog(this->CTest, DEBUG,
"Testing " << this->TestProperties->Name << " ... ");
}
diff --git a/Source/CTest/cmCTestRunTest.h b/Source/CTest/cmCTestRunTest.h
index 7e80157..48a5064 100644
--- a/Source/CTest/cmCTestRunTest.h
+++ b/Source/CTest/cmCTestRunTest.h
@@ -5,6 +5,7 @@
#include "cmConfigure.h" // IWYU pragma: keep
+#include <cmath>
#include <set>
#include <stddef.h>
#include <string>
@@ -64,7 +65,7 @@ public:
void CompressOutput();
// launch the test process, return whether it started correctly
- bool StartTest(size_t total);
+ bool StartTest(size_t completed, size_t total);
// capture and report the test results
bool EndTest(size_t completed, size_t total, bool started);
// Called by ctest -N to log the command string
@@ -72,7 +73,7 @@ public:
void ComputeWeightedCost();
- bool StartAgain();
+ bool StartAgain(size_t completed);
void StartFailure(std::string const& output);
@@ -93,6 +94,9 @@ private:
// Run post processing of the process output for MemCheck
void MemCheckPostProcess();
+ // Returns "completed/total Test #Index: "
+ std::string GetTestPrefix(size_t completed, size_t total) const;
+
cmCTestTestHandler::cmCTestTestProperties* TestProperties;
bool TimeoutIsForStopTime = false;
// Pointer back to the "parent"; the handler that invoked this test run
@@ -118,14 +122,7 @@ private:
inline int getNumWidth(size_t n)
{
- int numWidth = 1;
- if (n >= 10) {
- numWidth = 2;
- }
- if (n >= 100) {
- numWidth = 3;
- }
- return numWidth;
+ return static_cast<int>(std::log10(n)) + 1;
}
#endif
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index 6639787..908eea1 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -24,6 +24,11 @@
#include <time.h>
#include <utility>
#include <vector>
+#if defined(_WIN32)
+# include <windows.h> // IWYU pragma: keep
+#else
+# include <unistd.h> // IWYU pragma: keep
+#endif
#include "cmAlgorithms.h"
#include "cmCTestBuildAndTestHandler.h"
@@ -263,6 +268,8 @@ cmCTest::cmCTest()
this->Failover = false;
this->ForceNewCTestProcess = false;
this->TomorrowTag = false;
+ this->TestProgressOutput = false;
+ this->FlushTestProgressLine = false;
this->Verbose = false;
this->Debug = false;
@@ -290,10 +297,16 @@ cmCTest::cmCTest()
this->OutputTestOutputOnTestFailure = false;
this->RepeatTests = 1; // default to run each test once
this->RepeatUntilFail = false;
- std::string outOnFail;
- if (cmSystemTools::GetEnv("CTEST_OUTPUT_ON_FAILURE", outOnFail)) {
- this->OutputTestOutputOnTestFailure = !cmSystemTools::IsOff(outOnFail);
+
+ std::string envValue;
+ if (cmSystemTools::GetEnv("CTEST_OUTPUT_ON_FAILURE", envValue)) {
+ this->OutputTestOutputOnTestFailure = !cmSystemTools::IsOff(envValue);
+ }
+ envValue.clear();
+ if (cmSystemTools::GetEnv("CTEST_PROGRESS_OUTPUT", envValue)) {
+ this->TestProgressOutput = !cmSystemTools::IsOff(envValue);
}
+
this->InitStreams();
this->Parts[PartStart].SetName("Start");
@@ -1875,6 +1888,9 @@ bool cmCTest::HandleCommandLineArguments(size_t& i,
if (this->CheckArgument(arg, "-Q", "--quiet")) {
this->Quiet = true;
}
+ if (this->CheckArgument(arg, "--progress")) {
+ this->TestProgressOutput = true;
+ }
if (this->CheckArgument(arg, "-V", "--verbose")) {
this->Verbose = true;
}
@@ -2038,6 +2054,23 @@ bool cmCTest::HandleCommandLineArguments(size_t& i,
return true;
}
+bool cmCTest::ProgressOutputSupportedByConsole() const
+{
+#if defined(_WIN32)
+ // On Windows we need a console buffer.
+ void* console = GetStdHandle(STD_OUTPUT_HANDLE);
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ return GetConsoleScreenBufferInfo(console, &csbi);
+#else
+ // On UNIX we need a non-dumb tty.
+ std::string term_env_variable;
+ if (cmSystemTools::GetEnv("TERM", term_env_variable)) {
+ return isatty(1) && term_env_variable != "dumb";
+ }
+#endif
+ return false;
+}
+
// handle the -S -SR and -SP arguments
void cmCTest::HandleScriptArguments(size_t& i, std::vector<std::string>& args,
bool& SRArgumentSpecified)
@@ -2192,6 +2225,18 @@ int cmCTest::Run(std::vector<std::string>& args, std::string* output)
}
}
+ // TestProgressOutput only supported if console supports it and not logging
+ // to a file
+ this->TestProgressOutput = this->TestProgressOutput &&
+ !this->OutputLogFile && this->ProgressOutputSupportedByConsole();
+#ifdef _WIN32
+ if (this->TestProgressOutput) {
+ // Disable output line buffering so we can print content without
+ // a newline.
+ std::setvbuf(stdout, nullptr, _IONBF, 0);
+ }
+#endif
+
// now what should cmake do? if --build-and-test was specified then
// we run the build and test handler and return
if (cmakeAndTest) {
@@ -2761,6 +2806,7 @@ static const char* cmCTestStringLogType[] = { "DEBUG",
"OUTPUT",
"HANDLER_OUTPUT",
"HANDLER_PROGRESS_OUTPUT",
+ "HANDLER_TEST_PROGRESS_OUTPUT",
"HANDLER_VERBOSE_OUTPUT",
"WARNING",
"ERROR_MESSAGE",
@@ -2821,6 +2867,34 @@ void cmCTest::Log(int logType, const char* file, int line, const char* msg,
if (!this->Quiet) {
std::ostream& out = *this->StreamOut;
std::ostream& err = *this->StreamErr;
+
+ if (logType == HANDLER_TEST_PROGRESS_OUTPUT) {
+ if (this->TestProgressOutput) {
+ cmCTestLogOutputFileLine(out);
+ if (this->FlushTestProgressLine) {
+ printf("\r");
+ this->FlushTestProgressLine = false;
+ out.flush();
+ }
+
+ std::string msg_str{ msg };
+ auto const lineBreakIt = msg_str.find('\n');
+ if (lineBreakIt != std::string::npos) {
+ this->FlushTestProgressLine = true;
+ msg_str.erase(std::remove(msg_str.begin(), msg_str.end(), '\n'),
+ msg_str.end());
+ }
+
+ out << msg_str;
+#ifndef _WIN32
+ printf("\x1B[K"); // move caret to end
+#endif
+ out.flush();
+ return;
+ }
+ logType = HANDLER_OUTPUT;
+ }
+
switch (logType) {
case DEBUG:
if (this->Debug) {
diff --git a/Source/cmCTest.h b/Source/cmCTest.h
index c6ece98..345b538 100644
--- a/Source/cmCTest.h
+++ b/Source/cmCTest.h
@@ -390,6 +390,7 @@ public:
OUTPUT,
HANDLER_OUTPUT,
HANDLER_PROGRESS_OUTPUT,
+ HANDLER_TEST_PROGRESS_OUTPUT,
HANDLER_VERBOSE_OUTPUT,
WARNING,
ERROR_MESSAGE,
@@ -429,6 +430,8 @@ public:
void SetFailover(bool failover) { this->Failover = failover; }
bool GetFailover() { return this->Failover; }
+ bool GetTestProgressOutput() const { return this->TestProgressOutput; }
+
bool GetVerbose() { return this->Verbose; }
bool GetExtraVerbose() { return this->ExtraVerbose; }
@@ -467,6 +470,7 @@ private:
std::string ConfigType;
std::string ScheduleType;
std::chrono::system_clock::time_point StopTime;
+ bool TestProgressOutput;
bool Verbose;
bool ExtraVerbose;
bool ProduceXML;
@@ -476,6 +480,8 @@ private:
bool PrintLabels;
bool Failover;
+ bool FlushTestProgressLine;
+
bool ForceNewCTestProcess;
bool RunConfigurationScript;
@@ -561,6 +567,9 @@ private:
bool HandleCommandLineArguments(size_t& i, std::vector<std::string>& args,
std::string& errormsg);
+ /** returns true iff the console supports progress output */
+ bool ProgressOutputSupportedByConsole() const;
+
/** handle the -S -SP and -SR arguments */
void HandleScriptArguments(size_t& i, std::vector<std::string>& args,
bool& SRArgumentSpecified);
diff --git a/Source/cmCustomCommandGenerator.cxx b/Source/cmCustomCommandGenerator.cxx
index 6c9f9d6..5bbae17 100644
--- a/Source/cmCustomCommandGenerator.cxx
+++ b/Source/cmCustomCommandGenerator.cxx
@@ -64,6 +64,13 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc,
}
this->Depends.insert(this->Depends.end(), result.begin(), result.end());
}
+
+ const std::string& workingdirectory = this->CC.GetWorkingDirectory();
+ if (!workingdirectory.empty()) {
+ std::unique_ptr<cmCompiledGeneratorExpression> cge =
+ this->GE->Parse(workingdirectory);
+ this->WorkingDirectory = cge->Evaluate(this->LG, this->Config);
+ }
}
cmCustomCommandGenerator::~cmCustomCommandGenerator()
@@ -186,7 +193,7 @@ const char* cmCustomCommandGenerator::GetComment() const
std::string cmCustomCommandGenerator::GetWorkingDirectory() const
{
- return this->CC.GetWorkingDirectory();
+ return this->WorkingDirectory;
}
std::vector<std::string> const& cmCustomCommandGenerator::GetOutputs() const
diff --git a/Source/cmCustomCommandGenerator.h b/Source/cmCustomCommandGenerator.h
index 34fd653..b7e2a39 100644
--- a/Source/cmCustomCommandGenerator.h
+++ b/Source/cmCustomCommandGenerator.h
@@ -23,6 +23,7 @@ class cmCustomCommandGenerator
cmGeneratorExpression* GE;
cmCustomCommandLines CommandLines;
std::vector<std::string> Depends;
+ std::string WorkingDirectory;
const char* GetCrossCompilingEmulator(unsigned int c) const;
const char* GetArgv0Location(unsigned int c) const;
diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx
index 5ea323a..82fcaad 100644
--- a/Source/cmGlobalVisualStudio10Generator.cxx
+++ b/Source/cmGlobalVisualStudio10Generator.cxx
@@ -938,6 +938,7 @@ void cmGlobalVisualStudio10Generator::GenerateBuildCommand(
configArg += "Debug";
}
makeCommand.push_back(configArg);
+ makeCommand.push_back("/p:Platform=" + this->GetPlatformName());
makeCommand.push_back(std::string("/p:VisualStudioVersion=") +
this->GetIDEVersion());
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index c8dc392..f423560 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -181,6 +181,36 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules()
this->LocalGenerator->MaybeConvertToRelativePath(currentBinDir,
output));
}
+ const std::vector<std::string>& byproducts = ccg.GetByproducts();
+ for (std::string const& byproduct : byproducts) {
+ this->CleanFiles.push_back(
+ this->LocalGenerator->MaybeConvertToRelativePath(currentBinDir,
+ byproduct));
+ }
+ }
+ }
+
+ // Add byproducts from build events to the clean rules
+ if (clean) {
+ std::vector<cmCustomCommand> buildEventCommands =
+ this->GeneratorTarget->GetPreBuildCommands();
+
+ buildEventCommands.insert(
+ buildEventCommands.end(),
+ this->GeneratorTarget->GetPreLinkCommands().begin(),
+ this->GeneratorTarget->GetPreLinkCommands().end());
+ buildEventCommands.insert(
+ buildEventCommands.end(),
+ this->GeneratorTarget->GetPostBuildCommands().begin(),
+ this->GeneratorTarget->GetPostBuildCommands().end());
+
+ for (const auto& be : buildEventCommands) {
+ const std::vector<std::string>& byproducts = be.GetByproducts();
+ for (std::string const& byproduct : byproducts) {
+ this->CleanFiles.push_back(
+ this->LocalGenerator->MaybeConvertToRelativePath(currentBinDir,
+ byproduct));
+ }
}
}
std::vector<cmSourceFile const*> headerSources;
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index c5295f2..f0d6519 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -454,6 +454,31 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
if (this->TargetTypeValue <= cmStateEnums::UTILITY) {
this->SetPropertyDefault("DOTNET_TARGET_FRAMEWORK_VERSION", nullptr);
}
+
+ if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
+ this->GetType() != cmStateEnums::UTILITY) {
+
+ // check for "CMAKE_VS_GLOBALS" variable and set up target properties
+ // if any
+ const char* globals = mf->GetDefinition("CMAKE_VS_GLOBALS");
+ if (globals) {
+ const std::string genName = mf->GetGlobalGenerator()->GetName();
+ if (cmHasLiteralPrefix(genName, "Visual Studio")) {
+ std::vector<std::string> props;
+ cmSystemTools::ExpandListArgument(globals, props);
+ const std::string vsGlobal = "VS_GLOBAL_";
+ for (const std::string& i : props) {
+ // split NAME=VALUE
+ const std::string::size_type assignment = i.find('=');
+ if (assignment != std::string::npos) {
+ const std::string propName = vsGlobal + i.substr(0, assignment);
+ const std::string propValue = i.substr(assignment + 1);
+ this->SetPropertyDefault(propName, propValue.c_str());
+ }
+ }
+ }
+ }
+ }
}
cmGlobalGenerator* cmTarget::GetGlobalGenerator() const
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index c26a380..889a5fb 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -2467,6 +2467,14 @@ int cmake::Build(int jobs, const std::string& dir, const std::string& target,
return 1;
}
}
+ const char* cachedGeneratorPlatform =
+ this->State->GetCacheEntryValue("CMAKE_GENERATOR_PLATFORM");
+ if (cachedGeneratorPlatform) {
+ cmMakefile mf(gen, this->GetCurrentSnapshot());
+ if (!gen->SetGeneratorPlatform(cachedGeneratorPlatform, &mf)) {
+ return 1;
+ }
+ }
std::string output;
std::string projName;
const char* cachedProjectName =
diff --git a/Source/ctest.cxx b/Source/ctest.cxx
index b338dea..ca8a776 100644
--- a/Source/ctest.cxx
+++ b/Source/ctest.cxx
@@ -27,6 +27,7 @@ static const char* cmDocumentationUsage[][2] = { { nullptr,
static const char* cmDocumentationOptions[][2] = {
{ "-C <cfg>, --build-config <cfg>", "Choose configuration to test." },
+ { "--progress", "Enable short progress output from tests." },
{ "-V,--verbose", "Enable verbose output from tests." },
{ "-VV,--extra-verbose", "Enable more verbose output from tests." },
{ "--debug", "Displaying more verbose internals of CTest." },
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 83349e3..0de6c41 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -2128,12 +2128,13 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
get_filename_component(ntver "[HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion;CurrentVersion]" NAME)
if(WIN32 AND ntver VERSION_GREATER 6.1) # Windows >= 8.0
- macro(add_test_VSWinStorePhone name generator systemName systemVersion)
+ macro(add_test_VSWinStorePhone name generator systemName systemVersion architecture)
add_test(NAME VSWinStorePhone.${name} COMMAND ${CMAKE_CTEST_COMMAND}
--build-and-test
"${CMake_SOURCE_DIR}/Tests/VSWinStorePhone"
"${CMake_BINARY_DIR}/Tests/VSWinStorePhone/${name}"
--build-generator "${generator}"
+ --build-generator-platform "${architecture}"
--build-project VSWinStorePhone
--build-config $<CONFIGURATION>
--build-options -DCMAKE_SYSTEM_NAME=${systemName}
@@ -2143,14 +2144,14 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
endmacro()
if(vs11 AND ws80)
- add_test_VSWinStorePhone(vs11-store80-X86 "Visual Studio 11 2012" WindowsStore 8.0)
- add_test_VSWinStorePhone(vs11-store80-ARM "Visual Studio 11 2012 ARM" WindowsStore 8.0)
- add_test_VSWinStorePhone(vs11-store80-X64 "Visual Studio 11 2012 Win64" WindowsStore 8.0)
+ add_test_VSWinStorePhone(vs11-store80-X86 "Visual Studio 11 2012" WindowsStore 8.0 Win32)
+ add_test_VSWinStorePhone(vs11-store80-ARM "Visual Studio 11 2012" WindowsStore 8.0 ARM)
+ add_test_VSWinStorePhone(vs11-store80-X64 "Visual Studio 11 2012" WindowsStore 8.0 x64)
endif()
if(vs12 AND ws81)
- add_test_VSWinStorePhone(vs12-store81-X86 "Visual Studio 12 2013" WindowsStore 8.1)
- add_test_VSWinStorePhone(vs12-store81-ARM "Visual Studio 12 2013 ARM" WindowsStore 8.1)
- add_test_VSWinStorePhone(vs12-store81-X64 "Visual Studio 12 2013 Win64" WindowsStore 8.1)
+ add_test_VSWinStorePhone(vs12-store81-X86 "Visual Studio 12 2013" WindowsStore 8.1 Win32)
+ add_test_VSWinStorePhone(vs12-store81-ARM "Visual Studio 12 2013" WindowsStore 8.1 ARM)
+ add_test_VSWinStorePhone(vs12-store81-X64 "Visual Studio 12 2013" WindowsStore 8.1 x64)
add_test(NAME VSXaml COMMAND ${CMAKE_CTEST_COMMAND}
--build-and-test
@@ -2164,22 +2165,23 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
)
endif()
if(CMake_TEST_VSWinStorePhone_VS_2017 AND ws10_0)
- add_test_VSWinStorePhone(vs15-store10_0-X86 "Visual Studio 15 2017" WindowsStore 10.0)
- add_test_VSWinStorePhone(vs15-store10_0-ARM "Visual Studio 15 2017 ARM" WindowsStore 10.0)
- add_test_VSWinStorePhone(vs15-store10_0-X64 "Visual Studio 15 2017 Win64" WindowsStore 10.0)
+ add_test_VSWinStorePhone(vs15-store10_0-X86 "Visual Studio 15 2017" WindowsStore 10.0 Win32)
+ add_test_VSWinStorePhone(vs15-store10_0-ARM "Visual Studio 15 2017" WindowsStore 10.0 ARM)
+ add_test_VSWinStorePhone(vs15-store10_0-X64 "Visual Studio 15 2017" WindowsStore 10.0 x64)
+ add_test_VSWinStorePhone(vs15-store10_0-ARM64 "Visual Studio 15 2017" WindowsStore 10.0 ARM64)
endif()
if(vs14 AND ws10_0)
- add_test_VSWinStorePhone(vs14-store10_0-X86 "Visual Studio 14 2015" WindowsStore 10.0)
- add_test_VSWinStorePhone(vs14-store10_0-ARM "Visual Studio 14 2015 ARM" WindowsStore 10.0)
- add_test_VSWinStorePhone(vs14-store10_0-X64 "Visual Studio 14 2015 Win64" WindowsStore 10.0)
+ add_test_VSWinStorePhone(vs14-store10_0-X86 "Visual Studio 14 2015" WindowsStore 10.0 Win32)
+ add_test_VSWinStorePhone(vs14-store10_0-ARM "Visual Studio 14 2015" WindowsStore 10.0 ARM)
+ add_test_VSWinStorePhone(vs14-store10_0-X64 "Visual Studio 14 2015" WindowsStore 10.0 x64)
endif()
if(vs11 AND wp80)
- add_test_VSWinStorePhone(vs11-phone80-X86 "Visual Studio 11 2012" WindowsPhone 8.0)
- add_test_VSWinStorePhone(vs11-phone80-ARM "Visual Studio 11 2012 ARM" WindowsPhone 8.0)
+ add_test_VSWinStorePhone(vs11-phone80-X86 "Visual Studio 11 2012" WindowsPhone 8.0 Win32)
+ add_test_VSWinStorePhone(vs11-phone80-ARM "Visual Studio 11 2012" WindowsPhone 8.0 ARM)
endif()
if(vs12 AND wp81)
- add_test_VSWinStorePhone(vs12-phone81-X86 "Visual Studio 12 2013" WindowsPhone 8.1)
- add_test_VSWinStorePhone(vs12-phone81-ARM "Visual Studio 12 2013 ARM" WindowsPhone 8.1)
+ add_test_VSWinStorePhone(vs12-phone81-X86 "Visual Studio 12 2013" WindowsPhone 8.1 Win32)
+ add_test_VSWinStorePhone(vs12-phone81-ARM "Visual Studio 12 2013" WindowsPhone 8.1 ARM)
endif()
endif()
diff --git a/Tests/CustomCommandWorkingDirectory/CMakeLists.txt b/Tests/CustomCommandWorkingDirectory/CMakeLists.txt
index 4975feb..2e12a78 100644
--- a/Tests/CustomCommandWorkingDirectory/CMakeLists.txt
+++ b/Tests/CustomCommandWorkingDirectory/CMakeLists.txt
@@ -9,17 +9,17 @@ add_custom_command(
)
set_source_files_properties(
- "${TestWorkingDir_BINARY_DIR}/customTarget.c"
+ "${TestWorkingDir_BINARY_DIR}/customTarget1.c"
"${TestWorkingDir_BINARY_DIR}/customTarget2.c"
PROPERTIES GENERATED 1)
add_executable(working "${TestWorkingDir_BINARY_DIR}/working.c"
- "${TestWorkingDir_BINARY_DIR}/customTarget.c")
+ "${TestWorkingDir_BINARY_DIR}/customTarget1.c")
add_custom_target(
Custom ALL
- COMMAND "${CMAKE_COMMAND}" -E copy_if_different ./customTarget.c "${TestWorkingDir_BINARY_DIR}/customTarget.c"
- BYPRODUCTS "${TestWorkingDir_BINARY_DIR}/customTarget.c"
+ COMMAND "${CMAKE_COMMAND}" -E copy_if_different ./customTarget.c "${TestWorkingDir_BINARY_DIR}/customTarget1.c"
+ BYPRODUCTS "${TestWorkingDir_BINARY_DIR}/customTarget1.c"
WORKING_DIRECTORY "${TestWorkingDir_SOURCE_DIR}"
)
@@ -42,3 +42,23 @@ add_custom_target(
)
add_dependencies(working2 Custom2)
+
+file(MAKE_DIRECTORY ${TestWorkingDir_BINARY_DIR}/genex)
+add_custom_command(
+ OUTPUT "${TestWorkingDir_BINARY_DIR}/genex/working.c"
+ COMMAND "${CMAKE_COMMAND}" -E copy "${TestWorkingDir_SOURCE_DIR}/working.c.in" "${TestWorkingDir_BINARY_DIR}/genex/working.c"
+ WORKING_DIRECTORY "${TestWorkingDir_BINARY_DIR}/$<1:genex>/"
+ COMMENT "custom command"
+)
+
+add_executable(workinggenex "${TestWorkingDir_BINARY_DIR}/genex/working.c"
+ "${TestWorkingDir_BINARY_DIR}/genex/customTarget.c")
+
+add_custom_target(
+ CustomGenex ALL
+ COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${TestWorkingDir_SOURCE_DIR}/customTarget.c" "${TestWorkingDir_BINARY_DIR}/genex/customTarget.c"
+ BYPRODUCTS "${TestWorkingDir_BINARY_DIR}/genex/customTarget.c"
+ WORKING_DIRECTORY "${TestWorkingDir_BINARY_DIR}/$<1:genex>/"
+)
+
+add_dependencies(workinggenex CustomGenex)
diff --git a/Tests/RunCMake/Byproducts/CMakeLists.txt b/Tests/RunCMake/Byproducts/CMakeLists.txt
new file mode 100644
index 0000000..bf2ef15
--- /dev/null
+++ b/Tests/RunCMake/Byproducts/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.10)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/Byproducts/CleanByproducts.cmake b/Tests/RunCMake/Byproducts/CleanByproducts.cmake
new file mode 100644
index 0000000..85d9582
--- /dev/null
+++ b/Tests/RunCMake/Byproducts/CleanByproducts.cmake
@@ -0,0 +1,93 @@
+cmake_minimum_required(VERSION 3.10)
+project(CleanByproducts)
+
+# Configurable parameters
+set(TEST_CLEAN_NO_CUSTOM FALSE CACHE BOOL "Value for the CLEAN_NO_CUSTOM PROPERTY")
+set(TEST_BUILD_EVENTS TRUE CACHE BOOL "Create byproducts with build events")
+set(TEST_CUSTOM_TARGET TRUE CACHE BOOL "Create a byproduct with a custom target")
+set(TEST_CUSTOM_COMMAND TRUE CACHE BOOL "Create a byproduct with a custom command")
+
+set_property(DIRECTORY PROPERTY CLEAN_NO_CUSTOM ${TEST_CLEAN_NO_CUSTOM})
+
+macro(add_build_event)
+ set(oneValueArgs EVENT)
+
+ cmake_parse_Arguments(ABE "" "${oneValueArgs}" "" ${ARGN})
+
+ # Create two byproducts and only declare one
+ add_custom_command(TARGET foo
+ ${ABE_EVENT}
+ COMMAND ${CMAKE_COMMAND} -E touch foo.${ABE_EVENT}
+ COMMAND ${CMAKE_COMMAND} -E touch foo.${ABE_EVENT}.notdeclared
+ COMMENT "Creating byproducts with ${ABE_EVENT}"
+ BYPRODUCTS foo.${ABE_EVENT}
+ )
+
+ # The nondeclared byproduct should always be present
+ list(APPEND EXPECTED_PRESENT foo.${ABE_EVENT}.notdeclared)
+
+ # If CLEAN_NO_CUSTOM is set, the declared byproduct should be present
+ if(TEST_CLEAN_NO_CUSTOM)
+ list(APPEND EXPECTED_PRESENT foo.${ABE_EVENT})
+ else()
+ list(APPEND EXPECTED_DELETED foo.${ABE_EVENT})
+ endif()
+endmacro()
+
+add_executable(foo foo.cpp)
+
+# Test build events
+if(TEST_BUILD_EVENTS)
+ add_build_event(EVENT "PRE_BUILD" ENABLE ${TEST_PRE_BUILD})
+ add_build_event(EVENT "PRE_LINK" ENABLE ${TEST_PRE_LINK})
+ add_build_event(EVENT "POST_BUILD" ENABLE ${TEST_POST_BUILD})
+endif()
+
+# Custom command that generates byproducts
+if(TEST_CUSTOM_COMMAND)
+ file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/bar.cpp.in "void bar() {}\n")
+ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/bar.cpp
+ COMMAND ${CMAKE_COMMAND} -E touch foo.customcommand
+ COMMAND ${CMAKE_COMMAND} -E touch foo.customcommand.notdeclared
+ COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/bar.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/bar.cpp
+ BYPRODUCTS foo.customcommand
+ COMMENT "Creating byproducts with a custom command"
+ )
+
+ # The nondeclared byproduct should always be present
+ list(APPEND EXPECTED_PRESENT "foo.customcommand.notdeclared")
+
+ # If CLEAN_NO_CUSTOM is set, both the output and byproduct should be present
+ if(TEST_CLEAN_NO_CUSTOM)
+ list(APPEND EXPECTED_PRESENT "bar.cpp")
+ list(APPEND EXPECTED_PRESENT "foo.customcommand")
+ else()
+ list(APPEND EXPECTED_DELETED "bar.cpp")
+ list(APPEND EXPECTED_DELETED "foo.customcommand")
+ endif()
+
+ target_sources(foo PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/bar.cpp")
+endif()
+
+# Custom target that generates byproducts
+if(TEST_CUSTOM_TARGET)
+ add_custom_target(foo_file ALL
+ DEPENDS foo
+ COMMAND ${CMAKE_COMMAND} -E touch foo.customtarget
+ COMMAND ${CMAKE_COMMAND} -E touch foo.customtarget.notdeclared
+ BYPRODUCTS foo.customtarget
+ COMMENT "Creating byproducts with a custom target"
+ )
+
+ # The nondeclared byproduct should always be present
+ list(APPEND EXPECTED_PRESENT "foo.customtarget.notdeclared")
+
+ # If CLEAN_NO_CUSTOM is set, the declared byproduct should be present
+ if(TEST_CLEAN_NO_CUSTOM)
+ list(APPEND EXPECTED_PRESENT "foo.customtarget")
+ else()
+ list(APPEND EXPECTED_DELETED "foo.customtarget")
+ endif()
+endif()
+
+configure_file(files.cmake.in files.cmake)
diff --git a/Tests/RunCMake/Byproducts/RunCMakeTest.cmake b/Tests/RunCMake/Byproducts/RunCMakeTest.cmake
new file mode 100644
index 0000000..a7584ee
--- /dev/null
+++ b/Tests/RunCMake/Byproducts/RunCMakeTest.cmake
@@ -0,0 +1,58 @@
+include(RunCMake)
+
+function(run_CleanByproducts case)
+ set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CleanByproducts-${case}-build)
+ set(RunCMake_TEST_OPTIONS "${ARGN}")
+
+ run_cmake(CleanByproducts)
+ set(RunCMake_TEST_NO_CLEAN 1)
+
+ run_cmake_command(CleanByProducts-build ${CMAKE_COMMAND} --build .)
+ include("${RunCMake_TEST_BINARY_DIR}/files.cmake")
+
+ message("Checking that all expected files are present")
+ check_files(EXPECTED_PRESENT "${RunCMake_TEST_BINARY_DIR}" TRUE)
+ check_files(EXPECTED_DELETED "${RunCMake_TEST_BINARY_DIR}" TRUE)
+
+ run_cmake_command(CleanByProducts-clean ${CMAKE_COMMAND} --build . --target clean)
+
+ message("Checking that only the expected files are present after cleaning")
+ check_files(EXPECTED_PRESENT "${RunCMake_TEST_BINARY_DIR}" TRUE)
+ check_files(EXPECTED_DELETED "${RunCMake_TEST_BINARY_DIR}" FALSE)
+endfunction()
+
+function(check_files list path has_to_exist)
+ foreach(file IN LISTS ${list})
+ message("Checking ${file}")
+ set(file_exists FALSE)
+ if(EXISTS "${path}/${file}")
+ set(file_exists TRUE)
+ endif()
+
+ if(file_exists AND NOT has_to_exist)
+ message(FATAL_ERROR "${file} should have been deleted")
+ elseif(NOT file_exists AND has_to_exist)
+ message(FATAL_ERROR "${file} does not exist")
+ elseif(file_exists AND has_to_exist)
+ message("${file} found as expected")
+ elseif(NOT file_exists AND NOT has_to_exist)
+ message("${file} deleted as expected")
+ endif()
+
+ endforeach()
+endfunction()
+
+
+# Iterate through all possible test values
+set(counter 0)
+foreach(test_clean_no_custom TRUE FALSE)
+ foreach(test_build_events TRUE FALSE)
+ foreach(test_custom_command TRUE FALSE)
+ foreach(test_custom_target TRUE FALSE)
+ math(EXPR counter "${counter} + 1")
+ message("Test ${counter} - CLEAN_NO_CUSTOM: ${test_clean_no_custom}, Build events: ${test_build_events}, Custom command: ${test_custom_command}, Custom target: ${test_custom_target}")
+ run_CleanByproducts("buildevents${counter}" -DCLEAN_NO_CUSTOM=${test_clean_no_custom} -DTEST_BUILD_EVENTS=${test_build_events} -DTEST_CUSTOM_COMMAND=${test_custom_command} -DTEST_CUSTOM_TARGET=${test_custom_target})
+ endforeach()
+ endforeach()
+ endforeach()
+endforeach()
diff --git a/Tests/RunCMake/Byproducts/files.cmake.in b/Tests/RunCMake/Byproducts/files.cmake.in
new file mode 100644
index 0000000..a7d4831
--- /dev/null
+++ b/Tests/RunCMake/Byproducts/files.cmake.in
@@ -0,0 +1,2 @@
+set(EXPECTED_PRESENT "@EXPECTED_PRESENT@")
+set(EXPECTED_DELETED "@EXPECTED_DELETED@")
diff --git a/Tests/RunCMake/Byproducts/foo.cpp b/Tests/RunCMake/Byproducts/foo.cpp
new file mode 100644
index 0000000..d47cb91
--- /dev/null
+++ b/Tests/RunCMake/Byproducts/foo.cpp
@@ -0,0 +1,14 @@
+int bar(int y)
+{
+ return y * 6;
+}
+
+int foo(int x)
+{
+ return x * bar(x);
+}
+
+int main()
+{
+ return foo(4);
+}
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 080d0d0..90681b9 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -144,6 +144,9 @@ endif()
add_RunCMake_test(AndroidTestUtilities)
add_RunCMake_test(BuildDepends)
if(UNIX AND "${CMAKE_GENERATOR}" MATCHES "Unix Makefiles|Ninja")
+ add_RunCMake_test(Byproducts)
+endif()
+if(UNIX AND "${CMAKE_GENERATOR}" MATCHES "Unix Makefiles|Ninja")
add_RunCMake_test(CompilerChange)
endif()
add_RunCMake_test(CompilerNotFound)
diff --git a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
index d50de3d..4bfb2f2 100644
--- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
+++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
@@ -13,3 +13,4 @@ run_cmake(VsCSharpCustomTags)
run_cmake(VsCSharpReferenceProps)
run_cmake(VsCSharpWithoutSources)
run_cmake(VsSdkDirectories)
+run_cmake(VsGlobals)
diff --git a/Tests/RunCMake/VS10Project/VsGlobals-check.cmake b/Tests/RunCMake/VS10Project/VsGlobals-check.cmake
new file mode 100644
index 0000000..0e7fd45
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsGlobals-check.cmake
@@ -0,0 +1,44 @@
+set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj")
+if(NOT EXISTS "${vcProjectFile}")
+ set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.")
+ return()
+endif()
+
+set(InsideGlobals FALSE)
+set(DefaultLanguageSet FALSE)
+set(MinimumVisualStudioVersionSet FALSE)
+
+file(STRINGS "${vcProjectFile}" lines)
+foreach(line IN LISTS lines)
+ if(line MATCHES "^ *<PropertyGroup Label=\"Globals\"> *$")
+ set(InsideGlobals TRUE)
+ elseif(line MATCHES "^ *<DefaultLanguage>([a-zA-Z\\-]+)</DefaultLanguage> *$")
+ if("${CMAKE_MATCH_1}" STREQUAL "en-US")
+ if(InsideGlobals)
+ message(STATUS "foo.vcxproj has correct DefaultLanguage global property")
+ set(DefaultLanguageSet TRUE)
+ else()
+ message(STATUS "DefaultLanguage is set but not within \"Globals\" property group")
+ endif()
+ endif()
+ elseif(line MATCHES "^ *<MinimumVisualStudioVersion>([0-9\\.]+)</MinimumVisualStudioVersion> *$")
+ if("${CMAKE_MATCH_1}" STREQUAL "14.0")
+ if(InsideGlobals)
+ message(STATUS "foo.vcxproj has correct MinimumVisualStudioVersion global property")
+ set(MinimumVisualStudioVersionSet TRUE)
+ else()
+ message(STATUS "MinimumVisualStudioVersion is set but not within \"Globals\" property group")
+ endif()
+ endif()
+ endif()
+endforeach()
+
+if(NOT DefaultLanguageSet)
+ set(RunCMake_TEST_FAILED "DefaultLanguageSet not found or not set correctly.")
+ return()
+endif()
+
+if(NOT MinimumVisualStudioVersionSet)
+ set(RunCMake_TEST_FAILED "MinimumVisualStudioVersionSet not found or not set correctly.")
+ return()
+endif()
diff --git a/Tests/RunCMake/VS10Project/VsGlobals.cmake b/Tests/RunCMake/VS10Project/VsGlobals.cmake
new file mode 100644
index 0000000..a3ed5af
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsGlobals.cmake
@@ -0,0 +1,8 @@
+enable_language(CXX)
+
+set(CMAKE_VS_GLOBALS
+ "DefaultLanguage=en-US"
+ "MinimumVisualStudioVersion=14.0"
+)
+
+add_library(foo foo.cpp)