| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487 | 
							- /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
 
-    file Copyright.txt or https://cmake.org/licensing for details.  */
 
- #include "cmForEachCommand.h"
 
- #include <algorithm>
 
- #include <cassert>
 
- #include <cstddef> // IWYU pragma: keep
 
- // NOTE The declaration of `std::abs` has moved to `cmath` since C++17
 
- // See https://en.cppreference.com/w/cpp/numeric/math/abs
 
- // ALERT But IWYU used to lint `#include`s do not "understand"
 
- // conditional compilation (i.e. `#if __cplusplus >= 201703L`)
 
- #include <cstdlib>
 
- #include <iterator>
 
- #include <map>
 
- #include <sstream>
 
- #include <stdexcept>
 
- #include <utility>
 
- #include <cm/memory>
 
- #include <cm/optional>
 
- #include <cm/string_view>
 
- #include <cmext/string_view>
 
- #include "cmExecutionStatus.h"
 
- #include "cmFunctionBlocker.h"
 
- #include "cmListFileCache.h"
 
- #include "cmMakefile.h"
 
- #include "cmMessageType.h"
 
- #include "cmPolicies.h"
 
- #include "cmRange.h"
 
- #include "cmStringAlgorithms.h"
 
- #include "cmSystemTools.h"
 
- #include "cmValue.h"
 
- namespace {
 
- class cmForEachFunctionBlocker : public cmFunctionBlocker
 
- {
 
- public:
 
-   explicit cmForEachFunctionBlocker(cmMakefile* mf);
 
-   ~cmForEachFunctionBlocker() override;
 
-   cm::string_view StartCommandName() const override { return "foreach"_s; }
 
-   cm::string_view EndCommandName() const override { return "endforeach"_s; }
 
-   bool ArgumentsMatch(cmListFileFunction const& lff,
 
-                       cmMakefile& mf) const override;
 
-   bool Replay(std::vector<cmListFileFunction> functions,
 
-               cmExecutionStatus& inStatus) override;
 
-   void SetIterationVarsCount(const std::size_t varsCount)
 
-   {
 
-     this->IterationVarsCount = varsCount;
 
-   }
 
-   void SetZipLists() { this->ZipLists = true; }
 
-   std::vector<std::string> Args;
 
- private:
 
-   struct InvokeResult
 
-   {
 
-     bool Restore;
 
-     bool Break;
 
-   };
 
-   bool ReplayItems(std::vector<cmListFileFunction> const& functions,
 
-                    cmExecutionStatus& inStatus);
 
-   bool ReplayZipLists(std::vector<cmListFileFunction> const& functions,
 
-                       cmExecutionStatus& inStatus);
 
-   InvokeResult invoke(std::vector<cmListFileFunction> const& functions,
 
-                       cmExecutionStatus& inStatus, cmMakefile& mf);
 
-   cmMakefile* Makefile;
 
-   std::size_t IterationVarsCount = 0u;
 
-   bool ZipLists = false;
 
- };
 
- cmForEachFunctionBlocker::cmForEachFunctionBlocker(cmMakefile* mf)
 
-   : Makefile(mf)
 
- {
 
-   this->Makefile->PushLoopBlock();
 
- }
 
- cmForEachFunctionBlocker::~cmForEachFunctionBlocker()
 
- {
 
-   this->Makefile->PopLoopBlock();
 
- }
 
- bool cmForEachFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
 
-                                               cmMakefile& mf) const
 
- {
 
-   std::vector<std::string> expandedArguments;
 
-   mf.ExpandArguments(lff.Arguments(), expandedArguments);
 
-   return expandedArguments.empty() ||
 
-     expandedArguments.front() == this->Args.front();
 
- }
 
- bool cmForEachFunctionBlocker::Replay(
 
-   std::vector<cmListFileFunction> functions, cmExecutionStatus& inStatus)
 
- {
 
-   return this->ZipLists ? this->ReplayZipLists(functions, inStatus)
 
-                         : this->ReplayItems(functions, inStatus);
 
- }
 
- bool cmForEachFunctionBlocker::ReplayItems(
 
-   std::vector<cmListFileFunction> const& functions,
 
-   cmExecutionStatus& inStatus)
 
