| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 | /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying   file Copyright.txt or https://cmake.org/licensing for details.  */#include "cmTestGenerator.h"#include <memory>#include <ostream>#include <utility>#include <vector>#include "cmGeneratorExpression.h"#include "cmGeneratorTarget.h"#include "cmListFileCache.h"#include "cmLocalGenerator.h"#include "cmOutputConverter.h"#include "cmPropertyMap.h"#include "cmRange.h"#include "cmStateTypes.h"#include "cmStringAlgorithms.h"#include "cmSystemTools.h"#include "cmTest.h"cmTestGenerator::cmTestGenerator(  cmTest* test, std::vector<std::string> const& configurations)  : cmScriptGenerator("CTEST_CONFIGURATION_TYPE", configurations)  , Test(test){  this->ActionsPerConfig = !test->GetOldStyle();  this->TestGenerated = false;  this->LG = nullptr;}cmTestGenerator::~cmTestGenerator() = default;void cmTestGenerator::Compute(cmLocalGenerator* lg){  this->LG = lg;}bool cmTestGenerator::TestsForConfig(const std::string& config){  return this->GeneratesForConfig(config);}cmTest* cmTestGenerator::GetTest() const{  return this->Test;}void cmTestGenerator::GenerateScriptConfigs(std::ostream& os, Indent indent){  // Create the tests.  this->cmScriptGenerator::GenerateScriptConfigs(os, indent);}void cmTestGenerator::GenerateScriptActions(std::ostream& os, Indent indent){  if (this->ActionsPerConfig) {    // This is the per-config generation in a single-configuration    // build generator case.  The superclass will call our per-config    // method.    this->cmScriptGenerator::GenerateScriptActions(os, indent);  } else {    // This is an old-style test, so there is only one config.    // assert(this->Test->GetOldStyle());    this->GenerateOldStyle(os, indent);  }}void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,                                              const std::string& config,                                              Indent indent){  this->TestGenerated = true;  // Set up generator expression evaluation context.  cmGeneratorExpression ge(this->Test->GetBacktrace());  // Start the test command.  os << indent << "add_test(\"" << this->Test->GetName() << "\" ";  // Evaluate command line arguments  std::vector<std::string> argv =    EvaluateCommandLineArguments(this->Test->GetCommand(), ge, config);  // Expand arguments if COMMAND_EXPAND_LISTS is set  if (this->Test->GetCommandExpandLists()) {    argv = cmExpandedLists(argv.begin(), argv.end());    // Expanding lists on an empty command may have left it empty    if (argv.empty()) {      argv.emplace_back();    }  }  // Check whether the command executable is a target whose name is to  // be translated.  std::string exe = argv[0];  cmGeneratorTarget* target = this->LG->FindGeneratorTargetToUse(exe);  if (target && target->GetType() == cmStateEnums::EXECUTABLE) {    // Use the target file on disk.    exe = target->GetFullPath(config);    // Prepend with the emulator when cross compiling if required.    cmProp emulator = target->GetProperty("CROSSCOMPILING_EMULATOR");    if (emulator != nullptr && !emulator->empty()) {      std::vector<std::string> emulatorWithArgs = cmExpandedList(*emulator);      std::string emulatorExe(emulatorWithArgs[0]);      cmSystemTools::ConvertToUnixSlashes(emulatorExe);      os << cmOutputConverter::EscapeForCMake(emulatorExe) << " ";      for (std::string const& arg : cmMakeRange(emulatorWithArgs).advance(1)) {        os << cmOutputConverter::EscapeForCMake(arg) << " ";      }    }  } else {    // Use the command name given.    cmSystemTools::ConvertToUnixSlashes(exe);  }  // Generate the command line with full escapes.  os << cmOutputConverter::EscapeForCMake(exe);  for (auto const& arg : cmMakeRange(argv).advance(1)) {    os << " " << cmOutputConverter::EscapeForCMake(arg);  }  // Finish the test command.  os << ")\n";  // Output properties for the test.  os << indent << "set_tests_properties(\"" << this->Test->GetName()     << "\" PROPERTIES ";  for (auto const& i : this->Test->GetProperties().GetList()) {    os << " " << i.first << " "       << cmOutputConverter::EscapeForCMake(            ge.Parse(i.second)->Evaluate(this->LG, config));  }  this->GenerateInternalProperties(os);  os << ")\n";}void cmTestGenerator::GenerateScriptNoConfig(std::ostream& os, Indent indent){  os << indent << "add_test(\"" << this->Test->GetName()     << "\" NOT_AVAILABLE)\n";}bool cmTestGenerator::NeedsScriptNoConfig() const{  return (this->TestGenerated &&    // test generated for at least one config          this->ActionsPerConfig && // test is config-aware          this->Configurations.empty() &&      // test runs in all configs          !this->ConfigurationTypes->empty()); // config-dependent command}void cmTestGenerator::GenerateOldStyle(std::ostream& fout, Indent indent){  this->TestGenerated = true;  // 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 << "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    // correctly by the test system.    fout << " \"";    for (char c : arg) {      // Escape quotes within arguments.  We should escape      // backslashes too but we cannot because it makes the result      // inconsistent with previous behavior of this command.      if (c == '"') {        fout << '\\';      }      fout << c;    }    fout << '"';  }  fout << ")\n";  // Output properties for the test.  fout << indent << "set_tests_properties(" << this->Test->GetName()       << " PROPERTIES ";  for (auto const& i : this->Test->GetProperties().GetList()) {    fout << " " << i.first << " "         << cmOutputConverter::EscapeForCMake(i.second);  }  this->GenerateInternalProperties(fout);  fout << ")\n";}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 << ";";    }    os << entry.FilePath << ";" << entry.Line << ";" << entry.Name;    bt = bt.Pop();    prependTripleSeparator = true;  }  os << '"';}std::vector<std::string> cmTestGenerator::EvaluateCommandLineArguments(  const std::vector<std::string>& argv, cmGeneratorExpression& ge,  const std::string& config) const{  // Evaluate executable name and arguments  auto evaluatedRange =    cmMakeRange(argv).transform([&](const std::string& arg) {      return ge.Parse(arg)->Evaluate(this->LG, config);    });  return { evaluatedRange.begin(), evaluatedRange.end() };}
 |