diff --git a/docs/cmake-toml.md b/docs/cmake-toml.md index 5f719bf..1c32d4f 100644 --- a/docs/cmake-toml.md +++ b/docs/cmake-toml.md @@ -88,7 +88,7 @@ sources = ["src/main.cpp"] ptr64.sources = ["src/ptr64_only.cpp"] ``` -Instead of a named condition you can also specify a [CMake expression](https://cmake.org/cmake/help/latest/command/if.html#condition-syntax) directly. +Instead of a named condition you can also specify a [CMake expression](https://cmake.org/cmake/help/latest/command/if.html#condition-syntax) in quotes. Instances of `$` are replaced with the corresponding condition. For example: `"CONDITIONS_BUILD_TESTS AND $"` becomes `CONDITIONS_BUILD_TESTS AND (CMAKE_SYSTEM_NAME MATCHES "Linux")` in the final `CMakeLists.txt` file. ### Predefined conditions diff --git a/src/cmake_generator.cpp b/src/cmake_generator.cpp index e42c4df..f3ed4b0 100644 --- a/src/cmake_generator.cpp +++ b/src/cmake_generator.cpp @@ -547,12 +547,57 @@ struct Generator { // NOTE: this should have been caught by the parser already throw std::runtime_error("Condition '" + condition + "' is not defined"); } - cmd("if", "NOTE: unnamed condition")(RawArg(condition)); + cmd("if", "NOTE: unnamed condition")(RawArg(cmake_condition(condition))); } else { cmd("if", condition)(RawArg(found->second)); } return true; } + + private: + std::string cmake_condition(const std::string &condition) { + // HACK: this replaces '$' with the value of the 'name' condition. We can safely + // reuse the generator expression syntax, because it is not valid in CMake conditions. + // TODO: properly handle quoted arguments (using a simple state machine): + // https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#quoted-argument + std::string result = ""; + bool in_replacement = false; + std::string temp; + for (size_t i = 0; i < condition.length(); i++) { + if (in_replacement) { + if (condition[i] == '>') { + in_replacement = false; + if (temp.empty()) { + throw std::runtime_error("Empty replacement in condition '" + condition + "'"); + } + auto found = project.conditions.find(temp); + if (found == project.conditions.end()) { + throw std::runtime_error("Unknown condition '" + temp + "' in replacement"); + } + auto has_space = found->second.find(' ') != std::string::npos; + if (has_space) { + result += '('; + } + result += found->second; + if (has_space) { + result += ')'; + } + temp.clear(); + } else { + temp += condition[i]; + } + } else if (condition[i] == '$' && i + 1 < condition.length() && condition[i + 1] == '<') { + i++; + in_replacement = true; + } else { + result += condition[i]; + } + } + if (!temp.empty()) { + throw std::runtime_error("Unterminated replacement in condition '" + condition + "'"); + } + return result; + } }; struct ConditionScope { diff --git a/tests/conditions/cmake.toml b/tests/conditions/cmake.toml index 0216029..3913d5f 100644 --- a/tests/conditions/cmake.toml +++ b/tests/conditions/cmake.toml @@ -3,7 +3,7 @@ name = "conditions" cmake-after = "set(CUSTOM ON)" [options] -CONDITIONS_BUILD_TESTS = false +CONDITIONS_BUILD_TESTS = "root" [conditions] custom = "CUSTOM" @@ -19,6 +19,7 @@ linux.cmake-after = "message(STATUS linux-after)" unix.cmake-after = "message(STATUS unix-after)" custom.cmake-after = "message(STATUS custom-after)" build-tests.cmake-after = "message(STATUS build-tests)" +"CONDITIONS_BUILD_TESTS AND $".cmake-after = "message(STATUS linux-tests)" [target.example.properties] AUTOMOC = false