- {
 
-   assert("Unexpected number of iteration variables" &&
 
-          this->IterationVarsCount == 1);
 
-   auto& mf = inStatus.GetMakefile();
 
-   // At end of for each execute recorded commands
 
-   // store the old value
 
-   cm::optional<std::string> oldDef;
 
-   if (mf.GetPolicyStatus(cmPolicies::CMP0124) != cmPolicies::NEW) {
 
-     oldDef = mf.GetSafeDefinition(this->Args.front());
 
-   } else if (mf.IsNormalDefinitionSet(this->Args.front())) {
 
-     oldDef = *mf.GetDefinition(this->Args.front());
 
-   }
 
-   auto restore = false;
 
-   for (std::string const& arg : cmMakeRange(this->Args).advance(1)) {
 
-     // Set the variable to the loop value
 
-     mf.AddDefinition(this->Args.front(), arg);
 
-     // Invoke all the functions that were collected in the block.
 
-     auto r = this->invoke(functions, inStatus, mf);
 
-     restore = r.Restore;
 
-     if (r.Break) {
 
-       break;
 
-     }
 
-   }
 
-   if (restore) {
 
-     if (oldDef) {
 
-       // restore the variable to its prior value
 
-       mf.AddDefinition(this->Args.front(), *oldDef);
 
-     } else {
 
-       mf.RemoveDefinition(this->Args.front());
 
-     }
 
-   }
 
-   return true;
 
- }
 
- bool cmForEachFunctionBlocker::ReplayZipLists(
 
-   std::vector<cmListFileFunction> const& functions,
 
-   cmExecutionStatus& inStatus)
 
- {
 
-   assert("Unexpected number of iteration variables" &&
 
-          this->IterationVarsCount >= 1);
 
-   auto& mf = inStatus.GetMakefile();
 
-   // Expand the list of list-variables into a list of lists of strings
 
-   std::vector<std::vector<std::string>> values;
 
-   values.reserve(this->Args.size() - this->IterationVarsCount);
 
-   // Also track the longest list size
 
-   std::size_t maxItems = 0u;
 
-   for (auto const& var :
 
-        cmMakeRange(this->Args).advance(this->IterationVarsCount)) {
 
-     std::vector<std::string> items;
 
-     auto const& value = mf.GetSafeDefinition(var);
 
-     if (!value.empty()) {
 
-       cmExpandList(value, items, true);
 
-     }
 
-     maxItems = std::max(maxItems, items.size());
 
-     values.emplace_back(std::move(items));
 
-   }
 
-   // Form the list of iteration variables
 
-   std::vector<std::string> iterationVars;
 
-   if (this->IterationVarsCount > 1) {
 
-     // If multiple iteration variables has given,
 
-     // just copy them to the `iterationVars` list.
 
-     iterationVars.reserve(values.size());
 
-     std::copy(this->Args.begin(),
 
-               this->Args.begin() + this->IterationVarsCount,
 
-               std::back_inserter(iterationVars));
 
-   } else {
 
-     // In case of the only iteration variable,
 
-     // generate names as `var_name_N`,
 
-     // where `N` is the count of lists to zip
 
-     iterationVars.resize(values.size());
 
-     const auto iter_var_prefix = this->Args.front() + "_";
 
-     auto i = 0u;
 
-     std::generate(
 
-       iterationVars.begin(), iterationVars.end(),
 
-       [&]() -> std::string { return iter_var_prefix + std::to_string(i++); });
 
-   }
 
-   assert("Sanity check" && iterationVars.size() == values.size());
 
-   // Store old values for iteration variables
 
-   std::map<std::string, cm::optional<std::string>> oldDefs;
 
-   for (auto i = 0u; i < values.size(); ++i) {
 
-     const auto& varName = iterationVars[i];
 
-     if (mf.GetPolicyStatus(cmPolicies::CMP0124) != cmPolicies::NEW) {
 
-       oldDefs.emplace(varName, mf.GetSafeDefinition(varName));
 
-     } else if (mf.IsNormalDefinitionSet(varName)) {
 
-       oldDefs.emplace(varName, *mf.GetDefinition(varName));
 
-     } else {
 
-       oldDefs.emplace(varName, cm::nullopt);
 
-     }
 
-   }
 
-   // Form a vector of current positions in all lists (Ok, vectors) of values
 
-   std::vector<decltype(values)::value_type::iterator> positions;
 
-   positions.reserve(values.size());
 
-   std::transform(
 
-     values.begin(), values.end(), std::back_inserter(positions),
 
-     // Set the initial position to the beginning of every list
 
-     [](decltype(values)::value_type& list) { return list.begin(); });
 
-   assert("Sanity check" && positions.size() == values.size());
 
-   auto restore = false;
 
-   // Iterate over all the lists simulateneously
 
-   for (auto i = 0u; i < maxItems; ++i) {
 
-     // Declare iteration variables
 
-     for (auto j = 0u; j < values.size(); ++j) {
 
-       // Define (or not) the iteration variable if the current position
 
-       // still not at the end...
 
-       if (positions[j] != values[j].end()) {
 
-         mf.AddDefinition(iterationVars[j], *positions[j]);
 
-         ++positions[j];
 
-       } else {
 
-         mf.RemoveDefinition(iterationVars[j]);
 
-       }
 
-     }
 
-     // Invoke all the functions that were collected in the block.
 
-     auto r = this->invoke(functions, inStatus, mf);
 
-     restore = r.Restore;
 
-     if (r.Break) {
 
-       break;
 
-     }
 
-   }
 
-   // Restore the variables to its prior value
 
-   if (restore) {
 
-     for (auto const& p : oldDefs) {
 
-       if (p.second) {
 
-         mf.AddDefinition(p.first, *p.second);
 
-       } else {
 
-         mf.RemoveDefinition(p.first);
 
-       }
 
-     }
 
-   }
 
-   return true;
 
- }
 
