diff --git a/Source/cmEnvironment.cxx b/Source/cmEnvironment.cxx index 9cc9bc7868..8832c56542 100644 --- a/Source/cmEnvironment.cxx +++ b/Source/cmEnvironment.cxx @@ -2,12 +2,16 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmEnvironment.h" +#include #include #include #include #include +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" + #if defined(_WIN32) # include "cmsys/String.h" #endif @@ -64,3 +68,138 @@ std::vector cmEnvironment::GetVariables() const } return result; } + +namespace { + +auto const ValidOperators = std::set{ + "set", + "unset", + "string_append", + "string_prepend", + "path_list_append", + "path_list_prepend", + "cmake_list_append", + "cmake_list_prepend", +}; + +struct ListAppend +{ +public: + ListAppend(std::string val, char sep) + : value(std::move(val)) + , separator(sep) + { + } + + void operator()(std::string& output) const + { + if (!output.empty()) { + output += separator; + } + output += value; + } + +private: + std::string value; + char separator; +}; + +struct ListPrepend +{ +public: + ListPrepend(std::string val, char sep) + : value(std::move(val)) + , separator(sep) + { + } + + void operator()(std::string& output) const + { + if (!output.empty()) { + output.insert(output.begin(), separator); + } + output.insert(0, value); + } + +private: + std::string value; + char separator; +}; + +} // namespace + +bool cmEnvironmentModification::Add(std::vector const& envmod) +{ + bool ok = true; + for (auto const& entry : envmod) { + ok &= this->Add(entry); + } + return ok; +} + +bool cmEnvironmentModification::Add(std::string const& envmod) +{ + // Split on `=` + auto const eq_loc = envmod.find_first_of('='); + if (eq_loc == std::string::npos) { + cmSystemTools::Error(cmStrCat( + "Error: Missing `=` after the variable name in: ", envmod, '\n')); + return false; + } + + // Split operation on `:` + auto const op_value_start = eq_loc + 1; + auto const colon_loc = envmod.find_first_of(':', op_value_start); + if (colon_loc == std::string::npos) { + cmSystemTools::Error( + cmStrCat("Error: Missing `:` after the operation in: ", envmod, '\n')); + return false; + } + + auto entry = Entry{}; + entry.Name = envmod.substr(0, eq_loc); + entry.Op = envmod.substr(op_value_start, colon_loc - op_value_start); + entry.Value = envmod.substr(colon_loc + 1); + + if (entry.Op == "reset") { + cm::erase_if(this->Entries, + [&entry](Entry const& e) { return e.Name == entry.Name; }); + return true; + } + + if (!cm::contains(ValidOperators, entry.Op)) { + cmSystemTools::Error(cmStrCat( + "Error: Unrecognized environment manipulation argument: ", entry.Op, + '\n')); + return false; + } + + this->Entries.push_back(std::move(entry)); + return true; +} + +void cmEnvironmentModification::ApplyTo(cmEnvironment& env) +{ + char const path_sep = cmSystemTools::GetSystemPathlistSeparator(); + + for (auto const& e : this->Entries) { + if (e.Op == "set") { + env.PutEnv(e.Name + "=" + e.Value); + } else if (e.Op == "unset") { + env.UnPutEnv(e.Name); + } else if (e.Op == "string_append") { + env.Modify(e.Name, [&e](std::string& output) { output += e.Value; }); + } else if (e.Op == "string_prepend") { + env.Modify(e.Name, + [&e](std::string& output) { output.insert(0, e.Value); }); + } else if (e.Op == "path_list_append") { + env.Modify(e.Name, ListAppend(e.Value, path_sep)); + } else if (e.Op == "path_list_prepend") { + env.Modify(e.Name, ListPrepend(e.Value, path_sep)); + } else if (e.Op == "cmake_list_append") { + env.Modify(e.Name, ListAppend(e.Value, ';')); + } else if (e.Op == "cmake_list_prepend") { + env.Modify(e.Name, ListPrepend(e.Value, ';')); + } + } +} diff --git a/Source/cmEnvironment.h b/Source/cmEnvironment.h index 5ceaa857eb..700b7ac897 100644 --- a/Source/cmEnvironment.h +++ b/Source/cmEnvironment.h @@ -53,3 +53,22 @@ protected: }; std::map, EnvNameLess> Map; }; + +class cmEnvironmentModification +{ +public: + bool Add(std::string const& envmod); + bool Add(std::vector const& envmod); + + void ApplyTo(cmEnvironment& env); + +private: + struct Entry + { + std::string Name; + std::string Value; + std::string Op; + }; + + std::vector Entries; +};