//===----------------------- SearchPathOptions.cpp ------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "swift/AST/SearchPathOptions.h" #include "swift/Basic/Assertions.h" #include "llvm/ADT/SmallSet.h" #include "llvm/Support/Errc.h" using namespace swift; void ModuleSearchPathLookup::addFilesInPathToLookupTable( llvm::vfs::FileSystem *FS, StringRef SearchPath, ModuleSearchPathKind Kind, bool IsSystem, unsigned SearchPathIndex) { std::error_code Error; auto entryAlreadyExists = [this](ModuleSearchPathKind Kind, unsigned SearchPathIndex) -> bool { return llvm::any_of(LookupTable, [&](const auto &LookupTableEntry) { return llvm::any_of( LookupTableEntry.second, [&](ModuleSearchPathPtr ExistingSearchPath) { return ExistingSearchPath->getKind() == Kind && ExistingSearchPath->getIndex() == SearchPathIndex; }); }); }; assert(!entryAlreadyExists(Kind, SearchPathIndex) && "Search path with this kind and index already exists"); ModuleSearchPathPtr TableEntry = new ModuleSearchPath(SearchPath, Kind, IsSystem, SearchPathIndex); for (auto Dir = FS->dir_begin(SearchPath, Error); !Error && Dir != llvm::vfs::directory_iterator(); Dir.increment(Error)) { StringRef Filename = llvm::sys::path::filename(Dir->path()); LookupTable[Filename].push_back(TableEntry); } } void ModuleSearchPathLookup::rebuildLookupTable(const SearchPathOptions *Opts, llvm::vfs::FileSystem *FS, bool IsOSDarwin) { clearLookupTable(); for (auto Entry : llvm::enumerate(Opts->getImportSearchPaths())) { addFilesInPathToLookupTable(FS, Entry.value().Path, ModuleSearchPathKind::Import, Entry.value().IsSystem, Entry.index()); } for (auto Entry : llvm::enumerate(Opts->getFrameworkSearchPaths())) { addFilesInPathToLookupTable(FS, Entry.value().Path, ModuleSearchPathKind::Framework, Entry.value().IsSystem, Entry.index()); } for (auto Entry : llvm::enumerate(Opts->getImplicitFrameworkSearchPaths())) { addFilesInPathToLookupTable(FS, Entry.value(), ModuleSearchPathKind::ImplicitFramework, /*isSystem=*/true, Entry.index()); } for (auto Entry : llvm::enumerate(Opts->getRuntimeLibraryImportPaths())) { addFilesInPathToLookupTable(FS, Entry.value(), ModuleSearchPathKind::RuntimeLibrary, /*isSystem=*/true, Entry.index()); } State.FileSystem = FS; State.IsOSDarwin = IsOSDarwin; State.Opts = Opts; State.IsPopulated = true; } static std::string computeSDKPlatformPath(StringRef SDKPath, llvm::vfs::FileSystem *FS) { if (SDKPath.empty()) return ""; SmallString<128> platformPath; if (auto err = FS->getRealPath(SDKPath, platformPath)) llvm::sys::path::append(platformPath, SDKPath); llvm::sys::path::remove_filename(platformPath); // specific SDK llvm::sys::path::remove_filename(platformPath); // SDKs llvm::sys::path::remove_filename(platformPath); // Developer if (!llvm::sys::path::filename(platformPath).ends_with(".platform")) return ""; return platformPath.str().str(); } std::optional SearchPathOptions::getSDKPlatformPath(llvm::vfs::FileSystem *FS) const { if (!SDKPlatformPath) SDKPlatformPath = computeSDKPlatformPath(getSDKPath(), FS); if (SDKPlatformPath->empty()) return std::nullopt; return *SDKPlatformPath; } void SearchPathOptions::dump(bool isDarwin) const { llvm::errs() << "Module import search paths:\n"; for (auto Entry : llvm::enumerate(getImportSearchPaths())) { llvm::errs() << " [" << Entry.index() << "] " << (Entry.value().IsSystem ? "(system) " : "(non-system) ") << Entry.value().Path << "\n"; } llvm::errs() << "Framework search paths:\n"; for (auto Entry : llvm::enumerate(getFrameworkSearchPaths())) { llvm::errs() << " [" << Entry.index() << "] " << (Entry.value().IsSystem ? "(system) " : "(non-system) ") << Entry.value().Path << "\n"; } llvm::errs() << "Implicit framework search paths:\n"; for (auto Entry : llvm::enumerate(getImplicitFrameworkSearchPaths())) { llvm::errs() << " [" << Entry.index() << "] " << Entry.value() << "\n"; } llvm::errs() << "Runtime library import search paths:\n"; for (auto Entry : llvm::enumerate(getRuntimeLibraryImportPaths())) { llvm::errs() << " [" << Entry.index() << "] " << Entry.value() << "\n"; } llvm::errs() << "(End of search path lists.)\n"; } SmallVector ModuleSearchPathLookup::searchPathsContainingFile( const SearchPathOptions *Opts, llvm::ArrayRef Filenames, llvm::vfs::FileSystem *FS, bool IsOSDarwin) { if (!State.IsPopulated || State.FileSystem != FS || State.IsOSDarwin != IsOSDarwin || State.Opts != Opts) { rebuildLookupTable(Opts, FS, IsOSDarwin); } // Gather all search paths that include a file whose name is in Filenames. // To make sure that we don't include the same search paths twice, keep track // of which search paths have already been added to Result by their kind and // Index in ResultIds. // Note that if a search path is specified twice by including it twice in // compiler arguments or by specifying it as different kinds (e.g. once as // import and once as framework search path), these search paths are // considered different (because they have different indices/kinds and may // thus still be included twice. llvm::SmallVector Result; llvm::SmallSet, 4> ResultIds; for (auto &Filename : Filenames) { if (LookupTable.contains(Filename)) { for (auto &Entry : LookupTable.at(Filename)) { if (ResultIds.insert(std::make_pair(Entry->getKind(), Entry->getIndex())) .second) { Result.push_back(Entry.get()); } } } } // Make sure we maintain the same search paths order that we had used in // populateLookupTableIfNecessary after merging results from // different filenames. llvm::sort(Result, [](const ModuleSearchPath *Lhs, const ModuleSearchPath *Rhs) { return *Lhs < *Rhs; }); return Result; } /// Loads a VFS YAML file located at \p File using \p BaseFS and adds it to /// \p OverlayFS. Returns an error if either loading the \p File failed or it /// is invalid. static llvm::Error loadAndValidateVFSOverlay( const std::string &File, const llvm::IntrusiveRefCntPtr &BaseFS, const llvm::IntrusiveRefCntPtr &OverlayFS) { auto Buffer = BaseFS->getBufferForFile(File); if (!Buffer) return llvm::createFileError(File, Buffer.getError()); auto VFS = llvm::vfs::getVFSFromYAML(std::move(Buffer.get()), nullptr, File); if (!VFS) return llvm::createFileError(File, llvm::errc::invalid_argument); OverlayFS->pushOverlay(std::move(VFS)); return llvm::Error::success(); } llvm::Expected> SearchPathOptions::makeOverlayFileSystem( llvm::IntrusiveRefCntPtr BaseFS) const { // TODO: This implementation is different to how Clang reads overlays in. // Expose a helper in Clang rather than doing this ourselves. auto OverlayFS = llvm::makeIntrusiveRefCnt(BaseFS); llvm::Error AllErrors = llvm::Error::success(); bool hasOverlays = false; for (const auto &File : VFSOverlayFiles) { hasOverlays = true; if (auto Err = loadAndValidateVFSOverlay(File, BaseFS, OverlayFS)) AllErrors = llvm::joinErrors(std::move(AllErrors), std::move(Err)); } if (AllErrors) return std::move(AllErrors); if (hasOverlays) return OverlayFS; return BaseFS; }