- auto cmForEachFunctionBlocker::invoke(
 
-   std::vector<cmListFileFunction> const& functions,
 
-   cmExecutionStatus& inStatus, cmMakefile& mf) -> InvokeResult
 
- {
 
-   InvokeResult result = { true, false };
 
-   // Invoke all the functions that were collected in the block.
 
-   for (cmListFileFunction const& func : functions) {
 
-     cmExecutionStatus status(mf);
 
-     mf.ExecuteCommand(func, status);
 
-     if (status.GetReturnInvoked()) {
 
-       inStatus.SetReturnInvoked();
 
-       result.Break = true;
 
-       break;
 
-     }
 
-     if (status.GetBreakInvoked()) {
 
-       result.Break = true;
 
-       break;
 
-     }
 
-     if (status.GetContinueInvoked()) {
 
-       break;
 
-     }
 
-     if (cmSystemTools::GetFatalErrorOccured()) {
 
-       result.Restore = false;
 
-       result.Break = true;
 
-       break;
 
-     }
 
-   }
 
-   return result;
 
- }
 
- bool HandleInMode(std::vector<std::string> const& args,
 
-                   std::vector<std::string>::const_iterator kwInIter,
 
-                   cmMakefile& makefile)
 
- {
 
-   assert("A valid iterator expected" && kwInIter != args.end());
 
-   auto fb = cm::make_unique<cmForEachFunctionBlocker>(&makefile);
 
-   // Copy iteration variable names first
 
-   std::copy(args.begin(), kwInIter, std::back_inserter(fb->Args));
 
-   // Remember the count of given iteration variable names
 
-   const auto varsCount = fb->Args.size();
 
-   fb->SetIterationVarsCount(varsCount);
 
-   enum Doing
 
-   {
 
-     DoingNone,
 
-     DoingLists,
 
-     DoingItems,
 
-     DoingZipLists
 
-   };
 
-   Doing doing = DoingNone;
 
-   // Iterate over arguments past the "IN" keyword
 
-   for (std::string const& arg : cmMakeRange(++kwInIter, args.end())) {
 
-     if (arg == "LISTS") {
 
-       if (doing == DoingZipLists) {
 
-         makefile.IssueMessage(MessageType::FATAL_ERROR,
 
-                               "ZIP_LISTS can not be used with LISTS or ITEMS");
 
-         return true;
 
-       }
 
-       if (varsCount != 1u) {
 
-         makefile.IssueMessage(
 
-           MessageType::FATAL_ERROR,
 
-           "ITEMS or LISTS require exactly one iteration variable");
 
-         return true;
 
-       }
 
-       doing = DoingLists;
 
-     } else if (arg == "ITEMS") {
 
-       if (doing == DoingZipLists) {
 
-         makefile.IssueMessage(MessageType::FATAL_ERROR,
 
-                               "ZIP_LISTS can not be used with LISTS or ITEMS");
 
-         return true;
 
-       }
 
-       if (varsCount != 1u) {
 
-         makefile.IssueMessage(
 
-           MessageType::FATAL_ERROR,
 
-           "ITEMS or LISTS require exactly one iteration variable");
 
-         return true;
 
-       }
 
-       doing = DoingItems;
 
-     } else if (arg == "ZIP_LISTS") {
 
-       if (doing != DoingNone) {
 
-         makefile.IssueMessage(MessageType::FATAL_ERROR,
 
-                               "ZIP_LISTS can not be used with LISTS or ITEMS");
 
-         return true;
 
-       }
 
-       doing = DoingZipLists;
 
-       fb->SetZipLists();
 
-     } else if (doing == DoingLists) {
 
-       auto const& value = makefile.GetSafeDefinition(arg);
 
-       if (!value.empty()) {
 
-         cmExpandList(value, fb->Args, true);
 
-       }
 
-     } else if (doing == DoingItems || doing == DoingZipLists) {
 
-       fb->Args.push_back(arg);
 
-     } else {
 
-       makefile.IssueMessage(MessageType::FATAL_ERROR,
 
-                             cmStrCat("Unknown argument:\n", "  ", arg, "\n"));
 
-       return true;
 
-     }
 
-   }
 
-   // If `ZIP_LISTS` given and variables count more than 1,
 
-   // make sure the given lists count matches variables...
 
-   if (doing == DoingZipLists && varsCount > 1u &&
 
-       (2u * varsCount) != fb->Args.size()) {
 
-     makefile.IssueMessage(
 
-       MessageType::FATAL_ERROR,
 
-       cmStrCat("Expected ", std::to_string(varsCount),
 
-                " list variables, but given ",
 
-                std::to_string(fb->Args.size() - varsCount)));
 
-     return true;
 
-   }
 
-   makefile.AddFunctionBlocker(std::move(fb));
 
-   return true;
 
- }
 
