cmScriptGenerator.cxx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  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 =
  49. cmStrCat("\"${", this->RuntimeConfigVariable, "}\" MATCHES \"^(");
  50. if (!config.empty()) {
  51. cmScriptGeneratorEncodeConfig(config, result);
  52. }
  53. result += ")$\"";
  54. return result;
  55. }
  56. std::string cmScriptGenerator::CreateConfigTest(
  57. std::vector<std::string> const& configs)
  58. {
  59. std::string result =
  60. cmStrCat("\"${", this->RuntimeConfigVariable, "}\" MATCHES \"^(");
  61. const char* sep = "";
  62. for (std::string const& config : configs) {
  63. result += sep;
  64. sep = "|";
  65. cmScriptGeneratorEncodeConfig(config, result);
  66. }
  67. result += ")$\"";
  68. return result;
  69. }
  70. void cmScriptGenerator::GenerateScript(std::ostream& os)
  71. {
  72. // Track indentation.
  73. Indent indent;
  74. // Generate the script possibly with per-configuration code.
  75. this->GenerateScriptConfigs(os, indent);
  76. }
  77. void cmScriptGenerator::GenerateScriptConfigs(std::ostream& os, Indent indent)
  78. {
  79. if (this->ActionsPerConfig) {
  80. this->GenerateScriptActionsPerConfig(os, indent);
  81. } else {
  82. this->GenerateScriptActionsOnce(os, indent);
  83. }
  84. }
  85. void cmScriptGenerator::GenerateScriptActions(std::ostream& os, Indent indent)
  86. {
  87. if (this->ActionsPerConfig) {
  88. // This is reached for single-configuration build generators in a
  89. // per-config script generator.
  90. this->GenerateScriptForConfig(os, this->ConfigurationName, indent);
  91. }
  92. }
  93. void cmScriptGenerator::GenerateScriptForConfig(std::ostream& /*unused*/,
  94. const std::string& /*unused*/,
  95. Indent /*unused*/)
  96. {
  97. // No actions for this generator.
  98. }
  99. bool cmScriptGenerator::GeneratesForConfig(const std::string& config)
  100. {
  101. // If this is not a configuration-specific rule then we install.
  102. if (this->Configurations.empty()) {
  103. return true;
  104. }
  105. // This is a configuration-specific rule. Check if the config
  106. // matches this rule.
  107. std::string config_upper = cmSystemTools::UpperCase(config);
  108. return std::any_of(this->Configurations.begin(), this->Configurations.end(),
  109. [&config_upper](std::string const& cfg) -> bool {
  110. return cmSystemTools::UpperCase(cfg) == config_upper;
  111. });
  112. }
  113. void cmScriptGenerator::GenerateScriptActionsOnce(std::ostream& os,
  114. Indent indent)
  115. {
  116. if (this->Configurations.empty()) {
  117. // This rule is for all configurations.
  118. this->GenerateScriptActions(os, indent);
  119. } else {
  120. // Generate a per-configuration block.
  121. std::string config_test = this->CreateConfigTest(this->Configurations);
  122. os << indent << "if(" << config_test << ")\n";
  123. this->GenerateScriptActions(os, indent.Next());
  124. os << indent << "endif(" << config_test << ")\n";
  125. }
  126. }
  127. void cmScriptGenerator::GenerateScriptActionsPerConfig(std::ostream& os,
  128. Indent indent)
  129. {
  130. if (this->ConfigurationTypes->empty()) {
  131. // In a single-configuration generator there is only one action
  132. // and it applies if the runtime-requested configuration is among
  133. // the rule's allowed configurations. The configuration built in
  134. // the tree does not matter for this decision but will be used to
  135. // generate proper target file names into the code.
  136. this->GenerateScriptActionsOnce(os, indent);
  137. } else {
  138. // In a multi-configuration generator we produce a separate rule
  139. // in a block for each configuration that is built. We restrict
  140. // the list of configurations to those to which this rule applies.
  141. bool first = true;
  142. for (std::string const& cfgType : *this->ConfigurationTypes) {
  143. if (this->GeneratesForConfig(cfgType)) {
  144. // Generate a per-configuration block.
  145. std::string config_test = this->CreateConfigTest(cfgType);
  146. os << indent << (first ? "if(" : "elseif(") << config_test << ")\n";
  147. this->GenerateScriptForConfig(os, cfgType, indent.Next());
  148. first = false;
  149. }
  150. }
  151. if (!first) {
  152. if (this->NeedsScriptNoConfig()) {
  153. os << indent << "else()\n";
  154. this->GenerateScriptNoConfig(os, indent.Next());
  155. }
  156. os << indent << "endif()\n";
  157. }
  158. }
  159. }