cmTestGenerator.cxx 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmTestGenerator.h"
  4. #include <memory>
  5. #include <ostream>
  6. #include <utility>
  7. #include <vector>
  8. #include "cmGeneratorExpression.h"
  9. #include "cmGeneratorTarget.h"
  10. #include "cmListFileCache.h"
  11. #include "cmLocalGenerator.h"
  12. #include "cmOutputConverter.h"
  13. #include "cmPropertyMap.h"
  14. #include "cmRange.h"
  15. #include "cmStateTypes.h"
  16. #include "cmSystemTools.h"
  17. #include "cmTest.h"
  18. cmTestGenerator::cmTestGenerator(
  19. cmTest* test, std::vector<std::string> const& configurations)
  20. : cmScriptGenerator("CTEST_CONFIGURATION_TYPE", configurations)
  21. , Test(test)
  22. {
  23. this->ActionsPerConfig = !test->GetOldStyle();
  24. this->TestGenerated = false;
  25. this->LG = nullptr;
  26. }
  27. cmTestGenerator::~cmTestGenerator() = default;
  28. void cmTestGenerator::Compute(cmLocalGenerator* lg)
  29. {
  30. this->LG = lg;
  31. }
  32. bool cmTestGenerator::TestsForConfig(const std::string& config)
  33. {
  34. return this->GeneratesForConfig(config);
  35. }
  36. cmTest* cmTestGenerator::GetTest() const
  37. {
  38. return this->Test;
  39. }
  40. void cmTestGenerator::GenerateScriptConfigs(std::ostream& os, Indent indent)
  41. {
  42. // Create the tests.
  43. this->cmScriptGenerator::GenerateScriptConfigs(os, indent);
  44. }
  45. void cmTestGenerator::GenerateScriptActions(std::ostream& os, Indent indent)
  46. {
  47. if (this->ActionsPerConfig) {
  48. // This is the per-config generation in a single-configuration
  49. // build generator case. The superclass will call our per-config
  50. // method.
  51. this->cmScriptGenerator::GenerateScriptActions(os, indent);
  52. } else {
  53. // This is an old-style test, so there is only one config.
  54. // assert(this->Test->GetOldStyle());
  55. this->GenerateOldStyle(os, indent);
  56. }
  57. }
  58. void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,
  59. const std::string& config,
  60. Indent indent)
  61. {
  62. this->TestGenerated = true;
  63. // Set up generator expression evaluation context.
  64. cmGeneratorExpression ge(this->Test->GetBacktrace());
  65. // Start the test command.
  66. os << indent << "add_test(" << this->Test->GetName() << " ";
  67. // Evaluate command line arguments
  68. std::vector<std::string> argv =
  69. EvaluateCommandLineArguments(this->Test->GetCommand(), ge, config);
  70. // Expand arguments if COMMAND_EXPAND_LISTS is set
  71. if (this->Test->GetCommandExpandLists()) {
  72. argv = cmSystemTools::ExpandedLists(argv.begin(), argv.end());
  73. // Expanding lists on an empty command may have left it empty
  74. if (argv.empty()) {
  75. argv.emplace_back();
  76. }
  77. }
  78. // Check whether the command executable is a target whose name is to
  79. // be translated.
  80. std::string exe = argv[0];
  81. cmGeneratorTarget* target = this->LG->FindGeneratorTargetToUse(exe);
  82. if (target && target->GetType() == cmStateEnums::EXECUTABLE) {
  83. // Use the target file on disk.
  84. exe = target->GetFullPath(config);
  85. // Prepend with the emulator when cross compiling if required.
  86. const char* emulator = target->GetProperty("CROSSCOMPILING_EMULATOR");
  87. if (emulator != nullptr && *emulator) {
  88. std::vector<std::string> emulatorWithArgs;
  89. cmSystemTools::ExpandListArgument(emulator, emulatorWithArgs);
  90. std::string emulatorExe(emulatorWithArgs[0]);
  91. cmSystemTools::ConvertToUnixSlashes(emulatorExe);
  92. os << cmOutputConverter::EscapeForCMake(emulatorExe) << " ";
  93. for (std::string const& arg : cmMakeRange(emulatorWithArgs).advance(1)) {
  94. os << cmOutputConverter::EscapeForCMake(arg) << " ";
  95. }
  96. }
  97. } else {
  98. // Use the command name given.
  99. cmSystemTools::ConvertToUnixSlashes(exe);
  100. }
  101. // Generate the command line with full escapes.
  102. os << cmOutputConverter::EscapeForCMake(exe);
  103. for (auto const& arg : cmMakeRange(argv).advance(1)) {
  104. os << " " << cmOutputConverter::EscapeForCMake(arg);
  105. }
  106. // Finish the test command.
  107. os << ")\n";
  108. // Output properties for the test.
  109. os << indent << "set_tests_properties(" << this->Test->GetName()
  110. << " PROPERTIES ";
  111. for (auto const& i : this->Test->GetProperties().GetList()) {
  112. os << " " << i.first << " "
  113. << cmOutputConverter::EscapeForCMake(
  114. ge.Parse(i.second)->Evaluate(this->LG, config));
  115. }
  116. this->GenerateInternalProperties(os);
  117. os << ")" << std::endl;
  118. }
  119. void cmTestGenerator::GenerateScriptNoConfig(std::ostream& os, Indent indent)
  120. {
  121. os << indent << "add_test(" << this->Test->GetName() << " NOT_AVAILABLE)\n";
  122. }
  123. bool cmTestGenerator::NeedsScriptNoConfig() const
  124. {
  125. return (this->TestGenerated && // test generated for at least one config
  126. this->ActionsPerConfig && // test is config-aware
  127. this->Configurations.empty() && // test runs in all configs
  128. !this->ConfigurationTypes->empty()); // config-dependent command
  129. }
  130. void cmTestGenerator::GenerateOldStyle(std::ostream& fout, Indent indent)
  131. {
  132. this->TestGenerated = true;
  133. // Get the test command line to be executed.
  134. std::vector<std::string> const& command = this->Test->GetCommand();
  135. std::string exe = command[0];
  136. cmSystemTools::ConvertToUnixSlashes(exe);
  137. fout << indent;
  138. fout << "add_test(";
  139. fout << this->Test->GetName() << " \"" << exe << "\"";
  140. for (std::string const& arg : cmMakeRange(command).advance(1)) {
  141. // Just double-quote all arguments so they are re-parsed
  142. // correctly by the test system.
  143. fout << " \"";
  144. for (char c : arg) {
  145. // Escape quotes within arguments. We should escape
  146. // backslashes too but we cannot because it makes the result
  147. // inconsistent with previous behavior of this command.
  148. if (c == '"') {
  149. fout << '\\';
  150. }
  151. fout << c;
  152. }
  153. fout << "\"";
  154. }
  155. fout << ")" << std::endl;
  156. // Output properties for the test.
  157. fout << indent << "set_tests_properties(" << this->Test->GetName()
  158. << " PROPERTIES ";
  159. for (auto const& i : this->Test->GetProperties().GetList()) {
  160. fout << " " << i.first << " "
  161. << cmOutputConverter::EscapeForCMake(i.second);
  162. }
  163. this->GenerateInternalProperties(fout);
  164. fout << ")" << std::endl;
  165. }
  166. void cmTestGenerator::GenerateInternalProperties(std::ostream& os)
  167. {
  168. cmListFileBacktrace bt = this->Test->GetBacktrace();
  169. if (bt.Empty()) {
  170. return;
  171. }
  172. os << " "
  173. << "_BACKTRACE_TRIPLES"
  174. << " \"";
  175. bool prependTripleSeparator = false;
  176. while (!bt.Empty()) {
  177. const auto& entry = bt.Top();
  178. if (prependTripleSeparator) {
  179. os << ";";
  180. }
  181. os << entry.FilePath << ";" << entry.Line << ";" << entry.Name;
  182. bt = bt.Pop();
  183. prependTripleSeparator = true;
  184. }
  185. os << "\"";
  186. }
  187. std::vector<std::string> cmTestGenerator::EvaluateCommandLineArguments(
  188. const std::vector<std::string>& argv, cmGeneratorExpression& ge,
  189. const std::string& config) const
  190. {
  191. // Evaluate executable name and arguments
  192. auto evaluatedRange =
  193. cmMakeRange(argv).transform([&](const std::string& arg) {
  194. return ge.Parse(arg)->Evaluate(this->LG, config);
  195. });
  196. return { evaluatedRange.begin(), evaluatedRange.end() };
  197. }