| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt 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 "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( |
| const std::vector<std::unique_ptr<cmCompiledGeneratorExpression>>& cges, |
| cmLocalGenerator* lg, const std::string& config, |
| const cmGeneratorTarget* 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(lg, config, target, dagChecker); |
| cmList dirs{ entry }; |
| for (std::string dir : dirs) { |
| if (!cmSystemTools::FileIsFullPath(dir)) { |
| dir = cmStrCat(lg->GetCurrentSourceDirectory(), '/', dir); |
| } |
| |
| auto dirCacheResult = dirCache.emplace(dir, DirCacheEntry()); |
| auto& dirCacheEntry = dirCacheResult.first->second; |
| const auto 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))) { |
| 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( |
| const std::vector<std::string>& dirs, |
| std::map<std::string, std::vector<std::string>>& filesPerDir, |
| const std::unique_ptr<cmCompiledGeneratorExpression>& cge, |
| cmLocalGenerator* lg, const std::string& config, |
| const cmGeneratorTarget* target, |
| cmGeneratorExpressionDAGChecker* dagChecker) const |
| { |
| auto files = cge->Evaluate(lg, config, target, dagChecker); |
| for (std::string file : cmList{ files }) { |
| if (!cmSystemTools::FileIsFullPath(file)) { |
| file = cmStrCat(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; |
| } |
| lg->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str(), |
| cge->GetBacktrace()); |
| return; |
| } |
| |
| filesPerDir[relDir].push_back(file); |
| } |
| } |
| |
| bool cmFileSet::IsValidName(const std::string& name) |
| { |
| static const cmsys::RegularExpression regex("^[a-z0-9][a-zA-Z0-9_]*$"); |
| |
| cmsys::RegularExpressionMatch match; |
| return regex.find(name.c_str(), match); |
| } |