cmScriptGenerator.cxx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  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 "cmScriptGenerator.h"
  4. #include <algorithm>
  5. #include <utility>
  6. #include "cmStringAlgorithms.h"
  7. #include "cmSystemTools.h"
  8. cmScriptGenerator::cmScriptGenerator(std::string config_var,
  9. std::vector<std::string> configurations)
  10. : RuntimeConfigVariable(std::move(config_var))
  11. , Configurations(std::move(configurations))
  12. , ConfigurationTypes(nullptr)
  13. , ActionsPerConfig(false)
  14. {
  15. }
  16. cmScriptGenerator::~cmScriptGenerator() = default;
  17. void cmScriptGenerator::Generate(
  18. std::ostream& os, const std::string& config,
  19. std::vector<std::string> const& configurationTypes)
  20. {
  21. this->ConfigurationName = config;
  22. this->ConfigurationTypes = &configurationTypes;
  23. this->GenerateScript(os);
  24. this->ConfigurationName.clear();
  25. this->ConfigurationTypes = nullptr;
  26. }
  27. static void cmScriptGeneratorEncodeConfig(const std::string& config,
  28. std::string& result)
  29. {
  30. for (const char* c = config.c_str(); *c; ++c) {
  31. if (*c >= 'a' && *c <= 'z') {
  32. result += "[";
  33. result += static_cast<char>(*c + 'A' - 'a');
  34. result += *c;
  35. result += "]";
  36. } else if (*c >= 'A' && *c <= 'Z') {
  37. result += "[";
  38. result += *c;
  39. result += static_cast<char>(*c + 'a' - 'A');
  40. result += "]";
  41. } else {
  42. result += *c;
  43. }
  44. }
  45. }
  46. std::string cmScriptGenerator::CreateConfigTest(const std::string& config)
  47. {
  48. std::string result = cmStrCat(this->RuntimeConfigVariable, " MATCHES \"^(");
  49. if (!config.empty()) {
  50. cmScriptGeneratorEncodeConfig(config, result);
  51. }
  52. result += ")$\"";
  53. return result;
  54. }
  55. std::string cmScriptGenerator::CreateConfigTest(
  56. std::vector<std::string> const& configs)
  57. {
  58. std::string result = cmStrCat(this->RuntimeConfigVariable, " MATCHES \"^(");
  59. const char* sep = "";
  60. for (std::string const& config : configs) {
  61. result += sep;
  62. sep = "|";
  63. cmScriptGeneratorEncodeConfig(config, result);
  64. }
  65. result += ")$\"";
  66. return result;
  67. }
  68. void cmScriptGenerator::GenerateScript(std::ostream& os)
  69. {
  70. // Track indentation.
  71. Indent indent;
  72. // Generate the script possibly with per-configuration code.
  73. this->GenerateScriptConfigs(os, indent);
  74. }
  75. void cmScriptGenerator::GenerateScriptConfigs(std::ostream& os, Indent indent)
  76. {
  77. if (this->ActionsPerConfig) {
  78. this->GenerateScriptActionsPerConfig(os, indent);
  79. } else {
  80. this->GenerateScriptActionsOnce(os, indent);
  81. }
  82. }
  83. void cmScriptGenerator::GenerateScriptActions(std::ostream& os, Indent indent)
  84. {
  85. if (this->ActionsPerConfig) {
  86. // This is reached for single-configuration build generators in a
  87. // per-config script generator.
  88. this->GenerateScriptForConfig(os, this->ConfigurationName, indent);
  89. }
  90. }
  91. void cmScriptGenerator::GenerateScriptForConfig(std::ostream& /*unused*/,
  92. const std::string& /*unused*/,
  93. Indent /*unused*/)
  94. {
  95. // No actions for this generator.
  96. }
  97. bool cmScriptGenerator::GeneratesForConfig(const std::string& config)
  98. {
  99. // If this is not a configuration-specific rule then we install.
  100. if (this->Configurations.empty()) {
  101. return true;
  102. }
  103. // This is a configuration-specific rule. Check if the config
  104. // matches this rule.
  105. std::string config_upper = cmSystemTools::UpperCase(config);
  106. return std::any_of(this->Configurations.begin(), this->Configurations.end(),
  107. [&config_upper](std::string const& cfg) -> bool {
  108. return cmSystemTools::UpperCase(cfg) == config_upper;
  109. });
  110. }
  111. void cmScriptGenerator::GenerateScriptActionsOnce(std::ostream& os,
  112. Indent indent)
  113. {
  114. if (this->Configurations.empty()) {
  115. // This rule is for all configurations.
  116. this->GenerateScriptActions(os, indent);
  117. } else {
  118. // Generate a per-configuration block.
  119. std::string config_test = this->CreateConfigTest(this->Configurations);
  120. os << indent << "if(" << config_test << ")\n";
  121. this->GenerateScriptActions(os, indent.Next());
  122. os << indent << "endif(" << config_test << ")\n";
  123. }
  124. }
  125. void cmScriptGenerator::GenerateScriptActionsPerConfig(std::ostream& os,
  126. Indent indent)
  127. {
  128. if (this->ConfigurationTypes->empty()) {
  129. // In a single-configuration generator there is only one action
  130. // and it applies if the runtime-requested configuration is among
  131. // the rule's allowed configurations. The configuration built in
  132. // the tree does not matter for this decision but will be used to
  133. // generate proper target file names into the code.
  134. this->GenerateScriptActionsOnce(os, indent);
  135. } else {
  136. // In a multi-configuration generator we produce a separate rule
  137. // in a block for each configuration that is built. We restrict
  138. // the list of configurations to those to which this rule applies.
  139. bool first = true;
  140. for (std::string const& cfgType : *this->ConfigurationTypes) {
  141. if (this->GeneratesForConfig(cfgType)) {
  142. // Generate a per-configuration block.
  143. std::string config_test = this->CreateConfigTest(cfgType);
  144. os << indent << (first ? "if(" : "elseif(") << config_test << ")\n";
  145. this->GenerateScriptForConfig(os, cfgType, indent.Next());
  146. first = false;
  147. }
  148. }
  149. if (!first) {
  150. if (this->NeedsScriptNoConfig()) {
  151. os << indent << "else()\n";
  152. this->GenerateScriptNoConfig(os, indent.Next());
  153. }
  154. os << indent << "endif()\n";
  155. }
  156. }
  157. }