diff options
55 files changed, 952 insertions, 503 deletions
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 047859d..df8f12c 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -129,13 +129,16 @@ Properties on Targets /prop_tgt/AUTOGEN_TARGET_DEPENDS /prop_tgt/AUTOMOC_COMPILER_PREDEFINES /prop_tgt/AUTOMOC_DEPEND_FILTERS + /prop_tgt/AUTOMOC_EXECUTABLE /prop_tgt/AUTOMOC_MACRO_NAMES /prop_tgt/AUTOMOC_MOC_OPTIONS /prop_tgt/AUTOMOC /prop_tgt/AUTOUIC + /prop_tgt/AUTOUIC_EXECUTABLE /prop_tgt/AUTOUIC_OPTIONS /prop_tgt/AUTOUIC_SEARCH_PATHS /prop_tgt/AUTORCC + /prop_tgt/AUTORCC_EXECUTABLE /prop_tgt/AUTORCC_OPTIONS /prop_tgt/BINARY_DIR /prop_tgt/BUILD_RPATH diff --git a/Help/manual/ctest.1.rst b/Help/manual/ctest.1.rst index 1ef20ab..8490e3d 100644 --- a/Help/manual/ctest.1.rst +++ b/Help/manual/ctest.1.rst @@ -109,13 +109,23 @@ Options This option tells CTest to write all its output to a log file. -``-N,--show-only`` +``-N,--show-only[=<format>]`` Disable actual execution of tests. This option tells CTest to list the tests that would be run but not actually run them. Useful in conjunction with the ``-R`` and ``-E`` options. + ``<format>`` can be one of the following values. + + ``human`` + Human-friendly output. This is not guaranteed to be stable. + This is the default. + + ``json-v1`` + Dump the test information in JSON format. + See `Show as JSON Object Model`_. + ``-L <regex>, --label-regex <regex>`` Run tests with labels matching regular expression. @@ -1163,6 +1173,65 @@ Configuration settings include: * :module:`CTest` module variable: ``TRIGGER_SITE`` if set, else ``CTEST_TRIGGER_SITE`` +.. _`Show as JSON Object Model`: + +Show as JSON Object Model +========================= + +When the ``--show-only=json-v1`` command line option is given, the test +information is output in JSON format. Version 1.0 of the JSON object +model is defined as follows: + +``kind`` + The string "ctestInfo". + +``version`` + A JSON object specifying the version components. Its members are + + ``major`` + A non-negative integer specifying the major version component. + ``minor`` + A non-negative integer specifying the minor version component. + +``backtraceGraph`` + JSON object representing backtrace information with the + following members: + + ``commands`` + List of command names. + ``files`` + List of file names. + ``nodes`` + List of node JSON objects with members: + + ``command`` + Index into the ``commands`` member of the ``backtraceGraph``. + ``file`` + Index into the ``files`` member of the ``backtraceGraph``. + ``line`` + Line number in the file where the backtrace was added. + ``parent`` + Index into the ``nodes`` member of the ``backtraceGraph`` + representing the parent in the graph. + +``tests`` + A JSON array listing information about each test. Each entry + is a JSON object with members: + + ``name`` + Test name. + ``config`` + Configuration that the test can run on. + Empty string means any config. + ``command`` + List where the first element is the test command and the + remaining elements are the command arguments. + ``backtrace`` + Index into the ``nodes`` member of the ``backtraceGraph``. + ``properties`` + Test properties. + Can contain keys for each of the supported test properties. + See Also ======== diff --git a/Help/prop_tgt/AUTOMOC.rst b/Help/prop_tgt/AUTOMOC.rst index 7a3fd48..3e6d560 100644 --- a/Help/prop_tgt/AUTOMOC.rst +++ b/Help/prop_tgt/AUTOMOC.rst @@ -58,6 +58,9 @@ source files at build time and invoke moc accordingly. This property is initialized by the value of the :variable:`CMAKE_AUTOMOC` variable if it is set when a target is created. +The ``moc`` executable will be detected automatically, but can be forced to +a certain binary using the :prop_tgt:`AUTOMOC_EXECUTABLE` property. + Additional command line options for ``moc`` can be set via the :prop_tgt:`AUTOMOC_MOC_OPTIONS` property. diff --git a/Help/prop_tgt/AUTOMOC_EXECUTABLE.rst b/Help/prop_tgt/AUTOMOC_EXECUTABLE.rst new file mode 100644 index 0000000..6b66ce8 --- /dev/null +++ b/Help/prop_tgt/AUTOMOC_EXECUTABLE.rst @@ -0,0 +1,15 @@ +AUTOMOC_EXECUTABLE +------------------ + +:prop_tgt:`AUTOMOC_EXECUTABLE` is file path pointing to the ``moc`` +executable to use for :prop_tgt:`AUTOMOC` enabled files. Setting +this property will make CMake skip the automatic detection of the +``moc`` binary as well as the sanity-tests normally run to ensure +that the binary is available and working as expected. + +Usually this property does not need to be set. Only consider this +property if auto-detection of ``moc`` can not work -- e.g. because +you are building the ``moc`` binary as part of your project. + +See the :manual:`cmake-qt(7)` manual for more information on using CMake +with Qt. diff --git a/Help/prop_tgt/AUTORCC.rst b/Help/prop_tgt/AUTORCC.rst index 27fb149..5db6ed0 100644 --- a/Help/prop_tgt/AUTORCC.rst +++ b/Help/prop_tgt/AUTORCC.rst @@ -21,6 +21,9 @@ If the ``.qrc`` file is :prop_sf:`GENERATED` though, a Additional command line options for rcc can be set via the :prop_sf:`AUTORCC_OPTIONS` source file property on the ``.qrc`` file. +The ``rcc`` executable will be detected automatically, but can be forced to +a certain binary using the :prop_tgt:`AUTORCC_EXECUTABLE` property. + The global property :prop_gbl:`AUTOGEN_TARGETS_FOLDER` can be used to group the autorcc targets together in an IDE, e.g. in MSVS. diff --git a/Help/prop_tgt/AUTORCC_EXECUTABLE.rst b/Help/prop_tgt/AUTORCC_EXECUTABLE.rst new file mode 100644 index 0000000..ca0fbd7 --- /dev/null +++ b/Help/prop_tgt/AUTORCC_EXECUTABLE.rst @@ -0,0 +1,15 @@ +AUTORCC_EXECUTABLE +------------------ + +:prop_tgt:`AUTORCC_EXECUTABLE` is file path pointing to the ``rcc`` +executable to use for :prop_tgt:`AUTORCC` enabled files. Setting +this property will make CMake skip the automatic detection of the +``rcc`` binary as well as the sanity-tests normally run to ensure +that the binary is available and working as expected. + +Usually this property does not need to be set. Only consider this +property if auto-detection of ``rcc`` can not work -- e.g. because +you are building the ``rcc`` binary as part of your project. + +See the :manual:`cmake-qt(7)` manual for more information on using CMake +with Qt. diff --git a/Help/prop_tgt/AUTOUIC.rst b/Help/prop_tgt/AUTOUIC.rst index 4f58b35..85226c1 100644 --- a/Help/prop_tgt/AUTOUIC.rst +++ b/Help/prop_tgt/AUTOUIC.rst @@ -30,6 +30,9 @@ Additional command line options for ``uic`` can be set via the The global property :prop_gbl:`AUTOGEN_TARGETS_FOLDER` can be used to group the autouic targets together in an IDE, e.g. in MSVS. +The ``uic`` executable will be detected automatically, but can be forced to +a certain binary using the :prop_tgt:`AUTOUIC_EXECUTABLE` property. + Source files can be excluded from :prop_tgt:`AUTOUIC` processing by enabling :prop_sf:`SKIP_AUTOUIC` or the broader :prop_sf:`SKIP_AUTOGEN`. diff --git a/Help/prop_tgt/AUTOUIC_EXECUTABLE.rst b/Help/prop_tgt/AUTOUIC_EXECUTABLE.rst new file mode 100644 index 0000000..03bd554 --- /dev/null +++ b/Help/prop_tgt/AUTOUIC_EXECUTABLE.rst @@ -0,0 +1,15 @@ +AUTOUIC_EXECUTABLE +------------------ + +:prop_tgt:`AUTOUIC_EXECUTABLE` is file path pointing to the ``uic`` +executable to use for :prop_tgt:`AUTOUIC` enabled files. Setting +this property will make CMake skip the automatic detection of the +``uic`` binary as well as the sanity-tests normally run to ensure +that the binary is available and working as expected. + +Usually this property does not need to be set. Only consider this +property if auto-detection of ``uic`` can not work -- e.g. because +you are building the ``uic`` binary as part of your project. + +See the :manual:`cmake-qt(7)` manual for more information on using CMake +with Qt. diff --git a/Help/release/dev/autogen_executables.rst b/Help/release/dev/autogen_executables.rst new file mode 100644 index 0000000..5e967ea --- /dev/null +++ b/Help/release/dev/autogen_executables.rst @@ -0,0 +1,9 @@ +AUTO*_EXECUTABLE +---------------- + +* The :prop_tgt:`AUTOMOC_EXECUTABLE`, :prop_tgt:`AUTORCC_EXECUTABLE` and + :prop_tgt:`AUTOUIC_EXECUTABLE` target properties all take a path to an + executable and force automoc/autorcc/autouic to use this executable. + + Setting these will also prevent the configure time testing for these + executables. This is mainly useful when you build these tools yourself. diff --git a/Help/release/dev/ctest-show-only-json-v1.rst b/Help/release/dev/ctest-show-only-json-v1.rst new file mode 100644 index 0000000..f593e7e --- /dev/null +++ b/Help/release/dev/ctest-show-only-json-v1.rst @@ -0,0 +1,6 @@ +ctest-show-only-json-v1 +----------------------- + +* :manual:`ctest(1)` gained a ``--show-only=json-v1`` option to show the + list of tests in a machine-readable JSON format. + See the :ref:`Show as JSON Object Model` section of the manual. diff --git a/Modules/BundleUtilities.cmake b/Modules/BundleUtilities.cmake index d5c47f8..8c7646e 100644 --- a/Modules/BundleUtilities.cmake +++ b/Modules/BundleUtilities.cmake @@ -717,6 +717,9 @@ function(link_resolved_item_into_bundle resolved_item resolved_embedded_item) else() get_filename_component(target_dir "${resolved_embedded_item}" DIRECTORY) file(RELATIVE_PATH symlink_target "${target_dir}" "${resolved_item}") + if (NOT EXISTS "${target_dir}") + file(MAKE_DIRECTORY "${target_dir}") + endif() execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "${symlink_target}" "${resolved_embedded_item}") endif() endfunction() diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index f696fed..2c83140 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 13) -set(CMake_VERSION_PATCH 20190109) +set(CMake_VERSION_PATCH 20190111) #set(CMake_VERSION_RC 1) diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx index f026001..8867323 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.cxx +++ b/Source/CTest/cmCTestMultiProcessHandler.cxx @@ -6,9 +6,13 @@ #include "cmCTest.h" #include "cmCTestRunTest.h" #include "cmCTestTestHandler.h" +#include "cmDuration.h" +#include "cmListFileCache.h" #include "cmSystemTools.h" #include "cmWorkingDirectory.h" +#include "cm_jsoncpp_value.h" +#include "cm_jsoncpp_writer.h" #include "cm_uv.h" #include "cmUVSignalHackRAII.h" // IWYU pragma: keep @@ -20,13 +24,19 @@ #include <chrono> #include <cstring> #include <iomanip> +#include <iostream> #include <list> #include <math.h> #include <sstream> #include <stack> #include <stdlib.h> +#include <unordered_map> #include <utility> +namespace cmsys { +class RegularExpression; +} + class TestComparator { public: @@ -725,9 +735,330 @@ void cmCTestMultiProcessHandler::MarkFinished() cmSystemTools::RemoveFile(fname); } +static Json::Value DumpToJsonArray(const std::set<std::string>& values) +{ + Json::Value jsonArray = Json::arrayValue; + for (auto& it : values) { + jsonArray.append(it); + } + return jsonArray; +} + +static Json::Value DumpToJsonArray(const std::vector<std::string>& values) +{ + Json::Value jsonArray = Json::arrayValue; + for (auto& it : values) { + jsonArray.append(it); + } + return jsonArray; +} + +static Json::Value DumpRegExToJsonArray( + const std::vector<std::pair<cmsys::RegularExpression, std::string>>& values) +{ + Json::Value jsonArray = Json::arrayValue; + for (auto& it : values) { + jsonArray.append(it.second); + } + return jsonArray; +} + +static Json::Value DumpMeasurementToJsonArray( + const std::map<std::string, std::string>& values) +{ + Json::Value jsonArray = Json::arrayValue; + for (auto& it : values) { + Json::Value measurement = Json::objectValue; + measurement["measurement"] = it.first; + measurement["value"] = it.second; + jsonArray.append(measurement); + } + return jsonArray; +} + +static Json::Value DumpTimeoutAfterMatch( + cmCTestTestHandler::cmCTestTestProperties& testProperties) +{ + Json::Value timeoutAfterMatch = Json::objectValue; + timeoutAfterMatch["timeout"] = testProperties.AlternateTimeout.count(); + timeoutAfterMatch["regex"] = + DumpRegExToJsonArray(testProperties.TimeoutRegularExpressions); + return timeoutAfterMatch; +} + +static Json::Value DumpCTestProperty(std::string const& name, + Json::Value value) +{ + Json::Value property = Json::objectValue; + property["name"] = name; + property["value"] = std::move(value); + return property; +} + +static Json::Value DumpCTestProperties( + cmCTestTestHandler::cmCTestTestProperties& testProperties) +{ + Json::Value properties = Json::arrayValue; + if (!testProperties.AttachOnFail.empty()) { + properties.append(DumpCTestProperty( + "ATTACHED_FILES_ON_FAIL", DumpToJsonArray(testProperties.AttachOnFail))); + } + if (!testProperties.AttachedFiles.empty()) { + properties.append(DumpCTestProperty( + "ATTACHED_FILES", DumpToJsonArray(testProperties.AttachedFiles))); + } + if (testProperties.Cost != 0.0f) { + properties.append( + DumpCTestProperty("COST", static_cast<double>(testProperties.Cost))); + } + if (!testProperties.Depends.empty()) { + properties.append( + DumpCTestProperty("DEPENDS", DumpToJsonArray(testProperties.Depends))); + } + if (testProperties.Disabled) { + properties.append(DumpCTestProperty("DISABLED", testProperties.Disabled)); + } + if (!testProperties.Environment.empty()) { + properties.append(DumpCTestProperty( + "ENVIRONMENT", DumpToJsonArray(testProperties.Environment))); + } + if (!testProperties.ErrorRegularExpressions.empty()) { + properties.append(DumpCTestProperty( + "FAIL_REGULAR_EXPRESSION", + DumpRegExToJsonArray(testProperties.ErrorRegularExpressions))); + } + if (!testProperties.FixturesCleanup.empty()) { + properties.append(DumpCTestProperty( + "FIXTURES_CLEANUP", DumpToJsonArray(testProperties.FixturesCleanup))); + } + if (!testProperties.FixturesRequired.empty()) { + properties.append(DumpCTestProperty( + "FIXTURES_REQUIRED", DumpToJsonArray(testProperties.FixturesRequired))); + } + if (!testProperties.FixturesSetup.empty()) { + properties.append(DumpCTestProperty( + "FIXTURES_SETUP", DumpToJsonArray(testProperties.FixturesSetup))); + } + if (!testProperties.Labels.empty()) { + properties.append( + DumpCTestProperty("LABELS", DumpToJsonArray(testProperties.Labels))); + } + if (!testProperties.Measurements.empty()) { + properties.append(DumpCTestProperty( + "MEASUREMENT", DumpMeasurementToJsonArray(testProperties.Measurements))); + } + if (!testProperties.RequiredRegularExpressions.empty()) { + properties.append(DumpCTestProperty( + "PASS_REGULAR_EXPRESSION", + DumpRegExToJsonArray(testProperties.RequiredRegularExpressions))); + } + if (testProperties.WantAffinity) { + properties.append( + DumpCTestProperty("PROCESSOR_AFFINITY", testProperties.WantAffinity)); + } + if (testProperties.Processors != 1) { + properties.append( + DumpCTestProperty("PROCESSORS", testProperties.Processors)); + } + if (!testProperties.RequiredFiles.empty()) { + properties["REQUIRED_FILES"] = + DumpToJsonArray(testProperties.RequiredFiles); + } + if (!testProperties.LockedResources.empty()) { + properties.append(DumpCTestProperty( + "RESOURCE_LOCK", DumpToJsonArray(testProperties.LockedResources))); + } + if (testProperties.RunSerial) { + properties.append( + DumpCTestProperty("RUN_SERIAL", testProperties.RunSerial)); + } + if (testProperties.SkipReturnCode != -1) { + properties.append( + DumpCTestProperty("SKIP_RETURN_CODE", testProperties.SkipReturnCode)); + } + if (testProperties.ExplicitTimeout) { + properties.append( + DumpCTestProperty("TIMEOUT", testProperties.Timeout.count())); + } + if (!testProperties.TimeoutRegularExpressions.empty()) { + properties.append(DumpCTestProperty( + "TIMEOUT_AFTER_MATCH", DumpTimeoutAfterMatch(testProperties))); + } + if (testProperties.WillFail) { + properties.append(DumpCTestProperty("WILL_FAIL", testProperties.WillFail)); + } + if (!testProperties.Directory.empty()) { + properties.append( + DumpCTestProperty("WORKING_DIRECTORY", testProperties.Directory)); + } + return properties; +} + +class BacktraceData +{ + std::unordered_map<std::string, Json::ArrayIndex> CommandMap; + std::unordered_map<std::string, Json::ArrayIndex> FileMap; + std::unordered_map<cmListFileContext const*, Json::ArrayIndex> NodeMap; + Json::Value Commands = Json::arrayValue; + Json::Value Files = Json::arrayValue; + Json::Value Nodes = Json::arrayValue; + + Json::ArrayIndex AddCommand(std::string const& command) + { + auto i = this->CommandMap.find(command); + if (i == this->CommandMap.end()) { + i = this->CommandMap.emplace(command, this->Commands.size()).first; + this->Commands.append(command); + } + return i->second; + } + + Json::ArrayIndex AddFile(std::string const& file) + { + auto i = this->FileMap.find(file); + if (i == this->FileMap.end()) { + i = this->FileMap.emplace(file, this->Files.size()).first; + this->Files.append(file); + } + return i->second; + } + +public: + bool Add(cmListFileBacktrace const& bt, Json::ArrayIndex& index); + Json::Value Dump(); +}; + +bool BacktraceData::Add(cmListFileBacktrace const& bt, Json::ArrayIndex& index) +{ + if (bt.Empty()) { + return false; + } + cmListFileContext const* top = &bt.Top(); + auto found = this->NodeMap.find(top); + if (found != this->NodeMap.end()) { + index = found->second; + return true; + } + Json::Value entry = Json::objectValue; + entry["file"] = this->AddFile(top->FilePath); + if (top->Line) { + entry["line"] = static_cast<int>(top->Line); + } + if (!top->Name.empty()) { + entry["command"] = this->AddCommand(top->Name); + } + Json::ArrayIndex parent; + if (this->Add(bt.Pop(), parent)) { + entry["parent"] = parent; + } + index = this->NodeMap[top] = this->Nodes.size(); + this->Nodes.append(std::move(entry)); // NOLINT(*) + return true; +} + +Json::Value BacktraceData::Dump() +{ + Json::Value backtraceGraph; + this->CommandMap.clear(); + this->FileMap.clear(); + this->NodeMap.clear(); + backtraceGraph["commands"] = std::move(this->Commands); + backtraceGraph["files"] = std::move(this->Files); + backtraceGraph["nodes"] = std::move(this->Nodes); + return backtraceGraph; +} + +static void AddBacktrace(BacktraceData& backtraceGraph, Json::Value& object, + cmListFileBacktrace const& bt) +{ + Json::ArrayIndex backtrace; + if (backtraceGraph.Add(bt, backtrace)) { + object["backtrace"] = backtrace; + } +} + +static Json::Value DumpCTestInfo( + cmCTestRunTest& testRun, + cmCTestTestHandler::cmCTestTestProperties& testProperties, + BacktraceData& backtraceGraph) +{ + Json::Value testInfo = Json::objectValue; + // test name should always be present + testInfo["name"] = testProperties.Name; + std::string const& config = testRun.GetCTest()->GetConfigType(); + if (!config.empty()) { + testInfo["config"] = config; + } + std::string const& command = testRun.GetActualCommand(); + if (!command.empty()) { + std::vector<std::string> commandAndArgs; + commandAndArgs.push_back(command); + const std::vector<std::string>& args = testRun.GetArguments(); + if (!args.empty()) { + commandAndArgs.reserve(args.size() + 1); + commandAndArgs.insert(commandAndArgs.end(), args.begin(), args.end()); + } + testInfo["command"] = DumpToJsonArray(commandAndArgs); + } + Json::Value properties = DumpCTestProperties(testProperties); + if (!properties.empty()) { + testInfo["properties"] = properties; + } + if (!testProperties.Backtrace.Empty()) { + AddBacktrace(backtraceGraph, testInfo, testProperties.Backtrace); + } + return testInfo; +} + +static Json::Value DumpVersion(int major, int minor) +{ + Json::Value version = Json::objectValue; + version["major"] = major; + version["minor"] = minor; + return version; +} + +void cmCTestMultiProcessHandler::PrintOutputAsJson() +{ + this->TestHandler->SetMaxIndex(this->FindMaxIndex()); + + Json::Value result = Json::objectValue; + result["kind"] = "ctestInfo"; + result["version"] = DumpVersion(1, 0); + + BacktraceData backtraceGraph; + Json::Value tests = Json::arrayValue; + for (auto& it : this->Properties) { + cmCTestTestHandler::cmCTestTestProperties& p = *it.second; + + // Don't worry if this fails, we are only showing the test list, not + // running the tests + cmWorkingDirectory workdir(p.Directory); + cmCTestRunTest testRun(*this); + testRun.SetIndex(p.Index); + testRun.SetTestProperties(&p); + testRun.ComputeArguments(); + + Json::Value testInfo = DumpCTestInfo(testRun, p, backtraceGraph); + tests.append(testInfo); + } + result["backtraceGraph"] = backtraceGraph.Dump(); + result["tests"] = std::move(tests); + + Json::StreamWriterBuilder builder; + builder["indentation"] = " "; + std::unique_ptr<Json::StreamWriter> jout(builder.newStreamWriter()); + jout->write(result, &std::cout); +} + // For ShowOnly mode void cmCTestMultiProcessHandler::PrintTestList() { + if (this->CTest->GetOutputAsJson()) { + PrintOutputAsJson(); + return; + } + this->TestHandler->SetMaxIndex(this->FindMaxIndex()); int count = 0; diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h index 3927a8a..93bb880 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.h +++ b/Source/CTest/cmCTestMultiProcessHandler.h @@ -51,6 +51,7 @@ public: void SetParallelLevel(size_t); void SetTestLoad(unsigned long load); virtual void RunTests(); + void PrintOutputAsJson(); void PrintTestList(); void PrintLabels(); diff --git a/Source/CTest/cmCTestRunTest.h b/Source/CTest/cmCTestRunTest.h index 10dceca..c786413 100644 --- a/Source/CTest/cmCTestRunTest.h +++ b/Source/CTest/cmCTestRunTest.h @@ -78,6 +78,10 @@ public: cmCTest* GetCTest() const { return this->CTest; } + std::string& GetActualCommand() { return this->ActualCommand; } + + const std::vector<std::string>& GetArguments() { return this->Arguments; } + void FinalizeTest(); bool TimedOutForStopTime() const { return this->TimeoutIsForStopTime; } diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index 2e1bb0a..9fd2299 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx @@ -2147,6 +2147,32 @@ bool cmCTestTestHandler::SetTestsProperties( for (std::string const& t : tests) { for (cmCTestTestProperties& rt : this->TestList) { if (t == rt.Name) { + if (key == "_BACKTRACE_TRIPLES") { + std::vector<std::string> triples; + // allow empty args in the triples + cmSystemTools::ExpandListArgument(val, triples, true); + + // Ensure we have complete triples otherwise the data is corrupt. + if (triples.size() % 3 == 0) { + cmState state; + rt.Backtrace = cmListFileBacktrace(state.CreateBaseSnapshot()); + + // the first entry represents the top of the trace so we need to + // reconstruct the backtrace in reverse + for (size_t i = triples.size(); i >= 3; i -= 3) { + cmListFileContext fc; + fc.FilePath = triples[i - 3]; + long line = 0; + if (!cmSystemTools::StringToLong(triples[i - 2].c_str(), + &line)) { + line = 0; + } + fc.Line = line; + fc.Name = triples[i - 1]; + rt.Backtrace = rt.Backtrace.Push(fc); + } + } + } if (key == "WILL_FAIL") { rt.WillFail = cmSystemTools::IsOn(val); } diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h index bcacf23..0b557db 100644 --- a/Source/CTest/cmCTestTestHandler.h +++ b/Source/CTest/cmCTestTestHandler.h @@ -7,6 +7,7 @@ #include "cmCTestGenericHandler.h" #include "cmDuration.h" +#include "cmListFileCache.h" #include "cmsys/RegularExpression.hxx" #include <chrono> @@ -141,6 +142,8 @@ public: std::set<std::string> FixturesCleanup; std::set<std::string> FixturesRequired; std::set<std::string> RequireSuccessDepends; + // Private test generator properties used to track backtraces + cmListFileBacktrace Backtrace; }; struct cmCTestTestResult diff --git a/Source/cmAlgorithms.h b/Source/cmAlgorithms.h index bbd3e8e..2f8e675 100644 --- a/Source/cmAlgorithms.h +++ b/Source/cmAlgorithms.h @@ -396,6 +396,12 @@ constexpr #endif +template <typename T> +int isize(const T& t) +{ + return static_cast<int>(cm::size(t)); +} + #if __cplusplus >= 201402L || defined(_MSVC_LANG) && _MSVC_LANG >= 201402L using std::cbegin; diff --git a/Source/cmCPluginAPI.cxx b/Source/cmCPluginAPI.cxx index 065a184..1d9621c 100644 --- a/Source/cmCPluginAPI.cxx +++ b/Source/cmCPluginAPI.cxx @@ -407,7 +407,7 @@ char CCONV* cmExpandVariablesInString(void* arg, const char* source, cmMakefile* mf = static_cast<cmMakefile*>(arg); std::string barf = source; std::string const& result = - mf->ExpandVariablesInString(barf, escapeQuotes, atOnly); + mf->ExpandVariablesInString(barf, escapeQuotes != 0, atOnly != 0); return strdup(result.c_str()); } diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index 7c19864..225c99f 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -278,6 +278,8 @@ cmCTest::cmCTest() this->ExtraVerbose = false; this->ProduceXML = false; this->ShowOnly = false; + this->OutputAsJson = false; + this->OutputAsJsonVersion = 1; this->RunConfigurationScript = false; this->UseHTTP10 = false; this->PrintLabels = false; @@ -1930,6 +1932,20 @@ bool cmCTest::HandleCommandLineArguments(size_t& i, if (this->CheckArgument(arg, "-N", "--show-only")) { this->ShowOnly = true; } + if (cmSystemTools::StringStartsWith(arg.c_str(), "--show-only=")) { + this->ShowOnly = true; + + // Check if a specific format is requested. Defaults to human readable + // text. + std::string argWithFormat = "--show-only="; + std::string format = arg.substr(argWithFormat.length()); + if (format == "json-v1") { + // Force quiet mode so the only output is the json object model. + this->Quiet = true; + this->OutputAsJson = true; + this->OutputAsJsonVersion = 1; + } + } if (this->CheckArgument(arg, "-O", "--output-log") && i < args.size() - 1) { i++; @@ -2630,6 +2646,16 @@ bool cmCTest::GetShowOnly() return this->ShowOnly; } +bool cmCTest::GetOutputAsJson() +{ + return this->OutputAsJson; +} + +int cmCTest::GetOutputAsJsonVersion() +{ + return this->OutputAsJsonVersion; +} + int cmCTest::GetMaxTestNameWidth() const { return this->MaxTestNameWidth; diff --git a/Source/cmCTest.h b/Source/cmCTest.h index 480204a..2b40ca3 100644 --- a/Source/cmCTest.h +++ b/Source/cmCTest.h @@ -215,6 +215,10 @@ public: /** Should we only show what we would do? */ bool GetShowOnly(); + bool GetOutputAsJson(); + + int GetOutputAsJsonVersion(); + bool ShouldUseHTTP10() { return this->UseHTTP10; } bool ShouldPrintLabels() { return this->PrintLabels; } @@ -507,6 +511,8 @@ private: t_TestingHandlers TestingHandlers; bool ShowOnly; + bool OutputAsJson; + int OutputAsJsonVersion; /** Map of configuration properties */ typedef std::map<std::string, std::string> CTestConfigurationMap; diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 594cb67..fc9c1d2 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -220,7 +220,7 @@ bool cmFileCommand::HandleWriteCommand(std::vector<std::string> const& args, // Set permissions to writable if (cmSystemTools::GetPermissions(fileName.c_str(), mode)) { #if defined(_MSC_VER) || defined(__MINGW32__) - writable = mode & S_IWRITE; + writable = (mode & S_IWRITE) != 0; mode_t newMode = mode | S_IWRITE; #else writable = mode & S_IWUSR; diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index c9c6938..4709194 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx @@ -488,16 +488,6 @@ std::string cmGlobalVisualStudio10Generator::SelectWindowsCEToolset() const return ""; } -void cmGlobalVisualStudio10Generator::WriteSLNHeader(std::ostream& fout) -{ - fout << "Microsoft Visual Studio Solution File, Format Version 11.00\n"; - if (this->ExpressEdition) { - fout << "# Visual C++ Express 2010\n"; - } else { - fout << "# Visual Studio 2010\n"; - } -} - ///! Create a local generator appropriate to this Global Generator cmLocalGenerator* cmGlobalVisualStudio10Generator::CreateLocalGenerator( cmMakefile* mf) @@ -1024,6 +1014,27 @@ std::string cmGlobalVisualStudio10Generator::Encoding() return "utf-8"; } +const char* cmGlobalVisualStudio10Generator::GetToolsVersion() const +{ + switch (this->Version) { + case cmGlobalVisualStudioGenerator::VS9: + case cmGlobalVisualStudioGenerator::VS10: + case cmGlobalVisualStudioGenerator::VS11: + return "4.0"; + + // in Visual Studio 2013 they detached the MSBuild tools version + // from the .Net Framework version and instead made it have it's own + // version number + case cmGlobalVisualStudioGenerator::VS12: + return "12.0"; + case cmGlobalVisualStudioGenerator::VS14: + return "14.0"; + case cmGlobalVisualStudioGenerator::VS15: + return "15.0"; + } + return ""; +} + bool cmGlobalVisualStudio10Generator::IsNsightTegra() const { return !this->NsightTegraVersion.empty(); diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h index dc49ded..1e72959 100644 --- a/Source/cmGlobalVisualStudio10Generator.h +++ b/Source/cmGlobalVisualStudio10Generator.h @@ -43,7 +43,6 @@ public: */ void EnableLanguage(std::vector<std::string> const& languages, cmMakefile*, bool optional) override; - void WriteSLNHeader(std::ostream& fout) override; bool IsCudaEnabled() const { return this->CudaEnabled; } @@ -104,7 +103,7 @@ public: std::string const& sfRel); std::string Encoding() override; - virtual const char* GetToolsVersion() { return "4.0"; } + const char* GetToolsVersion() const; virtual bool IsDefaultToolset(const std::string& version) const; virtual std::string GetAuxiliaryToolset() const; @@ -140,8 +139,6 @@ protected: virtual bool SelectWindowsPhoneToolset(std::string& toolset) const; virtual bool SelectWindowsStoreToolset(std::string& toolset) const; - const char* GetIDEVersion() const override { return "10.0"; } - std::string const& GetMSBuildCommand(); cmIDEFlagTable const* LoadFlagTable(std::string const& flagTableName, diff --git a/Source/cmGlobalVisualStudio11Generator.cxx b/Source/cmGlobalVisualStudio11Generator.cxx index 4cde874..499ae32 100644 --- a/Source/cmGlobalVisualStudio11Generator.cxx +++ b/Source/cmGlobalVisualStudio11Generator.cxx @@ -188,16 +188,6 @@ bool cmGlobalVisualStudio11Generator::SelectWindowsStoreToolset( toolset); } -void cmGlobalVisualStudio11Generator::WriteSLNHeader(std::ostream& fout) -{ - fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; - if (this->ExpressEdition) { - fout << "# Visual Studio Express 2012 for Windows Desktop\n"; - } else { - fout << "# Visual Studio 2012\n"; - } -} - bool cmGlobalVisualStudio11Generator::UseFolderProperty() const { // Intentionally skip up to the top-level class implementation. diff --git a/Source/cmGlobalVisualStudio11Generator.h b/Source/cmGlobalVisualStudio11Generator.h index 5b089a4..6346da2 100644 --- a/Source/cmGlobalVisualStudio11Generator.h +++ b/Source/cmGlobalVisualStudio11Generator.h @@ -26,8 +26,6 @@ public: bool MatchesGeneratorName(const std::string& name) const override; - void WriteSLNHeader(std::ostream& fout) override; - protected: bool InitializeWindowsPhone(cmMakefile* mf) override; bool InitializeWindowsStore(cmMakefile* mf) override; @@ -43,7 +41,6 @@ protected: bool IsWindowsPhoneToolsetInstalled() const; bool IsWindowsStoreToolsetInstalled() const; - const char* GetIDEVersion() const override { return "11.0"; } bool UseFolderProperty() const override; static std::set<std::string> GetInstalledWindowsCESDKs(); diff --git a/Source/cmGlobalVisualStudio12Generator.cxx b/Source/cmGlobalVisualStudio12Generator.cxx index 3be7d24..2cec48c 100644 --- a/Source/cmGlobalVisualStudio12Generator.cxx +++ b/Source/cmGlobalVisualStudio12Generator.cxx @@ -186,16 +186,6 @@ bool cmGlobalVisualStudio12Generator::SelectWindowsStoreToolset( toolset); } -void cmGlobalVisualStudio12Generator::WriteSLNHeader(std::ostream& fout) -{ - fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; - if (this->ExpressEdition) { - fout << "# Visual Studio Express 2013 for Windows Desktop\n"; - } else { - fout << "# Visual Studio 2013\n"; - } -} - bool cmGlobalVisualStudio12Generator::IsWindowsDesktopToolsetInstalled() const { const char desktop81Key[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\" diff --git a/Source/cmGlobalVisualStudio12Generator.h b/Source/cmGlobalVisualStudio12Generator.h index ae78de7..1b7bbc9 100644 --- a/Source/cmGlobalVisualStudio12Generator.h +++ b/Source/cmGlobalVisualStudio12Generator.h @@ -24,13 +24,6 @@ public: bool MatchesGeneratorName(const std::string& name) const override; - void WriteSLNHeader(std::ostream& fout) override; - - // in Visual Studio 2013 they detached the MSBuild tools version - // from the .Net Framework version and instead made it have it's own - // version number - const char* GetToolsVersion() override { return "12.0"; } - protected: bool ProcessGeneratorToolsetField(std::string const& key, std::string const& value) override; @@ -48,7 +41,6 @@ protected: // of the toolset is installed bool IsWindowsPhoneToolsetInstalled() const; bool IsWindowsStoreToolsetInstalled() const; - const char* GetIDEVersion() const override { return "12.0"; } private: class Factory; diff --git a/Source/cmGlobalVisualStudio14Generator.cxx b/Source/cmGlobalVisualStudio14Generator.cxx index ac969e8..a6dbc23 100644 --- a/Source/cmGlobalVisualStudio14Generator.cxx +++ b/Source/cmGlobalVisualStudio14Generator.cxx @@ -174,17 +174,6 @@ bool cmGlobalVisualStudio14Generator::SelectWindowsStoreToolset( toolset); } -void cmGlobalVisualStudio14Generator::WriteSLNHeader(std::ostream& fout) -{ - // Visual Studio 14 writes .sln format 12.00 - fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; - if (this->ExpressEdition) { - fout << "# Visual Studio Express 14 for Windows Desktop\n"; - } else { - fout << "# Visual Studio 14\n"; - } -} - bool cmGlobalVisualStudio14Generator::IsWindowsDesktopToolsetInstalled() const { const char desktop10Key[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\" diff --git a/Source/cmGlobalVisualStudio14Generator.h b/Source/cmGlobalVisualStudio14Generator.h index 4bc430b..4be21e0 100644 --- a/Source/cmGlobalVisualStudio14Generator.h +++ b/Source/cmGlobalVisualStudio14Generator.h @@ -24,10 +24,6 @@ public: bool MatchesGeneratorName(const std::string& name) const override; - void WriteSLNHeader(std::ostream& fout) override; - - const char* GetToolsVersion() override { return "14.0"; } - protected: bool InitializeWindows(cmMakefile* mf) override; bool InitializeWindowsStore(cmMakefile* mf) override; @@ -41,7 +37,6 @@ protected: // version of the toolset. virtual std::string GetWindows10SDKMaxVersion() const; - const char* GetIDEVersion() const override { return "14.0"; } virtual bool SelectWindows10SDK(cmMakefile* mf, bool required); // Used to verify that the Desktop toolset for the current generator is diff --git a/Source/cmGlobalVisualStudio15Generator.cxx b/Source/cmGlobalVisualStudio15Generator.cxx index 4a08352..2af17e8 100644 --- a/Source/cmGlobalVisualStudio15Generator.cxx +++ b/Source/cmGlobalVisualStudio15Generator.cxx @@ -97,17 +97,6 @@ bool cmGlobalVisualStudio15Generator::MatchesGeneratorName( return false; } -void cmGlobalVisualStudio15Generator::WriteSLNHeader(std::ostream& fout) -{ - // Visual Studio 15 writes .sln format 12.00 - fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; - if (this->ExpressEdition) { - fout << "# Visual Studio Express 15 for Windows Desktop\n"; - } else { - fout << "# Visual Studio 15\n"; - } -} - bool cmGlobalVisualStudio15Generator::SetGeneratorInstance( std::string const& i, cmMakefile* mf) { diff --git a/Source/cmGlobalVisualStudio15Generator.h b/Source/cmGlobalVisualStudio15Generator.h index 68aa14f..233f3bc 100644 --- a/Source/cmGlobalVisualStudio15Generator.h +++ b/Source/cmGlobalVisualStudio15Generator.h @@ -24,10 +24,6 @@ public: bool MatchesGeneratorName(const std::string& name) const override; - void WriteSLNHeader(std::ostream& fout) override; - - const char* GetToolsVersion() override { return "15.0"; } - bool SetGeneratorInstance(std::string const& i, cmMakefile* mf) override; bool GetVSInstance(std::string& dir) const; @@ -39,8 +35,6 @@ protected: bool InitializeWindows(cmMakefile* mf) override; bool SelectWindowsStoreToolset(std::string& toolset) const override; - const char* GetIDEVersion() const override { return "15.0"; } - // Used to verify that the Desktop toolset for the current generator is // installed on the machine. bool IsWindowsDesktopToolsetInstalled() const override; diff --git a/Source/cmGlobalVisualStudio71Generator.cxx b/Source/cmGlobalVisualStudio71Generator.cxx index 3be09b0..8694df2 100644 --- a/Source/cmGlobalVisualStudio71Generator.cxx +++ b/Source/cmGlobalVisualStudio71Generator.cxx @@ -217,9 +217,3 @@ void cmGlobalVisualStudio71Generator::WriteProjectConfigurations( } } } - -// output standard header for dsw file -void cmGlobalVisualStudio71Generator::WriteSLNHeader(std::ostream& fout) -{ - fout << "Microsoft Visual Studio Solution File, Format Version 8.00\n"; -} diff --git a/Source/cmGlobalVisualStudio71Generator.h b/Source/cmGlobalVisualStudio71Generator.h index b634b95..85755af 100644 --- a/Source/cmGlobalVisualStudio71Generator.h +++ b/Source/cmGlobalVisualStudio71Generator.h @@ -34,7 +34,6 @@ protected: void WriteExternalProject(std::ostream& fout, const std::string& name, const char* path, const char* typeGuid, const std::set<BT<std::string>>& depends) override; - void WriteSLNHeader(std::ostream& fout) override; // Folders are not supported by VS 7.1. bool UseFolderProperty() const override { return false; } diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h index 12a86f2..f092b56 100644 --- a/Source/cmGlobalVisualStudio7Generator.h +++ b/Source/cmGlobalVisualStudio7Generator.h @@ -134,7 +134,6 @@ protected: virtual void WriteSLNGlobalSections(std::ostream& fout, cmLocalGenerator* root); virtual void WriteSLNFooter(std::ostream& fout); - virtual void WriteSLNHeader(std::ostream& fout) = 0; std::string WriteUtilityDepend(const cmGeneratorTarget* target) override; virtual void WriteTargetsToSolution( diff --git a/Source/cmGlobalVisualStudio8Generator.h b/Source/cmGlobalVisualStudio8Generator.h index a21c53d..cacfa68 100644 --- a/Source/cmGlobalVisualStudio8Generator.h +++ b/Source/cmGlobalVisualStudio8Generator.h @@ -44,12 +44,8 @@ public: return !this->WindowsCEVersion.empty(); } - /** Is the installed VS an Express edition? */ - bool IsExpressEdition() const { return this->ExpressEdition; } - protected: void AddExtraIDETargets() override; - const char* GetIDEVersion() const override { return "8.0"; } std::string FindDevEnvCommand() override; @@ -77,6 +73,5 @@ protected: std::string Name; std::string WindowsCEVersion; - bool ExpressEdition; }; #endif diff --git a/Source/cmGlobalVisualStudio9Generator.cxx b/Source/cmGlobalVisualStudio9Generator.cxx index 7ac3a6f..760cce4 100644 --- a/Source/cmGlobalVisualStudio9Generator.cxx +++ b/Source/cmGlobalVisualStudio9Generator.cxx @@ -94,12 +94,6 @@ cmGlobalVisualStudio9Generator::cmGlobalVisualStudio9Generator( vc9Express, cmSystemTools::KeyWOW64_32); } -void cmGlobalVisualStudio9Generator::WriteSLNHeader(std::ostream& fout) -{ - fout << "Microsoft Visual Studio Solution File, Format Version 10.00\n"; - fout << "# Visual Studio 2008\n"; -} - std::string cmGlobalVisualStudio9Generator::GetUserMacrosDirectory() { std::string base; diff --git a/Source/cmGlobalVisualStudio9Generator.h b/Source/cmGlobalVisualStudio9Generator.h index ee17c37..e537a3d 100644 --- a/Source/cmGlobalVisualStudio9Generator.h +++ b/Source/cmGlobalVisualStudio9Generator.h @@ -18,12 +18,6 @@ public: static cmGlobalGeneratorFactory* NewFactory(); /** - * Try to determine system information such as shared library - * extension, pthreads, byte order etc. - */ - void WriteSLNHeader(std::ostream& fout) override; - - /** * Where does this version of Visual Studio look for macros for the * current user? Returns the empty string if this version of Visual * Studio does not implement support for VB macros. @@ -36,9 +30,6 @@ public: */ std::string GetUserMacrosRegKeyBase() override; -protected: - const char* GetIDEVersion() const override { return "9.0"; } - private: class Factory; friend class Factory; diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index da3daf8..adf0a81 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx @@ -43,6 +43,77 @@ void cmGlobalVisualStudioGenerator::SetVersion(VSVersion v) this->Version = v; } +const char* cmGlobalVisualStudioGenerator::GetIDEVersion() const +{ + switch (this->Version) { + case cmGlobalVisualStudioGenerator::VS9: + return "9.0"; + case cmGlobalVisualStudioGenerator::VS10: + return "10.0"; + case cmGlobalVisualStudioGenerator::VS11: + return "11.0"; + case cmGlobalVisualStudioGenerator::VS12: + return "12.0"; + case cmGlobalVisualStudioGenerator::VS14: + return "14.0"; + case cmGlobalVisualStudioGenerator::VS15: + return "15.0"; + } + return ""; +} + +void cmGlobalVisualStudioGenerator::WriteSLNHeader(std::ostream& fout) +{ + switch (this->Version) { + case cmGlobalVisualStudioGenerator::VS9: + fout << "Microsoft Visual Studio Solution File, Format Version 10.00\n"; + fout << "# Visual Studio 2008\n"; + break; + case cmGlobalVisualStudioGenerator::VS10: + fout << "Microsoft Visual Studio Solution File, Format Version 11.00\n"; + if (this->ExpressEdition) { + fout << "# Visual C++ Express 2010\n"; + } else { + fout << "# Visual Studio 2010\n"; + } + break; + case cmGlobalVisualStudioGenerator::VS11: + fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; + if (this->ExpressEdition) { + fout << "# Visual Studio Express 2012 for Windows Desktop\n"; + } else { + fout << "# Visual Studio 2012\n"; + } + break; + case cmGlobalVisualStudioGenerator::VS12: + fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; + if (this->ExpressEdition) { + fout << "# Visual Studio Express 2013 for Windows Desktop\n"; + } else { + fout << "# Visual Studio 2013\n"; + } + break; + case cmGlobalVisualStudioGenerator::VS14: + // Visual Studio 14 writes .sln format 12.00 + fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; + if (this->ExpressEdition) { + fout << "# Visual Studio Express 14 for Windows Desktop\n"; + } else { + fout << "# Visual Studio 14\n"; + } + break; + case cmGlobalVisualStudioGenerator::VS15: + // Visual Studio 15 writes .sln format 12.00 + fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; + if (this->ExpressEdition) { + fout << "# Visual Studio Express 15 for Windows Desktop\n"; + } else { + fout << "# Visual Studio 15\n"; + } + break; + } +} + std::string cmGlobalVisualStudioGenerator::GetRegistryBase() { return cmGlobalVisualStudioGenerator::GetRegistryBase(this->GetIDEVersion()); diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h index c891160..0d4491d 100644 --- a/Source/cmGlobalVisualStudioGenerator.h +++ b/Source/cmGlobalVisualStudioGenerator.h @@ -47,6 +47,9 @@ public: VSVersion GetVersion() const; void SetVersion(VSVersion v); + /** Is the installed VS an Express edition? */ + bool IsExpressEdition() const { return this->ExpressEdition; } + /** * Configure CMake's Visual Studio macros file into the user's Visual * Studio macros directory. @@ -137,7 +140,9 @@ protected: // below 8. virtual bool VSLinksDependencies() const { return true; } - virtual const char* GetIDEVersion() const = 0; + const char* GetIDEVersion() const; + + void WriteSLNHeader(std::ostream& fout); bool ComputeTargetDepends() override; class VSDependSet : public std::set<std::string> @@ -159,6 +164,7 @@ protected: protected: VSVersion Version; + bool ExpressEdition; private: virtual std::string GetVSMakeProgram() = 0; diff --git a/Source/cmQtAutoGenGlobalInitializer.cxx b/Source/cmQtAutoGenGlobalInitializer.cxx index 5470ec3..678ff14 100644 --- a/Source/cmQtAutoGenGlobalInitializer.cxx +++ b/Source/cmQtAutoGenGlobalInitializer.cxx @@ -75,13 +75,26 @@ cmQtAutoGenGlobalInitializer::cmQtAutoGenGlobalInitializer( bool const uic = target->GetPropertyAsBool("AUTOUIC"); bool const rcc = target->GetPropertyAsBool("AUTORCC"); if (moc || uic || rcc) { - // We support Qt4 and Qt5 + std::string const mocExec = + target->GetSafeProperty("AUTOMOC_EXECUTABLE"); + std::string const uicExec = + target->GetSafeProperty("AUTOUIC_EXECUTABLE"); + std::string const rccExec = + target->GetSafeProperty("AUTORCC_EXECUTABLE"); + + // We support Qt4, Qt5 and Qt6 auto qtVersion = cmQtAutoGenInitializer::GetQtVersion(target); - if ((qtVersion.Major == 4) || (qtVersion.Major == 5)) { + bool const validQt = (qtVersion.Major == 4) || + (qtVersion.Major == 5) || (qtVersion.Major == 6); + bool const mocIsValid = moc && (validQt || !mocExec.empty()); + bool const uicIsValid = uic && (validQt || !uicExec.empty()); + bool const rccIsValid = rcc && (validQt || !rccExec.empty()); + + if (mocIsValid || uicIsValid || rccIsValid) { // Create autogen target initializer Initializers_.emplace_back(cm::make_unique<cmQtAutoGenInitializer>( - this, target, qtVersion, moc, uic, rcc, globalAutoGenTarget, - globalAutoRccTarget)); + this, target, qtVersion, mocIsValid, uicIsValid, rccIsValid, + globalAutoGenTarget, globalAutoRccTarget)); } } } diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index 6a2a951..e4d2c82 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -9,6 +9,7 @@ #include "cmCustomCommandLines.h" #include "cmDuration.h" #include "cmFilePathChecksum.h" +#include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmLinkItem.h" @@ -42,6 +43,9 @@ std::string GetQtExecutableTargetName( const cmQtAutoGen::IntegerVersion& qtVersion, std::string const& executable) { + if (qtVersion.Major == 6) { + return ("Qt6::" + executable); + } if (qtVersion.Major == 5) { return ("Qt5::" + executable); } @@ -395,8 +399,16 @@ bool cmQtAutoGenInitializer::InitCustomTargets() } // Init uic specific settings - if (this->Uic.Enabled && !InitUic()) { - return false; + if (this->Uic.Enabled) { + if (InitUic()) { + auto* uicTarget = makefile->FindTargetToUse( + GetQtExecutableTargetName(this->QtVersion, "uic")); + if (uicTarget != nullptr) { + this->AutogenTarget.DependTargets.insert(uicTarget); + } + } else { + return false; + } } // Autogen target name @@ -437,6 +449,12 @@ bool cmQtAutoGenInitializer::InitCustomTargets() this->AutogenTarget.DependOrigin = this->Target->GetPropertyAsBool("AUTOGEN_ORIGIN_DEPENDS"); + auto* mocTarget = makefile->FindTargetToUse( + GetQtExecutableTargetName(this->QtVersion, "moc")); + if (mocTarget != nullptr) { + this->AutogenTarget.DependTargets.insert(mocTarget); + } + std::string const deps = this->Target->GetSafeProperty("AUTOGEN_TARGET_DEPENDS"); if (!deps.empty()) { @@ -504,7 +522,7 @@ bool cmQtAutoGenInitializer::InitMoc() { // We need to disable this until we have all implicit includes available. // See issue #18669. - // bool const appendImplicit = (this->QtVersion.Major == 5); + // bool const appendImplicit = (this->QtVersion.Major >= 5); auto GetIncludeDirs = [this, localGen](std::string const& cfg) -> std::vector<std::string> { @@ -839,7 +857,7 @@ bool cmQtAutoGenInitializer::InitScanFiles() // Process qrc files if (!this->Rcc.Qrcs.empty()) { - const bool QtV5 = (this->QtVersion.Major == 5); + const bool modernQt = (this->QtVersion.Major >= 5); // Target rcc options std::vector<std::string> optionsTarget; cmSystemTools::ExpandListArgument( @@ -911,10 +929,10 @@ bool cmQtAutoGenInitializer::InitScanFiles() std::vector<std::string> nameOpts; nameOpts.emplace_back("-name"); nameOpts.emplace_back(std::move(name)); - RccMergeOptions(opts, nameOpts, QtV5); + RccMergeOptions(opts, nameOpts, modernQt); } // Merge file option - RccMergeOptions(opts, qrc.Options, QtV5); + RccMergeOptions(opts, qrc.Options, modernQt); qrc.Options = std::move(opts); } // RCC resources @@ -1092,6 +1110,7 @@ bool cmQtAutoGenInitializer::InitRccTargets() { cmMakefile* makefile = this->Target->Target->GetMakefile(); cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); + auto rccTargetName = GetQtExecutableTargetName(this->QtVersion, "rcc"); for (Qrc const& qrc : this->Rcc.Qrcs) { // Register info file as generated by CMake @@ -1102,6 +1121,11 @@ bool cmQtAutoGenInitializer::InitRccTargets() std::vector<std::string> ccOutput; ccOutput.push_back(qrc.RccFile); + std::vector<std::string> ccDepends; + // Add the .qrc and info file to the custom command dependencies + ccDepends.push_back(qrc.QrcFile); + ccDepends.push_back(qrc.InfoFile); + cmCustomCommandLines commandLines; if (this->MultiConfig) { // Build for all configurations @@ -1137,15 +1161,12 @@ bool cmQtAutoGenInitializer::InitRccTargets() ccName += "_"; ccName += qrc.PathChecksum; } - std::vector<std::string> ccDepends; - // Add the .qrc and info file to the custom target dependencies - ccDepends.push_back(qrc.QrcFile); - ccDepends.push_back(qrc.InfoFile); cmTarget* autoRccTarget = makefile->AddUtilityCommand( ccName, cmMakefile::TargetOrigin::Generator, true, this->Dir.Work.c_str(), ccOutput, ccDepends, commandLines, false, ccComment.c_str()); + // Create autogen generator target localGen->AddGeneratorTarget( new cmGeneratorTarget(autoRccTarget, localGen)); @@ -1154,6 +1175,9 @@ bool cmQtAutoGenInitializer::InitRccTargets() if (!this->TargetsFolder.empty()) { autoRccTarget->SetProperty("FOLDER", this->TargetsFolder.c_str()); } + if (!rccTargetName.empty()) { + autoRccTarget->AddUtility(rccTargetName, makefile); + } } // Add autogen target to the origin target dependencies this->Target->Target->AddUtility(ccName, makefile); @@ -1166,16 +1190,15 @@ bool cmQtAutoGenInitializer::InitRccTargets() // Create custom rcc command { std::vector<std::string> ccByproducts; - std::vector<std::string> ccDepends; - // Add the .qrc and info file to the custom command dependencies - ccDepends.push_back(qrc.QrcFile); - ccDepends.push_back(qrc.InfoFile); // Add the resource files to the dependencies for (std::string const& fileName : qrc.Resources) { // Add resource file to the custom command dependencies ccDepends.push_back(fileName); } + if (!rccTargetName.empty()) { + ccDepends.push_back(rccTargetName); + } makefile->AddCustomCommandToOutput(ccOutput, ccByproducts, ccDepends, /*main_dependency*/ std::string(), commandLines, ccComment.c_str(), @@ -1374,7 +1397,7 @@ static std::vector<cmQtAutoGenInitializer::IntegerVersion> GetKnownQtVersions( std::vector<cmQtAutoGenInitializer::IntegerVersion> result; for (const std::string& prefix : - std::vector<std::string>({ "Qt5Core", "QT" })) { + std::vector<std::string>({ "Qt6Core", "Qt5Core", "QT" })) { auto tmp = cmQtAutoGenInitializer::IntegerVersion( StringToInt(makefile->GetSafeDefinition(prefix + "_VERSION_MAJOR")), StringToInt(makefile->GetSafeDefinition(prefix + "_VERSION_MINOR"))); @@ -1418,21 +1441,36 @@ std::pair<bool, std::string> GetQtExecutable( const cmQtAutoGen::IntegerVersion& qtVersion, cmGeneratorTarget* target, const std::string& executable, bool ignoreMissingTarget, std::string* output) { + const std::string upperExecutable = cmSystemTools::UpperCase(executable); + std::string result = + target->Target->GetSafeProperty("AUTO" + upperExecutable + "_EXECUTABLE"); + if (!result.empty()) { + cmListFileBacktrace lfbt = target->Target->GetMakefile()->GetBacktrace(); + cmGeneratorExpression ge(lfbt); + std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(result); + result = cge->Evaluate(target->GetLocalGenerator(), ""); + + return std::make_pair(true, result); + } + std::string err; - std::string result; // Find executable { const std::string targetName = GetQtExecutableTargetName(qtVersion, executable); if (targetName.empty()) { - err = "The AUTOMOC, AUTOUIC and AUTORCC feature "; - err += "supports only Qt 4 and Qt 5"; + err = "The AUTO" + upperExecutable + " feature "; + err += "supports only Qt 4, Qt 5 and Qt 6."; } else { cmLocalGenerator* localGen = target->GetLocalGenerator(); cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse(targetName); if (tgt != nullptr) { - result = tgt->ImportedGetLocation(""); + if (tgt->IsImported()) { + result = tgt->ImportedGetLocation(""); + } else { + result = tgt->GetLocation(""); + } } else { if (ignoreMissingTarget) { return std::make_pair(true, ""); @@ -1510,7 +1548,7 @@ bool cmQtAutoGenInitializer::GetRccExecutable() return false; } - if (this->QtVersion.Major == 5) { + if (this->QtVersion.Major == 5 || this->QtVersion.Major == 6) { if (stdOut.find("--list") != std::string::npos) { this->Rcc.ListOptions.push_back("--list"); } else { diff --git a/Source/cmTestGenerator.cxx b/Source/cmTestGenerator.cxx index 796d2df..e4ced6e 100644 --- a/Source/cmTestGenerator.cxx +++ b/Source/cmTestGenerator.cxx @@ -7,14 +7,16 @@ #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" +#include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmOutputConverter.h" #include "cmProperty.h" -#include "cmPropertyMap.h" #include "cmStateTypes.h" #include "cmSystemTools.h" #include "cmTest.h" +class cmPropertyMap; + cmTestGenerator::cmTestGenerator( cmTest* test, std::vector<std::string> const& configurations) : cmScriptGenerator("CTEST_CONFIGURATION_TYPE", configurations) @@ -121,16 +123,15 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os, // Output properties for the test. cmPropertyMap& pm = this->Test->GetProperties(); - if (!pm.empty()) { - os << indent << "set_tests_properties(" << this->Test->GetName() - << " PROPERTIES "; - for (auto const& i : pm) { - os << " " << i.first << " " - << cmOutputConverter::EscapeForCMake( - ge.Parse(i.second.GetValue())->Evaluate(this->LG, config)); - } - os << ")" << std::endl; + os << indent << "set_tests_properties(" << this->Test->GetName() + << " PROPERTIES "; + for (auto const& i : pm) { + os << " " << i.first << " " + << cmOutputConverter::EscapeForCMake( + ge.Parse(i.second.GetValue())->Evaluate(this->LG, config)); } + this->GenerateInternalProperties(os); + os << ")" << std::endl; } void cmTestGenerator::GenerateScriptNoConfig(std::ostream& os, Indent indent) @@ -179,13 +180,37 @@ void cmTestGenerator::GenerateOldStyle(std::ostream& fout, Indent indent) // Output properties for the test. cmPropertyMap& pm = this->Test->GetProperties(); - if (!pm.empty()) { - fout << indent << "set_tests_properties(" << this->Test->GetName() - << " PROPERTIES "; - for (auto const& i : pm) { - fout << " " << i.first << " " - << cmOutputConverter::EscapeForCMake(i.second.GetValue()); + fout << indent << "set_tests_properties(" << this->Test->GetName() + << " PROPERTIES "; + for (auto const& i : pm) { + fout << " " << i.first << " " + << cmOutputConverter::EscapeForCMake(i.second.GetValue()); + } + this->GenerateInternalProperties(fout); + fout << ")" << std::endl; +} + +void cmTestGenerator::GenerateInternalProperties(std::ostream& os) +{ + cmListFileBacktrace bt = this->Test->GetBacktrace(); + if (bt.Empty()) { + return; + } + + os << " " + << "_BACKTRACE_TRIPLES" + << " \""; + + bool prependTripleSeparator = false; + while (!bt.Empty()) { + const auto& entry = bt.Top(); + if (prependTripleSeparator) { + os << ";"; } - fout << ")" << std::endl; + os << entry.FilePath << ";" << entry.Line << ";" << entry.Name; + bt = bt.Pop(); + prependTripleSeparator = true; } + + os << "\""; } diff --git a/Source/cmTestGenerator.h b/Source/cmTestGenerator.h index 73d05a3..f26d2ff 100644 --- a/Source/cmTestGenerator.h +++ b/Source/cmTestGenerator.h @@ -35,6 +35,9 @@ public: cmTest* GetTest() const; +private: + void GenerateInternalProperties(std::ostream& os); + protected: void GenerateScriptConfigs(std::ostream& os, Indent indent) override; void GenerateScriptActions(std::ostream& os, Indent indent) override; diff --git a/Source/cmVSSetupHelper.cxx b/Source/cmVSSetupHelper.cxx index 7a54e12..d80b5a2 100644 --- a/Source/cmVSSetupHelper.cxx +++ b/Source/cmVSSetupHelper.cxx @@ -328,6 +328,9 @@ bool cmVSSetupAPIHelper::EnumerateAndChooseVSInstance() return false; } + // FIXME: Add a way for caller to specify other versions. + std::wstring wantVersion = std::to_wstring(15) + L'.'; + SmartCOMPtr<ISetupInstance> instance; while (SUCCEEDED(enumInstances->Next(1, &instance, NULL)) && instance) { SmartCOMPtr<ISetupInstance2> instance2 = NULL; @@ -343,6 +346,12 @@ bool cmVSSetupAPIHelper::EnumerateAndChooseVSInstance() instance = instance2 = NULL; if (isInstalled) { + // We are looking for a specific major version. + if (instanceInfo.Version.size() < wantVersion.size() || + instanceInfo.Version.substr(0, wantVersion.size()) != wantVersion) { + continue; + } + if (!this->SpecifiedVSInstallLocation.empty()) { // We are looking for a specific instance. std::string currentVSLocation = instanceInfo.GetInstallLocation(); diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx index 5d67dcf..afe9230 100644 --- a/Source/cmVisualStudioGeneratorOptions.cxx +++ b/Source/cmVisualStudioGeneratorOptions.cxx @@ -269,7 +269,7 @@ void cmVisualStudioGeneratorOptions::FixManifestUACFlags() if (keyValue[1].front() == '\'' && keyValue[1].back() == '\'') { keyValue[1] = - keyValue[1].substr(1, std::max<int>(0, keyValue[1].size() - 2)); + keyValue[1].substr(1, std::max(0, cm::isize(keyValue[1]) - 2)); } if (keyValue[0] == "level") { diff --git a/Source/cmWriteFileCommand.cxx b/Source/cmWriteFileCommand.cxx index c504ef4..49dbf1a 100644 --- a/Source/cmWriteFileCommand.cxx +++ b/Source/cmWriteFileCommand.cxx @@ -50,7 +50,7 @@ bool cmWriteFileCommand::InitialPass(std::vector<std::string> const& args, // Set permissions to writable if (cmSystemTools::GetPermissions(fileName.c_str(), mode)) { #if defined(_MSC_VER) || defined(__MINGW32__) - writable = mode & S_IWRITE; + writable = (mode & S_IWRITE) != 0; mode_t newMode = mode | S_IWRITE; #else writable = mode & S_IWUSR; diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 1bc36cc..e1bae34 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -1528,7 +1528,6 @@ void cmake::CreateDefaultGlobalGenerator() const char* GeneratorName; }; static VSVersionedGenerator const vsGenerators[] = { - { "15.0", "Visual Studio 15 2017" }, // { "14.0", "Visual Studio 14 2015" }, // { "12.0", "Visual Studio 12 2013" }, // { "11.0", "Visual Studio 11 2012" }, // diff --git a/Source/cmparseMSBuildXML.py b/Source/cmparseMSBuildXML.py deleted file mode 100755 index 1b38d15..0000000 --- a/Source/cmparseMSBuildXML.py +++ /dev/null @@ -1,341 +0,0 @@ -# This python script parses the spec files from MSBuild to create -# mappings from compiler options to IDE XML specifications. For -# more information see here: - -# http://blogs.msdn.com/vcblog/archive/2008/12/16/msbuild-task.aspx -# "${PROGRAMFILES}/MSBuild/Microsoft.Cpp/v4.0/1033/cl.xml" -# "${PROGRAMFILES}/MSBuild/Microsoft.Cpp/v4.0/1033/lib.xml" -# "${PROGRAMFILES}/MSBuild/Microsoft.Cpp/v4.0/1033/link.xml" -# "${PROGRAMFILES}/MSBuild/Microsoft.Cpp/v4.0/V110/1033/cl.xml" -# "${PROGRAMFILES}/MSBuild/Microsoft.Cpp/v4.0/V110/1033/lib.xml" -# "${PROGRAMFILES}/MSBuild/Microsoft.Cpp/v4.0/V110/1033/link.xml" -# "${PROGRAMFILES}/MSBuild/Microsoft.Cpp/v4.0/v120/1033/cl.xml" -# "${PROGRAMFILES}/MSBuild/Microsoft.Cpp/v4.0/v120/1033/lib.xml" -# "${PROGRAMFILES}/MSBuild/Microsoft.Cpp/v4.0/v120/1033/link.xml" -# "${PROGRAMFILES}/MSBuild/Microsoft.Cpp/v4.0/V140/1033/cl.xml" -# "${PROGRAMFILES}/MSBuild/Microsoft.Cpp/v4.0/V140/1033/lib.xml" -# "${PROGRAMFILES}/MSBuild/Microsoft.Cpp/v4.0/V140/1033/link.xml" -# "${PROGRAMFILES}/Microsoft Visual Studio/VS15Preview/Common7/IDE/VC/VCTargets/1033/cl.xml" -# "${PROGRAMFILES}/Microsoft Visual Studio/VS15Preview/Common7/IDE/VC/VCTargets/1033/lib.xml" -# "${PROGRAMFILES}/Microsoft Visual Studio/VS15Preview/Common7/IDE/VC/VCTargets/1033/link.xml" -# -# BoolProperty <Name>true|false</Name> -# simple example: -# <BoolProperty ReverseSwitch="Oy-" Name="OmitFramePointers" -# Category="Optimization" Switch="Oy"> -# <BoolProperty.DisplayName> <BoolProperty.Description> -# <CLCompile> -# <OmitFramePointers>true</OmitFramePointers> -# </ClCompile> -# -# argument means it might be this: /MP3 -# example with argument: -# <BoolProperty Name="MultiProcessorCompilation" Category="General" Switch="MP"> -# <BoolProperty.DisplayName> -# <sys:String>Multi-processor Compilation</sys:String> -# </BoolProperty.DisplayName> -# <BoolProperty.Description> -# <sys:String>Multi-processor Compilation</sys:String> -# </BoolProperty.Description> -# <Argument Property="ProcessorNumber" IsRequired="false" /> -# </BoolProperty> -# <CLCompile> -# <MultiProcessorCompilation>true</MultiProcessorCompilation> -# <ProcessorNumber>4</ProcessorNumber> -# </ClCompile> -# IntProperty -# not used AFIT -# <IntProperty Name="ProcessorNumber" Category="General" Visible="false"> - - -# per config options example -# <EnableFiberSafeOptimizations Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</EnableFiberSafeOptimizations> -# -# EnumProperty -# <EnumProperty Name="Optimization" Category="Optimization"> -# <EnumProperty.DisplayName> -# <sys:String>Optimization</sys:String> -# </EnumProperty.DisplayName> -# <EnumProperty.Description> -# <sys:String>Select option for code optimization; choose Custom to use specific optimization options. (/Od, /O1, /O2, /Ox)</sys:String> -# </EnumProperty.Description> -# <EnumValue Name="MaxSpeed" Switch="O2"> -# <EnumValue.DisplayName> -# <sys:String>Maximize Speed</sys:String> -# </EnumValue.DisplayName> -# <EnumValue.Description> -# <sys:String>Equivalent to /Og /Oi /Ot /Oy /Ob2 /Gs /GF /Gy</sys:String> -# </EnumValue.Description> -# </EnumValue> -# <EnumValue Name="MinSpace" Switch="O1"> -# <EnumValue.DisplayName> -# <sys:String>Minimize Size</sys:String> -# </EnumValue.DisplayName> -# <EnumValue.Description> -# <sys:String>Equivalent to /Og /Os /Oy /Ob2 /Gs /GF /Gy</sys:String> -# </EnumValue.Description> -# </EnumValue> -# example for O2 would be this: -# <Optimization>MaxSpeed</Optimization> -# example for O1 would be this: -# <Optimization>MinSpace</Optimization> -# -# StringListProperty -# <StringListProperty Name="PreprocessorDefinitions" Category="Preprocessor" Switch="D "> -# <StringListProperty.DisplayName> -# <sys:String>Preprocessor Definitions</sys:String> -# </StringListProperty.DisplayName> -# <StringListProperty.Description> -# <sys:String>Defines a preprocessing symbols for your source file.</sys:String> -# </StringListProperty.Description> -# </StringListProperty> - -# <StringListProperty Subtype="folder" Name="AdditionalIncludeDirectories" Category="General" Switch="I"> -# <StringListProperty.DisplayName> -# <sys:String>Additional Include Directories</sys:String> -# </StringListProperty.DisplayName> -# <StringListProperty.Description> -# <sys:String>Specifies one or more directories to add to the include path; separate with semi-colons if more than one. (/I[path])</sys:String> -# </StringListProperty.Description> -# </StringListProperty> -# StringProperty - -# Example add bill include: - -# <AdditionalIncludeDirectories>..\..\..\..\..\..\bill;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - - -import sys -from xml.dom.minidom import parse, parseString - -def getText(node): - nodelist = node.childNodes - rc = "" - for child in nodelist: - if child.nodeType == child.TEXT_NODE: - rc = rc + child.data - return rc - -def print_tree(document, spaces=""): - for i in range(len(document.childNodes)): - if document.childNodes[i].nodeType == document.childNodes[i].ELEMENT_NODE: - print spaces+str(document.childNodes[i].nodeName ) - print_tree(document.childNodes[i],spaces+"----") - pass - -########################################################################################### -#Data structure that stores a property of MSBuild -class Property: - #type = type of MSBuild property (ex. if the property is EnumProperty type should be "Enum") - #attributeNames = a list of any attributes that this property could have (ex. if this was a EnumProperty it should be ["Name","Category"]) - #document = the dom file that's root node is the Property node (ex. if you were parsing a BoolProperty the root node should be something like <BoolProperty Name="RegisterOutput" Category="General" IncludeInCommandLine="false"> - def __init__(self,type,attributeNames,document=None): - self.suffix_type = "Property" - self.prefix_type = type - self.attributeNames = attributeNames - self.attributes = {} - self.DisplayName = "" - self.Description = "" - self.argumentProperty = "" - self.argumentIsRequired = "" - self.values = [] - if document is not None: - self.populate(document) - pass - - #document = the dom file that's root node is the Property node (ex. if you were parsing a BoolProperty the root node should be something like <BoolProperty Name="RegisterOutput" Category="General" IncludeInCommandLine="false"> - #spaces = do not use - def populate(self,document, spaces = ""): - if document.nodeName == self.prefix_type+self.suffix_type: - for i in self.attributeNames: - self.attributes[i] = document.getAttribute(i) - for i in range(len(document.childNodes)): - child = document.childNodes[i] - if child.nodeType == child.ELEMENT_NODE: - if child.nodeName == self.prefix_type+self.suffix_type+".DisplayName": - self.DisplayName = getText(child.childNodes[1]) - if child.nodeName == self.prefix_type+self.suffix_type+".Description": - self.Description = getText(child.childNodes[1]) - if child.nodeName == "Argument": - self.argumentProperty = child.getAttribute("Property") - self.argumentIsRequired = child.getAttribute("IsRequired") - if child.nodeName == self.prefix_type+"Value": - va = Property(self.prefix_type,["Name","DisplayName","Switch"]) - va.suffix_type = "Value" - va.populate(child) - self.values.append(va) - self.populate(child,spaces+"----") - pass - - #toString function - def __str__(self): - toReturn = self.prefix_type+self.suffix_type+":" - for i in self.attributeNames: - toReturn += "\n "+i+": "+self.attributes[i] - if self.argumentProperty != "": - toReturn += "\n Argument:\n Property: "+self.argumentProperty+"\n IsRequired: "+self.argumentIsRequired - for i in self.values: - toReturn+="\n "+str(i).replace("\n","\n ") - return toReturn -########################################################################################### - -########################################################################################### -#Class that populates itself from an MSBuild file and outputs it in CMake -#format - -class MSBuildToCMake: - #document = the entire MSBuild xml file - def __init__(self,document=None): - self.enumProperties = [] - self.stringProperties = [] - self.stringListProperties = [] - self.boolProperties = [] - self.intProperties = [] - if document!=None : - self.populate(document) - pass - - #document = the entire MSBuild xml file - #spaces = don't use - #To add a new property (if they exist) copy and paste this code and fill in appropriate places - # - #if child.nodeName == "<Name>Property": - # self.<Name>Properties.append(Property("<Name>",[<List of attributes>],child)) - # - #Replace <Name> with the name of the new property (ex. if property is StringProperty replace <Name> with String) - #Replace <List of attributes> with a list of attributes in your property's root node - #in the __init__ function add the line self.<Name>Properties = [] - # - #That is all that is required to add new properties - # - def populate(self,document, spaces=""): - for i in range(len(document.childNodes)): - child = document.childNodes[i] - if child.nodeType == child.ELEMENT_NODE: - if child.nodeName == "EnumProperty": - self.enumProperties.append(Property("Enum",["Name","Category"],child)) - if child.nodeName == "StringProperty": - self.stringProperties.append(Property("String",["Name","Subtype","Separator","Category","Visible","IncludeInCommandLine","Switch","DisplayName","ReadOnly"],child)) - if child.nodeName == "StringListProperty": - self.stringListProperties.append(Property("StringList",["Name","Category","Switch","DisplayName","Subtype"],child)) - if child.nodeName == "BoolProperty": - self.boolProperties.append(Property("Bool",["ReverseSwitch","Name","Category","Switch","DisplayName","SwitchPrefix","IncludeInCommandLine"],child)) - if child.nodeName == "IntProperty": - self.intProperties.append(Property("Int",["Name","Category","Visible"],child)) - self.populate(child,spaces+"----") - pass - - #outputs information that CMake needs to know about MSBuild xml files - def toCMake(self): - toReturn = "static cmVS7FlagTable cmVS10CxxTable[] =\n{\n" - toReturn += "\n //Enum Properties\n" - lastProp = {} - for i in self.enumProperties: - if i.attributes["Name"] == "CompileAsManaged": - #write these out after the rest of the enumProperties - lastProp = i - continue - for j in i.values: - #hardcore Brad King's manual fixes for cmVS10CLFlagTable.h - if i.attributes["Name"] == "PrecompiledHeader" and j.attributes["Switch"] != "": - toReturn+=" {\""+i.attributes["Name"]+"\", \""+j.attributes["Switch"]+"\",\n \""+j.attributes["DisplayName"]+"\", \""+j.attributes["Name"]+"\",\n cmVS7FlagTable::UserValueIgnored | cmVS7FlagTable::Continue},\n" - else: - #default (normal, non-hardcoded) case - toReturn+=" {\""+i.attributes["Name"]+"\", \""+j.attributes["Switch"]+"\",\n \""+j.attributes["DisplayName"]+"\", \""+j.attributes["Name"]+"\", 0},\n" - toReturn += "\n" - - if lastProp != {}: - for j in lastProp.values: - toReturn+=" {\""+lastProp.attributes["Name"]+"\", \""+j.attributes["Switch"]+"\",\n \""+j.attributes["DisplayName"]+"\", \""+j.attributes["Name"]+"\", 0},\n" - toReturn += "\n" - - toReturn += "\n //Bool Properties\n" - for i in self.boolProperties: - if i.argumentProperty == "": - if i.attributes["ReverseSwitch"] != "": - toReturn += " {\""+i.attributes["Name"]+"\", \""+i.attributes["ReverseSwitch"]+"\", \"\", \"false\", 0},\n" - if i.attributes["Switch"] != "": - toReturn += " {\""+i.attributes["Name"]+"\", \""+i.attributes["Switch"]+"\", \"\", \"true\", 0},\n" - - toReturn += "\n //Bool Properties With Argument\n" - for i in self.boolProperties: - if i.argumentProperty != "": - if i.attributes["ReverseSwitch"] != "": - toReturn += " {\""+i.attributes["Name"]+"\", \""+i.attributes["ReverseSwitch"]+"\", \"\", \"false\",\n cmVS7FlagTable::UserValueIgnored | cmVS7FlagTable::Continue},\n" - toReturn += " {\""+i.attributes["Name"]+"\", \""+i.attributes["ReverseSwitch"]+"\", \""+i.attributes["DisplayName"]+"\", \"\",\n cmVS7FlagTable::UserValueRequired},\n" - if i.attributes["Switch"] != "": - toReturn += " {\""+i.attributes["Name"]+"\", \""+i.attributes["Switch"]+"\", \"\", \"true\",\n cmVS7FlagTable::UserValueIgnored | cmVS7FlagTable::Continue},\n" - toReturn += " {\""+i.argumentProperty+"\", \""+i.attributes["Switch"]+"\", \""+i.attributes["DisplayName"]+"\", \"\",\n cmVS7FlagTable::UserValueRequired},\n" - - toReturn += "\n //String List Properties\n" - for i in self.stringListProperties: - if i.attributes["Switch"] == "": - toReturn += " // Skip [" + i.attributes["Name"] + "] - no command line Switch.\n"; - else: - toReturn +=" {\""+i.attributes["Name"]+"\", \""+i.attributes["Switch"]+"\",\n \""+i.attributes["DisplayName"]+"\",\n \"\", cmVS7FlagTable::UserValue | cmVS7FlagTable::SemicolonAppendable},\n" - - toReturn += "\n //String Properties\n" - for i in self.stringProperties: - if i.attributes["Switch"] == "": - if i.attributes["Name"] == "PrecompiledHeaderFile": - #more hardcoding - toReturn += " {\"PrecompiledHeaderFile\", \"Yc\",\n" - toReturn += " \"Precompiled Header Name\",\n" - toReturn += " \"\", cmVS7FlagTable::UserValueRequired},\n" - toReturn += " {\"PrecompiledHeaderFile\", \"Yu\",\n" - toReturn += " \"Precompiled Header Name\",\n" - toReturn += " \"\", cmVS7FlagTable::UserValueRequired},\n" - else: - toReturn += " // Skip [" + i.attributes["Name"] + "] - no command line Switch.\n"; - else: - toReturn +=" {\""+i.attributes["Name"]+"\", \""+i.attributes["Switch"]+i.attributes["Separator"]+"\",\n \""+i.attributes["DisplayName"]+"\",\n \"\", cmVS7FlagTable::UserValue},\n" - - toReturn += " {0,0,0,0,0}\n};" - return toReturn - pass - - #toString function - def __str__(self): - toReturn = "" - allList = [self.enumProperties,self.stringProperties,self.stringListProperties,self.boolProperties,self.intProperties] - for p in allList: - for i in p: - toReturn += "==================================================\n"+str(i).replace("\n","\n ")+"\n==================================================\n" - - return toReturn -########################################################################################### - -########################################################################################### -# main function -def main(argv): - xml_file = None - help = """ - Please specify an input xml file with -x - - Exiting... - Have a nice day :)""" - for i in range(0,len(argv)): - if argv[i] == "-x": - xml_file = argv[i+1] - if argv[i] == "-h": - print help - sys.exit(0) - pass - if xml_file == None: - print help - sys.exit(1) - - f = open(xml_file,"r") - xml_str = f.read() - xml_dom = parseString(xml_str) - - convertor = MSBuildToCMake(xml_dom) - print convertor.toCMake() - - xml_dom.unlink() -########################################################################################### -# main entry point -if __name__ == "__main__": - main(sys.argv) - -sys.exit(0) diff --git a/Source/ctest.cxx b/Source/ctest.cxx index ca412ae..8ba126f 100644 --- a/Source/ctest.cxx +++ b/Source/ctest.cxx @@ -46,7 +46,10 @@ static const char* cmDocumentationOptions[][2] = { "given number of jobs." }, { "-Q,--quiet", "Make ctest quiet." }, { "-O <file>, --output-log <file>", "Output to log file" }, - { "-N,--show-only", "Disable actual execution of tests." }, + { "-N,--show-only[=format]", + "Disable actual execution of tests. The optional 'format' defines the " + "format of the test information and can be 'human' for the current text " + "format or 'json-v1' for json format. Defaults to 'human'." }, { "-L <regex>, --label-regex <regex>", "Run tests with labels matching " "regular expression." }, diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 67fd65a..e222376 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -385,8 +385,9 @@ add_RunCMake_test(CPackConfig) add_RunCMake_test(CPackInstallProperties) add_RunCMake_test(ExternalProject) add_RunCMake_test(FetchContent) +set(CTestCommandLine_ARGS -DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}) if(NOT CMake_TEST_EXTERNAL_CMAKE) - set(CTestCommandLine_ARGS -DTEST_AFFINITY=$<TARGET_FILE:testAffinity>) + list(APPEND CTestCommandLine_ARGS -DTEST_AFFINITY=$<TARGET_FILE:testAffinity>) endif() add_executable(print_stdin print_stdin.c) add_RunCMake_test(CTestCommandLine -DTEST_PRINT_STDIN=$<TARGET_FILE:print_stdin>) diff --git a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake index 750ae50..cae14b1 100644 --- a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake +++ b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake @@ -173,3 +173,32 @@ function(run_TestStdin) run_cmake_command(TestStdin ${CMAKE_CTEST_COMMAND} -V) endfunction() run_TestStdin() + +function(ShowAsJson_check_python v) + set(json_file "${RunCMake_TEST_BINARY_DIR}/ctest.json") + file(WRITE "${json_file}" "${actual_stdout}") + set(actual_stdout "" PARENT_SCOPE) + execute_process( + COMMAND ${PYTHON_EXECUTABLE} "${RunCMake_SOURCE_DIR}/ShowAsJson${v}-check.py" "${json_file}" + RESULT_VARIABLE result + OUTPUT_VARIABLE output + ERROR_VARIABLE output + ) + if(NOT result EQUAL 0) + string(REPLACE "\n" "\n " output " ${output}") + set(RunCMake_TEST_FAILED "Unexpected output:\n${output}" PARENT_SCOPE) + endif() +endfunction() + +function(run_ShowAsJson) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ShowAsJson) + set(RunCMake_TEST_NO_CLEAN 1) + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake" " + add_test(ShowAsJson \"${CMAKE_COMMAND}\" -E echo) + set_tests_properties(ShowAsJson PROPERTIES WILL_FAIL true _BACKTRACE_TRIPLES \"file1;1;add_test;file0;;\") +") + run_cmake_command(ShowAsJsonVersionOne ${CMAKE_CTEST_COMMAND} --show-only=json-v1) +endfunction() +run_ShowAsJson() diff --git a/Tests/RunCMake/CTestCommandLine/ShowAsJson1-check.py b/Tests/RunCMake/CTestCommandLine/ShowAsJson1-check.py new file mode 100644 index 0000000..d794e7d --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/ShowAsJson1-check.py @@ -0,0 +1,106 @@ +from ShowAsJson_check import * + +def check_kind(k): + assert is_string(k) + assert k == "ctestInfo" + +def check_version(v): + assert is_dict(v) + assert sorted(v.keys()) == ["major", "minor"] + assert is_int(v["major"]) + assert is_int(v["minor"]) + assert v["major"] == 1 + assert v["minor"] == 0 + +def check_backtracegraph(b): + assert is_dict(b) + assert sorted(b.keys()) == ["commands", "files", "nodes"] + check_backtracegraph_commands(b["commands"]) + check_backtracegraph_files(b["files"]) + check_backtracegraph_nodes(b["nodes"]) + +def check_backtracegraph_commands(c): + assert is_list(c) + assert len(c) == 1 + assert is_string(c[0]) + assert c[0] == "add_test" + +def check_backtracegraph_files(f): + assert is_list(f) + assert len(f) == 2 + assert is_string(f[0]) + assert is_string(f[1]) + assert f[0] == "file1" + assert f[1] == "file0" + +def check_backtracegraph_nodes(n): + assert is_list(n) + assert len(n) == 2 + node = n[0] + assert is_dict(node) + assert sorted(node.keys()) == ["file"] + assert is_int(node["file"]) + assert node["file"] == 1 + node = n[1] + assert is_dict(node) + assert sorted(node.keys()) == ["command", "file", "line", "parent"] + assert is_int(node["command"]) + assert is_int(node["file"]) + assert is_int(node["line"]) + assert is_int(node["parent"]) + assert node["command"] == 0 + assert node["file"] == 0 + assert node["line"] == 1 + assert node["parent"] == 0 + +def check_command(c): + assert is_list(c) + assert len(c) == 3 + assert is_string(c[0]) + check_re(c[0], "/cmake(\.exe)?$") + assert is_string(c[1]) + assert c[1] == "-E" + assert is_string(c[2]) + assert c[2] == "echo" + +def check_willfail_property(p): + assert is_dict(p) + assert sorted(p.keys()) == ["name", "value"] + assert is_string(p["name"]) + assert is_bool(p["value"]) + assert p["name"] == "WILL_FAIL" + assert p["value"] == True + +def check_workingdir_property(p): + assert is_dict(p) + assert sorted(p.keys()) == ["name", "value"] + assert is_string(p["name"]) + assert is_string(p["value"]) + assert p["name"] == "WORKING_DIRECTORY" + assert p["value"].endswith("Tests/RunCMake/CTestCommandLine/ShowAsJson") + +def check_properties(p): + assert is_list(p) + assert len(p) == 2 + check_willfail_property(p[0]) + check_workingdir_property(p[1]) + +def check_tests(t): + assert is_list(t) + assert len(t) == 1 + test = t[0] + assert is_dict(test) + assert sorted(test.keys()) == ["backtrace", "command", "name", "properties"] + assert is_int(test["backtrace"]) + assert test["backtrace"] == 1 + check_command(test["command"]) + assert is_string(test["name"]) + assert test["name"] == "ShowAsJson" + check_properties(test["properties"]) + +assert is_dict(ctest_json) +assert sorted(ctest_json.keys()) == ["backtraceGraph", "kind", "tests", "version"] +check_backtracegraph(ctest_json["backtraceGraph"]) +check_kind(ctest_json["kind"]) +check_version(ctest_json["version"]) +check_tests(ctest_json["tests"]) diff --git a/Tests/RunCMake/CTestCommandLine/ShowAsJson_check.py b/Tests/RunCMake/CTestCommandLine/ShowAsJson_check.py new file mode 100644 index 0000000..493c9e5 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/ShowAsJson_check.py @@ -0,0 +1,24 @@ +import sys +import json +import re + +def is_bool(x): + return isinstance(x, bool) + +def is_dict(x): + return isinstance(x, dict) + +def is_list(x): + return isinstance(x, list) + +def is_int(x): + return isinstance(x, int) or isinstance(x, long) + +def is_string(x): + return isinstance(x, str) or isinstance(x, unicode) + +def check_re(x, regex): + assert re.search(regex, x) + +with open(sys.argv[1]) as f: + ctest_json = json.load(f) diff --git a/Utilities/Sphinx/colors.py b/Utilities/Sphinx/colors.py index f98a483..dae0063 100644 --- a/Utilities/Sphinx/colors.py +++ b/Utilities/Sphinx/colors.py @@ -14,14 +14,14 @@ class CMakeTemplateStyle(Style): styles = { Whitespace: "#bbbbbb", Comment: "italic #408080", - Operator: "bold #000000", + Operator: "#555555", String: "#217A21", Number: "#105030", - Name.Builtin: "#400080", # anything lowercase - Name.Function: "bold #1010A0", # function + Name.Builtin: "#333333", # anything lowercase + Name.Function: "#007020", # function Name.Variable: "#1080B0", # <..> - Name.Tag: "#19177C", # ${..} - Name.Constant: "#6020E0", # uppercase only + Name.Tag: "#bb60d5", # ${..} + Name.Constant: "#4070a0", # uppercase only Name.Entity: "italic #70A020", # @..@ Name.Attribute: "#906060", # paths, URLs Name.Label: "#A0A000", # anything left over |