summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
Diffstat (limited to 'Source')
-rw-r--r--Source/cmPolicies.h5
-rw-r--r--Source/cmTestGenerator.cxx121
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);