/* 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); }