- bool TryParseInteger(cmExecutionStatus& status, const std::string& str, int& i)
 
- {
 
-   try {
 
-     i = std::stoi(str);
 
-   } catch (std::invalid_argument&) {
 
-     std::ostringstream e;
 
-     e << "Invalid integer: '" << str << "'";
 
-     status.SetError(e.str());
 
-     cmSystemTools::SetFatalErrorOccured();
 
-     return false;
 
-   } catch (std::out_of_range&) {
 
-     std::ostringstream e;
 
-     e << "Integer out of range: '" << str << "'";
 
-     status.SetError(e.str());
 
-     cmSystemTools::SetFatalErrorOccured();
 
-     return false;
 
-   }
 
-   return true;
 
- }
 
- } // anonymous namespace
 
- bool cmForEachCommand(std::vector<std::string> const& args,
 
-                       cmExecutionStatus& status)
 
- {
 
-   if (args.empty()) {
 
-     status.SetError("called with incorrect number of arguments");
 
-     return false;
 
-   }
 
-   auto kwInIter = std::find(args.begin(), args.end(), "IN");
 
-   if (kwInIter != args.end()) {
 
-     return HandleInMode(args, kwInIter, status.GetMakefile());
 
-   }
 
-   // create a function blocker
 
-   auto fb = cm::make_unique<cmForEachFunctionBlocker>(&status.GetMakefile());
 
-   if (args.size() > 1) {
 
-     if (args[1] == "RANGE") {
 
-       int start = 0;
 
-       int stop = 0;
 
-       int step = 0;
 
-       if (args.size() == 3) {
 
-         if (!TryParseInteger(status, args[2], stop)) {
 
-           return false;
 
-         }
 
-       }
 
-       if (args.size() == 4) {
 
-         if (!TryParseInteger(status, args[2], start)) {
 
-           return false;
 
-         }
 
-         if (!TryParseInteger(status, args[3], stop)) {
 
-           return false;
 
-         }
 
-       }
 
-       if (args.size() == 5) {
 
-         if (!TryParseInteger(status, args[2], start)) {
 
-           return false;
 
-         }
 
-         if (!TryParseInteger(status, args[3], stop)) {
 
-           return false;
 
-         }
 
-         if (!TryParseInteger(status, args[4], step)) {
 
-           return false;
 
-         }
 
-       }
 
-       if (step == 0) {
 
-         if (start > stop) {
 
-           step = -1;
 
-         } else {
 
-           step = 1;
 
-         }
 
-       }
 
-       if ((start > stop && step > 0) || (start < stop && step < 0) ||
 
-           step == 0) {
 
-         status.SetError(
 
-           cmStrCat("called with incorrect range specification: start ", start,
 
-                    ", stop ", stop, ", step ", step));
 
-         cmSystemTools::SetFatalErrorOccured();
 
-         return false;
 
-       }
 
-       // Calculate expected iterations count and reserve enough space
 
-       // in the `fb->Args` vector. The first item is the iteration variable
 
-       // name...
 
-       const std::size_t iter_cnt = 2u +
 
-         static_cast<int>(start < stop) * (stop - start) / std::abs(step) +
 
-         static_cast<int>(start > stop) * (start - stop) / std::abs(step);
 
-       fb->Args.resize(iter_cnt);
 
-       fb->Args.front() = args.front();
 
-       auto cc = start;
 
-       auto generator = [&cc, step]() -> std::string {
 
-         auto result = std::to_string(cc);
 
-         cc += step;
 
-         return result;
 
-       };
 
-       // Fill the `range` vector w/ generated string values
 
-       // (starting from 2nd position)
 
-       std::generate(++fb->Args.begin(), fb->Args.end(), generator);
 
-     } else {
 
-       fb->Args = args;
 
-     }
 
-   } else {
 
-     fb->Args = args;
 
-   }
 
-   fb->SetIterationVarsCount(1u);
 
-   status.GetMakefile().AddFunctionBlocker(std::move(fb));
 
-   return true;
 
- }
 
 
  |