mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +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] ```
127 lines
5.2 KiB
C++
127 lines
5.2 KiB
C++
//===--- ModuleDependencyScanner.h - Import Swift modules --------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2020 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/ASTContext.h"
|
|
#include "swift/Frontend/ModuleInterfaceLoader.h"
|
|
#include "swift/Serialization/SerializedModuleLoader.h"
|
|
|
|
namespace swift {
|
|
/// A module "loader" that looks for .swiftinterface and .swiftmodule files
|
|
/// for the purpose of determining dependencies, but does not attempt to
|
|
/// load the module files.
|
|
class ModuleDependencyScanner : public SerializedModuleLoaderBase {
|
|
public:
|
|
enum ScannerKind {
|
|
MDS_plain,
|
|
MDS_placeholder
|
|
};
|
|
|
|
private:
|
|
/// The kind of scanner this is (LLVM-style RTTI)
|
|
const ScannerKind kind;
|
|
|
|
/// The module we're scanning dependencies of.
|
|
Identifier moduleName;
|
|
|
|
/// Scan the given interface file to determine dependencies.
|
|
llvm::ErrorOr<ModuleDependencies> scanInterfaceFile(
|
|
Twine moduleInterfacePath, bool isFramework);
|
|
|
|
InterfaceSubContextDelegate &astDelegate;
|
|
public:
|
|
Optional<ModuleDependencies> dependencies;
|
|
|
|
ModuleDependencyScanner(
|
|
ASTContext &ctx, ModuleLoadingMode LoadMode, Identifier moduleName,
|
|
InterfaceSubContextDelegate &astDelegate,
|
|
ScannerKind kind = MDS_plain)
|
|
: SerializedModuleLoaderBase(ctx, nullptr, LoadMode,
|
|
/*IgnoreSwiftSourceInfoFile=*/true),
|
|
kind(kind), moduleName(moduleName), astDelegate(astDelegate) {}
|
|
|
|
std::error_code findModuleFilesInDirectory(
|
|
ImportPath::Element ModuleID,
|
|
const SerializedModuleBaseName &BaseName,
|
|
SmallVectorImpl<char> *ModuleInterfacePath,
|
|
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
|
|
std::unique_ptr<llvm::MemoryBuffer> *ModuleDocBuffer,
|
|
std::unique_ptr<llvm::MemoryBuffer> *ModuleSourceInfoBuffer,
|
|
bool skipBuildingInterface, bool IsFramework) override;
|
|
|
|
virtual void collectVisibleTopLevelModuleNames(
|
|
SmallVectorImpl<Identifier> &names) const override {
|
|
llvm_unreachable("Not used");
|
|
}
|
|
|
|
ScannerKind getKind() const { return kind; }
|
|
static bool classof(const ModuleDependencyScanner *MDS) {
|
|
return MDS->getKind() == MDS_plain;
|
|
}
|
|
};
|
|
|
|
/// A ModuleLoader that loads placeholder dependency module stubs specified in
|
|
/// -placeholder-dependency-module-map-file
|
|
/// This loader is used only in dependency scanning to inform the scanner that a
|
|
/// set of modules constitute placeholder dependencies that are not visible to the
|
|
/// scanner but will nevertheless be provided by the scanner's clients.
|
|
/// This "loader" will not attempt to load any module files.
|
|
class PlaceholderSwiftModuleScanner : public ModuleDependencyScanner {
|
|
/// Scan the given placeholder module map
|
|
void parsePlaceholderModuleMap(StringRef fileName) {
|
|
ExplicitModuleMapParser parser(Allocator);
|
|
auto result =
|
|
parser.parseSwiftExplicitModuleMap(fileName, PlaceholderDependencyModuleMap);
|
|
if (result == std::errc::invalid_argument) {
|
|
Ctx.Diags.diagnose(SourceLoc(),
|
|
diag::placeholder_dependency_module_map_corrupted,
|
|
fileName);
|
|
}
|
|
else if (result == std::errc::no_such_file_or_directory) {
|
|
Ctx.Diags.diagnose(SourceLoc(),
|
|
diag::placeholder_dependency_module_map_missing,
|
|
fileName);
|
|
}
|
|
}
|
|
|
|
llvm::StringMap<ExplicitModuleInfo> PlaceholderDependencyModuleMap;
|
|
llvm::BumpPtrAllocator Allocator;
|
|
|
|
public:
|
|
PlaceholderSwiftModuleScanner(ASTContext &ctx, ModuleLoadingMode LoadMode,
|
|
Identifier moduleName,
|
|
StringRef PlaceholderDependencyModuleMap,
|
|
InterfaceSubContextDelegate &astDelegate)
|
|
: ModuleDependencyScanner(ctx, LoadMode, moduleName, astDelegate,
|
|
MDS_placeholder) {
|
|
|
|
// FIXME: Find a better place for this map to live, to avoid
|
|
// doing the parsing on every module.
|
|
if (!PlaceholderDependencyModuleMap.empty()) {
|
|
parsePlaceholderModuleMap(PlaceholderDependencyModuleMap);
|
|
}
|
|
}
|
|
|
|
virtual bool
|
|
findModule(ImportPath::Element moduleID,
|
|
SmallVectorImpl<char> *moduleInterfacePath,
|
|
std::unique_ptr<llvm::MemoryBuffer> *moduleBuffer,
|
|
std::unique_ptr<llvm::MemoryBuffer> *moduleDocBuffer,
|
|
std::unique_ptr<llvm::MemoryBuffer> *moduleSourceInfoBuffer,
|
|
bool skipBuildingInterface, bool &isFramework,
|
|
bool &isSystemModule) override;
|
|
|
|
static bool classof(const ModuleDependencyScanner *MDS) {
|
|
return MDS->getKind() == MDS_placeholder;
|
|
}
|
|
};
|
|
}
|