| /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying | 
 |    file LICENSE.rst or https://cmake.org/licensing for details.  */ | 
 | #include "cmFileSet.h" | 
 |  | 
 | #include <sstream> | 
 | #include <string> | 
 | #include <unordered_map> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include <cm/optional> | 
 | #include <cmext/algorithm> | 
 | #include <cmext/string_view> | 
 |  | 
 | #include "cmsys/RegularExpression.hxx" | 
 |  | 
 | #include "cmGenExContext.h" | 
 | #include "cmGeneratorExpression.h" | 
 | #include "cmList.h" | 
 | #include "cmListFileCache.h" | 
 | #include "cmLocalGenerator.h" | 
 | #include "cmMakefile.h" | 
 | #include "cmMessageType.h" | 
 | #include "cmStringAlgorithms.h" | 
 | #include "cmSystemTools.h" | 
 | #include "cmake.h" | 
 |  | 
 | cm::static_string_view cmFileSetVisibilityToName(cmFileSetVisibility vis) | 
 | { | 
 |   switch (vis) { | 
 |     case cmFileSetVisibility::Interface: | 
 |       return "INTERFACE"_s; | 
 |     case cmFileSetVisibility::Public: | 
 |       return "PUBLIC"_s; | 
 |     case cmFileSetVisibility::Private: | 
 |       return "PRIVATE"_s; | 
 |   } | 
 |   return ""_s; | 
 | } | 
 |  | 
 | cmFileSetVisibility cmFileSetVisibilityFromName(cm::string_view name, | 
 |                                                 cmMakefile* mf) | 
 | { | 
 |   if (name == "INTERFACE"_s) { | 
 |     return cmFileSetVisibility::Interface; | 
 |   } | 
 |   if (name == "PUBLIC"_s) { | 
 |     return cmFileSetVisibility::Public; | 
 |   } | 
 |   if (name == "PRIVATE"_s) { | 
 |     return cmFileSetVisibility::Private; | 
 |   } | 
 |   auto msg = cmStrCat("File set visibility \"", name, "\" is not valid."); | 
 |   if (mf) { | 
 |     mf->IssueMessage(MessageType::FATAL_ERROR, msg); | 
 |   } else { | 
 |     cmSystemTools::Error(msg); | 
 |   } | 
 |   return cmFileSetVisibility::Private; | 
 | } | 
 |  | 
 | bool cmFileSetVisibilityIsForSelf(cmFileSetVisibility vis) | 
 | { | 
 |   switch (vis) { | 
 |     case cmFileSetVisibility::Interface: | 
 |       return false; | 
 |     case cmFileSetVisibility::Public: | 
 |     case cmFileSetVisibility::Private: | 
 |       return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | bool cmFileSetVisibilityIsForInterface(cmFileSetVisibility vis) | 
 | { | 
 |   switch (vis) { | 
 |     case cmFileSetVisibility::Interface: | 
 |     case cmFileSetVisibility::Public: | 
 |       return true; | 
 |     case cmFileSetVisibility::Private: | 
 |       return false; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | bool cmFileSetTypeCanBeIncluded(std::string const& type) | 
 | { | 
 |   return type == "HEADERS"_s; | 
 | } | 
 |  | 
 | cmFileSet::cmFileSet(cmake& cmakeInstance, std::string name, std::string type, | 
 |                      cmFileSetVisibility visibility) | 
 |   : CMakeInstance(cmakeInstance) | 
 |   , Name(std::move(name)) | 
 |   , Type(std::move(type)) | 
 |   , Visibility(visibility) | 
 | { | 
 | } | 
 |  | 
 | void cmFileSet::CopyEntries(cmFileSet const* fs) | 
 | { | 
 |   cm::append(this->DirectoryEntries, fs->DirectoryEntries); | 
 |   cm::append(this->FileEntries, fs->FileEntries); | 
 | } | 
 |  | 
 | void cmFileSet::ClearDirectoryEntries() | 
 | { | 
 |   this->DirectoryEntries.clear(); | 
 | } | 
 |  | 
 | void cmFileSet::AddDirectoryEntry(BT<std::string> directories) | 
 | { | 
 |   this->DirectoryEntries.push_back(std::move(directories)); | 
 | } | 
 |  | 
 | void cmFileSet::ClearFileEntries() | 
 | { | 
 |   this->FileEntries.clear(); | 
 | } | 
 |  | 
 | void cmFileSet::AddFileEntry(BT<std::string> files) | 
 | { | 
 |   this->FileEntries.push_back(std::move(files)); | 
 | } | 
 |  | 
 | std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> | 
 | cmFileSet::CompileFileEntries() const | 
 | { | 
 |   std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> result; | 
 |  | 
 |   for (auto const& entry : this->FileEntries) { | 
 |     for (auto const& ex : cmList{ entry.Value }) { | 
 |       cmGeneratorExpression ge(this->CMakeInstance, entry.Backtrace); | 
 |       auto cge = ge.Parse(ex); | 
 |       result.push_back(std::move(cge)); | 
 |     } | 
 |   } | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> | 
 | cmFileSet::CompileDirectoryEntries() const | 
 | { | 
 |   std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> result; | 
 |  | 
 |   for (auto const& entry : this->DirectoryEntries) { | 
 |     for (auto const& ex : cmList{ entry.Value }) { | 
 |       cmGeneratorExpression ge(this->CMakeInstance, entry.Backtrace); | 
 |       auto cge = ge.Parse(ex); | 
 |       result.push_back(std::move(cge)); | 
 |     } | 
 |   } | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | std::vector<std::string> cmFileSet::EvaluateDirectoryEntries( | 
 |   std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> const& cges, | 
 |   cm::GenEx::Context const& context, cmGeneratorTarget const* target, | 
 |   cmGeneratorExpressionDAGChecker* dagChecker) const | 
 | { | 
 |   struct DirCacheEntry | 
 |   { | 
 |     std::string collapsedDir; | 
 |     cm::optional<cmSystemTools::FileId> fileId; | 
 |   }; | 
 |  | 
 |   std::unordered_map<std::string, DirCacheEntry> dirCache; | 
 |   std::vector<std::string> result; | 
 |   for (auto const& cge : cges) { | 
 |     auto entry = cge->Evaluate(context, dagChecker, target); | 
 |     cmList dirs{ entry }; | 
 |     for (std::string dir : dirs) { | 
 |       if (!cmSystemTools::FileIsFullPath(dir)) { | 
 |         dir = cmStrCat(context.LG->GetCurrentSourceDirectory(), '/', dir); | 
 |       } | 
 |  | 
 |       auto dirCacheResult = dirCache.emplace(dir, DirCacheEntry()); | 
 |       auto& dirCacheEntry = dirCacheResult.first->second; | 
 |       auto const isNewCacheEntry = dirCacheResult.second; | 
 |  | 
 |       if (isNewCacheEntry) { | 
 |         cmSystemTools::FileId fileId; | 
 |         auto isFileIdValid = cmSystemTools::GetFileId(dir, fileId); | 
 |         dirCacheEntry.collapsedDir = cmSystemTools::CollapseFullPath(dir); | 
 |         dirCacheEntry.fileId = | 
 |           isFileIdValid ? cm::optional<decltype(fileId)>(fileId) : cm::nullopt; | 
 |       } | 
 |  | 
 |       for (auto const& priorDir : result) { | 
 |         auto priorDirCacheEntry = dirCache.at(priorDir); | 
 |         bool sameFile = dirCacheEntry.fileId.has_value() && | 
 |           priorDirCacheEntry.fileId.has_value() && | 
 |           (*dirCacheEntry.fileId == *priorDirCacheEntry.fileId); | 
 |         if (!sameFile && | 
 |             (cmSystemTools::IsSubDirectory(dirCacheEntry.collapsedDir, | 
 |                                            priorDirCacheEntry.collapsedDir) || | 
 |              cmSystemTools::IsSubDirectory(priorDirCacheEntry.collapsedDir, | 
 |                                            dirCacheEntry.collapsedDir))) { | 
 |           context.LG->GetCMakeInstance()->IssueMessage( | 
 |             MessageType::FATAL_ERROR, | 
 |             cmStrCat( | 
 |               "Base directories in file set cannot be subdirectories of each " | 
 |               "other:\n  ", | 
 |               priorDir, "\n  ", dir), | 
 |             cge->GetBacktrace()); | 
 |           return {}; | 
 |         } | 
 |       } | 
 |       result.push_back(dir); | 
 |     } | 
 |   } | 
 |   return result; | 
 | } | 
 |  | 
 | void cmFileSet::EvaluateFileEntry( | 
 |   std::vector<std::string> const& dirs, | 
 |   std::map<std::string, std::vector<std::string>>& filesPerDir, | 
 |   std::unique_ptr<cmCompiledGeneratorExpression> const& cge, | 
 |   cm::GenEx::Context const& context, cmGeneratorTarget const* target, | 
 |   cmGeneratorExpressionDAGChecker* dagChecker) const | 
 | { | 
 |   auto files = cge->Evaluate(context, dagChecker, target); | 
 |   for (std::string file : cmList{ files }) { | 
 |     if (!cmSystemTools::FileIsFullPath(file)) { | 
 |       file = cmStrCat(context.LG->GetCurrentSourceDirectory(), '/', file); | 
 |     } | 
 |     auto collapsedFile = cmSystemTools::CollapseFullPath(file); | 
 |     bool found = false; | 
 |     std::string relDir; | 
 |     for (auto const& dir : dirs) { | 
 |       auto collapsedDir = cmSystemTools::CollapseFullPath(dir); | 
 |       if (cmSystemTools::IsSubDirectory(collapsedFile, collapsedDir)) { | 
 |         found = true; | 
 |         relDir = cmSystemTools::GetParentDirectory( | 
 |           cmSystemTools::RelativePath(collapsedDir, collapsedFile)); | 
 |         break; | 
 |       } | 
 |     } | 
 |     if (!found) { | 
 |       std::ostringstream e; | 
 |       e << "File:\n  " << file | 
 |         << "\nmust be in one of the file set's base directories:"; | 
 |       for (auto const& dir : dirs) { | 
 |         e << "\n  " << dir; | 
 |       } | 
 |       context.LG->GetCMakeInstance()->IssueMessage( | 
 |         MessageType::FATAL_ERROR, e.str(), cge->GetBacktrace()); | 
 |       return; | 
 |     } | 
 |  | 
 |     filesPerDir[relDir].push_back(file); | 
 |   } | 
 | } | 
 |  | 
 | bool cmFileSet::IsValidName(std::string const& name) | 
 | { | 
 |   static cmsys::RegularExpression const regex("^[a-z0-9][a-zA-Z0-9_]*$"); | 
 |  | 
 |   cmsys::RegularExpressionMatch match; | 
 |   return regex.find(name.c_str(), match); | 
 | } |