diff options
Diffstat (limited to 'Source')
-rw-r--r-- | Source/cmPolicies.h | 5 | ||||
-rw-r--r-- | Source/cmTestGenerator.cxx | 121 |
2 files changed, 116 insertions, 10 deletions
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index bba8b03..0c00ffb 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -323,7 +323,10 @@ class cmMakefile; 3, 18, 0, cmPolicies::WARN) \ SELECT(POLICY, CMP0109, \ "find_program() requires permission to execute but not to read.", 3, \ - 19, 0, cmPolicies::WARN) + 19, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0110, \ + "add_test() supports arbitrary characters in test names.", 3, 19, 0, \ + cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ diff --git a/Source/cmTestGenerator.cxx b/Source/cmTestGenerator.cxx index 78b230c..da4593f 100644 --- a/Source/cmTestGenerator.cxx +++ b/Source/cmTestGenerator.cxx @@ -2,8 +2,12 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmTestGenerator.h" +#include <algorithm> +#include <cstddef> // IWYU pragma: keep +#include <iterator> #include <memory> #include <ostream> +#include <string> #include <utility> #include <vector> @@ -11,7 +15,10 @@ #include "cmGeneratorTarget.h" #include "cmListFileCache.h" #include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" #include "cmOutputConverter.h" +#include "cmPolicies.h" #include "cmProperty.h" #include "cmPropertyMap.h" #include "cmRange.h" @@ -20,6 +27,52 @@ #include "cmSystemTools.h" #include "cmTest.h" +namespace /* anonymous */ +{ + +bool needToQuoteTestName(const cmMakefile& mf, const std::string& name) +{ + // Determine if policy CMP0110 is set to NEW. + switch (mf.GetPolicyStatus(cmPolicies::CMP0110)) { + case cmPolicies::WARN: + // Only warn if a forbidden character is used in the name. + if (name.find_first_of("$[] #;\t\n\"\\") != std::string::npos) { + mf.IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0110), + "\nThe following name given to add_test() is invalid if " + "CMP0110 is not set or set to OLD:\n `", + name, "ยด\n")); + } + CM_FALLTHROUGH; + case cmPolicies::OLD: + // OLD behavior is to not quote the test's name. + return false; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + default: + // NEW behavior is to quote the test's name. + return true; + } +} + +std::size_t countMaxConsecutiveEqualSigns(const std::string& name) +{ + std::size_t max = 0; + auto startIt = find(name.begin(), name.end(), '='); + auto endIt = startIt; + for (; startIt != name.end(); startIt = find(endIt, name.end(), '=')) { + endIt = + find_if_not(startIt + 1, name.end(), [](char c) { return c == '='; }); + max = + std::max(max, static_cast<std::size_t>(std::distance(startIt, endIt))); + } + return max; +} + +} // End: anonymous namespace + cmTestGenerator::cmTestGenerator( cmTest* test, std::vector<std::string> const& configurations) : cmScriptGenerator("CTEST_CONFIGURATION_TYPE", configurations) @@ -76,8 +129,21 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os, // Set up generator expression evaluation context. cmGeneratorExpression ge(this->Test->GetBacktrace()); + // Determine if policy CMP0110 is set to NEW. + const bool quote_test_name = + needToQuoteTestName(*this->Test->GetMakefile(), this->Test->GetName()); + // Determine the number of equal-signs needed for quoting test name with + // [==[...]==] syntax. + const std::string equalSigns( + 1 + countMaxConsecutiveEqualSigns(this->Test->GetName()), '='); + // Start the test command. - os << indent << "add_test(" << this->Test->GetName() << " "; + if (quote_test_name) { + os << indent << "add_test([" << equalSigns << "[" << this->Test->GetName() + << "]" << equalSigns << "] "; + } else { + os << indent << "add_test(" << this->Test->GetName() << " "; + } // Evaluate command line arguments std::vector<std::string> argv = @@ -127,8 +193,13 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os, os << ")\n"; // Output properties for the test. - os << indent << "set_tests_properties(" << this->Test->GetName() - << " PROPERTIES "; + if (quote_test_name) { + os << indent << "set_tests_properties([" << equalSigns << "[" + << this->Test->GetName() << "]" << equalSigns << "] PROPERTIES "; + } else { + os << indent << "set_tests_properties(" << this->Test->GetName() + << " PROPERTIES "; + } for (auto const& i : this->Test->GetProperties().GetList()) { os << " " << i.first << " " << cmOutputConverter::EscapeForCMake( @@ -140,7 +211,21 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os, void cmTestGenerator::GenerateScriptNoConfig(std::ostream& os, Indent indent) { - os << indent << "add_test(" << this->Test->GetName() << " NOT_AVAILABLE)\n"; + // Determine if policy CMP0110 is set to NEW. + const bool quote_test_name = + needToQuoteTestName(*this->Test->GetMakefile(), this->Test->GetName()); + // Determine the number of equal-signs needed for quoting test name with + // [==[...]==] syntax. + const std::string equalSigns( + 1 + countMaxConsecutiveEqualSigns(this->Test->GetName()), '='); + + if (quote_test_name) { + os << indent << "add_test([" << equalSigns << "[" << this->Test->GetName() + << "]" << equalSigns << "] NOT_AVAILABLE)\n"; + } else { + os << indent << "add_test(" << this->Test->GetName() + << " NOT_AVAILABLE)\n"; + } } bool cmTestGenerator::NeedsScriptNoConfig() const @@ -155,14 +240,27 @@ void cmTestGenerator::GenerateOldStyle(std::ostream& fout, Indent indent) { this->TestGenerated = true; + // Determine if policy CMP0110 is set to NEW. + const bool quote_test_name = + needToQuoteTestName(*this->Test->GetMakefile(), this->Test->GetName()); + // Determine the number of equal-signs needed for quoting test name with + // [==[...]==] syntax. + const std::string equalSigns( + 1 + countMaxConsecutiveEqualSigns(this->Test->GetName()), '='); + // Get the test command line to be executed. std::vector<std::string> const& command = this->Test->GetCommand(); std::string exe = command[0]; cmSystemTools::ConvertToUnixSlashes(exe); - fout << indent; - fout << "add_test("; - fout << this->Test->GetName() << " \"" << exe << "\""; + if (quote_test_name) { + fout << indent << "add_test([" << equalSigns << "[" + << this->Test->GetName() << "]" << equalSigns << "] \"" << exe + << "\""; + } else { + fout << indent << "add_test(" << this->Test->GetName() << " \"" << exe + << "\""; + } for (std::string const& arg : cmMakeRange(command).advance(1)) { // Just double-quote all arguments so they are re-parsed @@ -182,8 +280,13 @@ void cmTestGenerator::GenerateOldStyle(std::ostream& fout, Indent indent) fout << ")\n"; // Output properties for the test. - fout << indent << "set_tests_properties(" << this->Test->GetName() - << " PROPERTIES "; + if (quote_test_name) { + fout << indent << "set_tests_properties([" << equalSigns << "[" + << this->Test->GetName() << "]" << equalSigns << "] PROPERTIES "; + } else { + fout << indent << "set_tests_properties(" << this->Test->GetName() + << " PROPERTIES "; + } for (auto const& i : this->Test->GetProperties().GetList()) { fout << " " << i.first << " " << cmOutputConverter::EscapeForCMake(i.second); |