mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
When looking for a Swift module on disk, we were scanning all module search paths if they contain the module we are searching for. In a setup where each module is contained in its own framework search path, this scaled quadratically with the number of modules being imported. E.g. a setup with 100 modules being imported form 100 module search paths could cause on the order of 10,000 checks of `FileSystem::exists`. While these checks are fairly fast (~10µs), they add up to ~100ms. To improve this, perform a first scan of all module search paths and list the files they contain. From this, create a lookup map that maps filenames to the search paths they can be found in. E.g. for ``` searchPath1/ Module1.framework searchPath2/ Module1.framework Module2.swiftmodule ``` we create the following lookup table ``` Module1.framework -> [searchPath1, searchPath2] Module2.swiftmodule -> [searchPath2] ```
108 lines
4.6 KiB
C++
108 lines
4.6 KiB
C++
//===----------------------- 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 "llvm/ADT/SmallSet.h"
|
|
|
|
using namespace swift;
|
|
|
|
void ModuleSearchPathLookup::addFilesInPathToLookupTable(
|
|
llvm::vfs::FileSystem *FS, StringRef SearchPath, ModuleSearchPathKind Kind,
|
|
bool IsSystem, unsigned SearchPathIndex) {
|
|
std::error_code Error;
|
|
assert(llvm::all_of(LookupTable, [&](const auto &LookupTableEntry) {
|
|
return llvm::none_of(LookupTableEntry.second, [&](const ModuleSearchPath &ExistingSearchPath) -> bool {
|
|
return ExistingSearchPath.Kind == Kind && ExistingSearchPath.Index == SearchPathIndex;
|
|
});
|
|
}) && "Search path with this kind and index already exists");
|
|
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(
|
|
{SearchPath, Kind, IsSystem, SearchPathIndex});
|
|
}
|
|
}
|
|
|
|
void ModuleSearchPathLookup::rebuildLookupTable(const SearchPathOptions *Opts,
|
|
llvm::vfs::FileSystem *FS,
|
|
bool IsOSDarwin) {
|
|
clearLookupTable();
|
|
|
|
for (auto Entry : llvm::enumerate(Opts->getImportSearchPaths())) {
|
|
addFilesInPathToLookupTable(FS, Entry.value(),
|
|
ModuleSearchPathKind::Import,
|
|
/*isSystem=*/false, Entry.index());
|
|
}
|
|
|
|
for (auto Entry : llvm::enumerate(Opts->getFrameworkSearchPaths())) {
|
|
addFilesInPathToLookupTable(FS, Entry.value().Path, ModuleSearchPathKind::Framework,
|
|
Entry.value().IsSystem, Entry.index());
|
|
}
|
|
|
|
// Apple platforms have extra implicit framework search paths:
|
|
// $SDKROOT/System/Library/Frameworks/ and $SDKROOT/Library/Frameworks/.
|
|
if (IsOSDarwin) {
|
|
for (auto Entry : llvm::enumerate(Opts->getDarwinImplicitFrameworkSearchPaths())) {
|
|
addFilesInPathToLookupTable(FS, Entry.value(),
|
|
ModuleSearchPathKind::DarwinImplictFramework,
|
|
/*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;
|
|
}
|
|
|
|
SmallVector<const ModuleSearchPath *, 4>
|
|
ModuleSearchPathLookup::searchPathsContainingFile(
|
|
const SearchPathOptions *Opts, llvm::ArrayRef<std::string> 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 indicies/kinds and may
|
|
// thus still be included twice.
|
|
llvm::SmallVector<const ModuleSearchPath *, 4> Result;
|
|
llvm::SmallSet<std::pair<ModuleSearchPathKind, unsigned>, 4> ResultIds;
|
|
|
|
for (auto &Filename : Filenames) {
|
|
for (auto &Entry : LookupTable[Filename]) {
|
|
if (ResultIds.insert(std::make_pair(Entry.Kind, Entry.Index)).second) {
|
|
Result.push_back(&Entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|