mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This reverts commit 4f059033bb. The change
is actually not NFC since previously, there is a cache in the
CompilerInvocation that prevents the same CAS from the same CASOptions
from being initialized multiple times, which was relied upon when
running inside sub invocation. When switching to a non-caching simple
CASOption types, it causes every single sub instance will create its own
CAS, and it can consume too many file descriptors and causing errors
during dependency scanning.
rdar://164903080
1019 lines
38 KiB
C++
1019 lines
38 KiB
C++
//===--- ModuleDependencies.h - Module Dependencies -------------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements data structures for capturing module dependencies.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "swift/AST/ModuleDependencies.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/DiagnosticsFrontend.h"
|
|
#include "swift/AST/DiagnosticsSema.h"
|
|
#include "swift/AST/MacroDefinition.h"
|
|
#include "swift/AST/Module.h"
|
|
#include "swift/AST/PluginLoader.h"
|
|
#include "swift/AST/SourceFile.h"
|
|
#include "swift/Frontend/Frontend.h"
|
|
#include "swift/Strings.h"
|
|
#include "clang/Lex/HeaderSearchOptions.h"
|
|
#include "llvm/Config/config.h"
|
|
#include "llvm/Support/Path.h"
|
|
using namespace swift;
|
|
|
|
ModuleDependencyInfoStorageBase::~ModuleDependencyInfoStorageBase() {}
|
|
|
|
bool ModuleDependencyInfo::isSwiftModule() const {
|
|
return isSwiftInterfaceModule() || isSwiftSourceModule() ||
|
|
isSwiftBinaryModule();
|
|
}
|
|
|
|
bool ModuleDependencyInfo::isTextualSwiftModule() const {
|
|
return isSwiftInterfaceModule() || isSwiftSourceModule();
|
|
}
|
|
|
|
namespace {
|
|
ModuleDependencyKind &operator++(ModuleDependencyKind &e) {
|
|
if (e == ModuleDependencyKind::LastKind) {
|
|
llvm_unreachable(
|
|
"Attempting to increment last enum value on ModuleDependencyKind");
|
|
}
|
|
e = ModuleDependencyKind(
|
|
static_cast<std::underlying_type<ModuleDependencyKind>::type>(e) + 1);
|
|
return e;
|
|
}
|
|
}
|
|
bool ModuleDependencyInfo::isSwiftInterfaceModule() const {
|
|
return isa<SwiftInterfaceModuleDependenciesStorage>(storage.get());
|
|
}
|
|
|
|
bool ModuleDependencyInfo::isSwiftSourceModule() const {
|
|
return isa<SwiftSourceModuleDependenciesStorage>(storage.get());
|
|
}
|
|
|
|
bool ModuleDependencyInfo::isSwiftBinaryModule() const {
|
|
return isa<SwiftBinaryModuleDependencyStorage>(storage.get());
|
|
}
|
|
|
|
bool ModuleDependencyInfo::isClangModule() const {
|
|
return isa<ClangModuleDependencyStorage>(storage.get());
|
|
}
|
|
|
|
/// Retrieve the dependencies for a Swift textual interface module.
|
|
const SwiftInterfaceModuleDependenciesStorage *
|
|
ModuleDependencyInfo::getAsSwiftInterfaceModule() const {
|
|
return dyn_cast<SwiftInterfaceModuleDependenciesStorage>(storage.get());
|
|
}
|
|
|
|
const SwiftSourceModuleDependenciesStorage *
|
|
ModuleDependencyInfo::getAsSwiftSourceModule() const {
|
|
return dyn_cast<SwiftSourceModuleDependenciesStorage>(storage.get());
|
|
}
|
|
|
|
/// Retrieve the dependencies for a binary Swift dependency module.
|
|
const SwiftBinaryModuleDependencyStorage *
|
|
ModuleDependencyInfo::getAsSwiftBinaryModule() const {
|
|
return dyn_cast<SwiftBinaryModuleDependencyStorage>(storage.get());
|
|
}
|
|
|
|
/// Retrieve the dependencies for a Clang module.
|
|
const ClangModuleDependencyStorage *
|
|
ModuleDependencyInfo::getAsClangModule() const {
|
|
return dyn_cast<ClangModuleDependencyStorage>(storage.get());
|
|
}
|
|
|
|
std::string ModuleDependencyInfo::getModuleDefiningPath() const {
|
|
std::string path = "";
|
|
switch (getKind()) {
|
|
case swift::ModuleDependencyKind::SwiftInterface:
|
|
path = getAsSwiftInterfaceModule()->swiftInterfaceFile;
|
|
break;
|
|
case swift::ModuleDependencyKind::SwiftBinary:
|
|
path = getAsSwiftBinaryModule()->compiledModulePath;
|
|
break;
|
|
case swift::ModuleDependencyKind::Clang:
|
|
path = getAsClangModule()->moduleMapFile;
|
|
break;
|
|
case swift::ModuleDependencyKind::SwiftSource:
|
|
path = getAsSwiftSourceModule()->sourceFiles.front();
|
|
break;
|
|
default:
|
|
llvm_unreachable("Unexpected dependency kind");
|
|
}
|
|
|
|
// Relative to the `module.modulemap` or `.swiftinterface` or `.swiftmodule`,
|
|
// the defininig path is the parent directory of the file.
|
|
return llvm::sys::path::parent_path(path).str();
|
|
}
|
|
|
|
void ModuleDependencyInfo::addTestableImport(ImportPath::Module module) {
|
|
assert(getAsSwiftSourceModule() && "Expected source module for addTestableImport.");
|
|
dyn_cast<SwiftSourceModuleDependenciesStorage>(storage.get())->addTestableImport(module);
|
|
}
|
|
|
|
bool ModuleDependencyInfo::isTestableImport(StringRef moduleName) const {
|
|
if (auto swiftSourceDepStorage = getAsSwiftSourceModule())
|
|
return swiftSourceDepStorage->testableImports.contains(moduleName);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void ModuleDependencyInfo::addOptionalModuleImport(
|
|
StringRef module, bool isExported, AccessLevel accessLevel,
|
|
llvm::StringSet<> *alreadyAddedModules) {
|
|
|
|
if (alreadyAddedModules && alreadyAddedModules->contains(module)) {
|
|
// Find a prior import of this module and add import location
|
|
// and adjust whether or not this module is ever imported as exported
|
|
// as well as the access level
|
|
for (auto &existingImport : storage->optionalModuleImports) {
|
|
if (existingImport.importIdentifier == module) {
|
|
existingImport.isExported |= isExported;
|
|
existingImport.accessLevel = std::max(existingImport.accessLevel,
|
|
accessLevel);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
if (alreadyAddedModules)
|
|
alreadyAddedModules->insert(module);
|
|
|
|
storage->optionalModuleImports.push_back(
|
|
{module.str(), isExported, accessLevel});
|
|
}
|
|
}
|
|
|
|
void ModuleDependencyInfo::addModuleImport(
|
|
StringRef module, bool isExported, AccessLevel accessLevel,
|
|
llvm::StringSet<> *alreadyAddedModules, const SourceManager *sourceManager,
|
|
SourceLoc sourceLocation) {
|
|
auto scannerImportLocToDiagnosticLocInfo =
|
|
[&sourceManager](SourceLoc sourceLocation) {
|
|
auto lineAndColumnNumbers =
|
|
sourceManager->getLineAndColumnInBuffer(sourceLocation);
|
|
return ScannerImportStatementInfo::ImportDiagnosticLocationInfo(
|
|
sourceManager->getDisplayNameForLoc(sourceLocation).str(),
|
|
lineAndColumnNumbers.first, lineAndColumnNumbers.second);
|
|
};
|
|
bool validSourceLocation = sourceManager && sourceLocation.isValid() &&
|
|
sourceManager->isOwning(sourceLocation);
|
|
|
|
if (alreadyAddedModules && alreadyAddedModules->contains(module)) {
|
|
// Find a prior import of this module and add import location
|
|
// and adjust whether or not this module is ever imported as exported
|
|
// as well as the access level
|
|
for (auto &existingImport : storage->moduleImports) {
|
|
if (existingImport.importIdentifier == module) {
|
|
if (validSourceLocation) {
|
|
existingImport.addImportLocation(
|
|
scannerImportLocToDiagnosticLocInfo(sourceLocation));
|
|
}
|
|
existingImport.isExported |= isExported;
|
|
existingImport.accessLevel = std::max(existingImport.accessLevel,
|
|
accessLevel);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
if (alreadyAddedModules)
|
|
alreadyAddedModules->insert(module);
|
|
|
|
if (validSourceLocation)
|
|
storage->moduleImports.push_back(ScannerImportStatementInfo(
|
|
module.str(), isExported, accessLevel,
|
|
scannerImportLocToDiagnosticLocInfo(sourceLocation)));
|
|
else
|
|
storage->moduleImports.push_back(
|
|
ScannerImportStatementInfo(module.str(), isExported, accessLevel));
|
|
}
|
|
}
|
|
|
|
void ModuleDependencyInfo::addModuleImport(
|
|
ImportPath::Module module, bool isExported, AccessLevel accessLevel,
|
|
llvm::StringSet<> *alreadyAddedModules, const SourceManager *sourceManager,
|
|
SourceLoc sourceLocation) {
|
|
std::string ImportedModuleName = module.front().Item.str().str();
|
|
auto submodulePath = module.getSubmodulePath();
|
|
if (submodulePath.size() > 0 && !submodulePath[0].Item.empty()) {
|
|
auto submoduleComponent = submodulePath[0];
|
|
// Special case: a submodule named "Foo.Private" can be moved to a top-level
|
|
// module named "Foo_Private". ClangImporter has special support for this.
|
|
if (submoduleComponent.Item.str() == "Private")
|
|
addOptionalModuleImport(ImportedModuleName + "_Private",
|
|
isExported, accessLevel,
|
|
alreadyAddedModules);
|
|
}
|
|
|
|
addModuleImport(ImportedModuleName, isExported, accessLevel,
|
|
alreadyAddedModules, sourceManager, sourceLocation);
|
|
}
|
|
|
|
void ModuleDependencyInfo::addModuleImports(
|
|
const SourceFile &sourceFile, llvm::StringSet<> &alreadyAddedModules,
|
|
const SourceManager *sourceManager) {
|
|
// Add all of the module dependencies.
|
|
SmallVector<Decl *, 32> decls;
|
|
sourceFile.getTopLevelDecls(decls);
|
|
for (auto decl : decls) {
|
|
if (auto importDecl = dyn_cast<ImportDecl>(decl)) {
|
|
ImportPath::Builder scratch;
|
|
auto realPath = importDecl->getRealModulePath(scratch);
|
|
|
|
// Explicit 'Builtin' import is not a part of the module's
|
|
// dependency set, does not exist on the filesystem,
|
|
// and is resolved within the compiler during compilation.
|
|
SmallString<64> importedModuleName;
|
|
realPath.getString(importedModuleName);
|
|
if (importedModuleName == BUILTIN_NAME)
|
|
continue;
|
|
|
|
// Ignore/diagnose tautological imports akin to import resolution
|
|
if (!swift::dependencies::checkImportNotTautological(
|
|
realPath, importDecl->getLoc(), sourceFile,
|
|
importDecl->isExported()))
|
|
continue;
|
|
|
|
addModuleImport(realPath, importDecl->isExported(),
|
|
importDecl->getAccessLevel(),
|
|
&alreadyAddedModules, sourceManager,
|
|
importDecl->getLoc());
|
|
|
|
// Additionally, keep track of which dependencies of a Source
|
|
// module are `@Testable`.
|
|
if (getKind() == swift::ModuleDependencyKind::SwiftSource &&
|
|
importDecl->isTestable())
|
|
addTestableImport(realPath);
|
|
} else if (auto macroDecl = dyn_cast<MacroDecl>(decl)) {
|
|
auto macroDef = macroDecl->getDefinition();
|
|
auto &ctx = macroDecl->getASTContext();
|
|
if (macroDef.kind != MacroDefinition::Kind::External)
|
|
continue;
|
|
auto external = macroDef.getExternalMacro();
|
|
PluginLoader &loader = ctx.getPluginLoader();
|
|
auto &entry = loader.lookupPluginByModuleName(external.moduleName);
|
|
if (entry.libraryPath.empty() && entry.executablePath.empty())
|
|
continue;
|
|
addMacroDependency(external.moduleName.str(), entry.libraryPath,
|
|
entry.executablePath);
|
|
}
|
|
}
|
|
|
|
auto fileName = sourceFile.getFilename();
|
|
if (fileName.empty())
|
|
return;
|
|
|
|
switch (getKind()) {
|
|
case swift::ModuleDependencyKind::SwiftInterface: {
|
|
// If the storage is for an interface file, the only source file we
|
|
// should see is that interface file.
|
|
assert(fileName ==
|
|
cast<SwiftInterfaceModuleDependenciesStorage>(storage.get())->swiftInterfaceFile);
|
|
break;
|
|
}
|
|
case swift::ModuleDependencyKind::SwiftSource: {
|
|
// Otherwise, record the source file.
|
|
auto swiftSourceStorage =
|
|
cast<SwiftSourceModuleDependenciesStorage>(storage.get());
|
|
swiftSourceStorage->sourceFiles.push_back(fileName.str());
|
|
break;
|
|
}
|
|
default:
|
|
llvm_unreachable("Unexpected dependency kind");
|
|
}
|
|
}
|
|
|
|
std::optional<std::string> ModuleDependencyInfo::getBridgingHeader() const {
|
|
switch (getKind()) {
|
|
case swift::ModuleDependencyKind::SwiftInterface: {
|
|
auto swiftInterfaceStorage =
|
|
cast<SwiftInterfaceModuleDependenciesStorage>(storage.get());
|
|
return swiftInterfaceStorage->textualModuleDetails.bridgingHeaderFile;
|
|
}
|
|
case swift::ModuleDependencyKind::SwiftSource: {
|
|
auto swiftSourceStorage =
|
|
cast<SwiftSourceModuleDependenciesStorage>(storage.get());
|
|
return swiftSourceStorage->textualModuleDetails.bridgingHeaderFile;
|
|
}
|
|
default:
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
std::optional<std::string> ModuleDependencyInfo::getCASFSRootID() const {
|
|
std::string Root;
|
|
switch (getKind()) {
|
|
case swift::ModuleDependencyKind::SwiftInterface: {
|
|
auto swiftInterfaceStorage =
|
|
cast<SwiftInterfaceModuleDependenciesStorage>(storage.get());
|
|
Root = swiftInterfaceStorage->textualModuleDetails.CASFileSystemRootID;
|
|
break;
|
|
}
|
|
case swift::ModuleDependencyKind::SwiftSource: {
|
|
auto swiftSourceStorage =
|
|
cast<SwiftSourceModuleDependenciesStorage>(storage.get());
|
|
Root = swiftSourceStorage->textualModuleDetails.CASFileSystemRootID;
|
|
break;
|
|
}
|
|
case swift::ModuleDependencyKind::Clang: {
|
|
auto clangModuleStorage = cast<ClangModuleDependencyStorage>(storage.get());
|
|
Root = clangModuleStorage->CASFileSystemRootID;
|
|
break;
|
|
}
|
|
default:
|
|
return std::nullopt;
|
|
}
|
|
if (Root.empty())
|
|
return std::nullopt;
|
|
|
|
return Root;
|
|
}
|
|
|
|
std::optional<std::string> ModuleDependencyInfo::getClangIncludeTree() const {
|
|
std::string Root;
|
|
switch (getKind()) {
|
|
case swift::ModuleDependencyKind::Clang: {
|
|
auto clangModuleStorage = cast<ClangModuleDependencyStorage>(storage.get());
|
|
Root = clangModuleStorage->CASClangIncludeTreeRootID;
|
|
break;
|
|
}
|
|
default:
|
|
return std::nullopt;
|
|
}
|
|
if (Root.empty())
|
|
return std::nullopt;
|
|
|
|
return Root;
|
|
}
|
|
|
|
std::optional<std::string>
|
|
ModuleDependencyInfo::getBridgingHeaderIncludeTree() const {
|
|
std::string Root;
|
|
switch (getKind()) {
|
|
case swift::ModuleDependencyKind::SwiftInterface: {
|
|
auto swiftInterfaceStorage =
|
|
cast<SwiftInterfaceModuleDependenciesStorage>(storage.get());
|
|
Root = swiftInterfaceStorage->textualModuleDetails
|
|
.CASBridgingHeaderIncludeTreeRootID;
|
|
break;
|
|
}
|
|
case swift::ModuleDependencyKind::SwiftSource: {
|
|
auto swiftSourceStorage =
|
|
cast<SwiftSourceModuleDependenciesStorage>(storage.get());
|
|
Root = swiftSourceStorage->textualModuleDetails
|
|
.CASBridgingHeaderIncludeTreeRootID;
|
|
break;
|
|
}
|
|
default:
|
|
return std::nullopt;
|
|
}
|
|
if (Root.empty())
|
|
return std::nullopt;
|
|
|
|
return Root;
|
|
}
|
|
|
|
std::string ModuleDependencyInfo::getModuleOutputPath() const {
|
|
switch (getKind()) {
|
|
case swift::ModuleDependencyKind::SwiftInterface: {
|
|
auto swiftInterfaceStorage =
|
|
cast<SwiftInterfaceModuleDependenciesStorage>(storage.get());
|
|
return swiftInterfaceStorage->moduleOutputPath;
|
|
}
|
|
case swift::ModuleDependencyKind::SwiftSource: {
|
|
return "<swiftmodule>";
|
|
}
|
|
case swift::ModuleDependencyKind::Clang: {
|
|
auto clangModuleStorage = cast<ClangModuleDependencyStorage>(storage.get());
|
|
return clangModuleStorage->pcmOutputPath;
|
|
}
|
|
case swift::ModuleDependencyKind::SwiftBinary: {
|
|
auto swiftBinaryStorage =
|
|
cast<SwiftBinaryModuleDependencyStorage>(storage.get());
|
|
return swiftBinaryStorage->compiledModulePath;
|
|
}
|
|
default:
|
|
llvm_unreachable("Unexpected dependency kind");
|
|
}
|
|
}
|
|
|
|
void ModuleDependencyInfo::addBridgingHeader(StringRef bridgingHeader) {
|
|
switch (getKind()) {
|
|
case swift::ModuleDependencyKind::SwiftInterface: {
|
|
auto swiftInterfaceStorage =
|
|
cast<SwiftInterfaceModuleDependenciesStorage>(storage.get());
|
|
assert(!swiftInterfaceStorage->textualModuleDetails.bridgingHeaderFile);
|
|
swiftInterfaceStorage->textualModuleDetails.bridgingHeaderFile = bridgingHeader.str();
|
|
break;
|
|
}
|
|
case swift::ModuleDependencyKind::SwiftSource: {
|
|
auto swiftSourceStorage =
|
|
cast<SwiftSourceModuleDependenciesStorage>(storage.get());
|
|
assert(!swiftSourceStorage->textualModuleDetails.bridgingHeaderFile);
|
|
swiftSourceStorage->textualModuleDetails.bridgingHeaderFile = bridgingHeader.str();
|
|
break;
|
|
}
|
|
default:
|
|
llvm_unreachable("Unexpected dependency kind");
|
|
}
|
|
}
|
|
|
|
/// Add source files that the bridging header depends on.
|
|
void ModuleDependencyInfo::setHeaderSourceFiles(
|
|
const std::vector<std::string> &files) {
|
|
switch (getKind()) {
|
|
case swift::ModuleDependencyKind::SwiftInterface: {
|
|
auto swiftInterfaceStorage =
|
|
cast<SwiftInterfaceModuleDependenciesStorage>(storage.get());
|
|
swiftInterfaceStorage->textualModuleDetails.bridgingSourceFiles = files;
|
|
break;
|
|
}
|
|
case swift::ModuleDependencyKind::SwiftSource: {
|
|
auto swiftSourceStorage =
|
|
cast<SwiftSourceModuleDependenciesStorage>(storage.get());
|
|
swiftSourceStorage->textualModuleDetails.bridgingSourceFiles = files;
|
|
break;
|
|
}
|
|
case swift::ModuleDependencyKind::SwiftBinary: {
|
|
auto swiftBinaryStorage =
|
|
cast<SwiftBinaryModuleDependencyStorage>(storage.get());
|
|
swiftBinaryStorage->headerSourceFiles = files;
|
|
break;
|
|
}
|
|
default:
|
|
llvm_unreachable("Unexpected dependency kind");
|
|
}
|
|
}
|
|
|
|
void ModuleDependencyInfo::addBridgingHeaderIncludeTree(StringRef ID) {
|
|
switch (getKind()) {
|
|
case swift::ModuleDependencyKind::SwiftInterface: {
|
|
auto swiftInterfaceStorage =
|
|
cast<SwiftInterfaceModuleDependenciesStorage>(storage.get());
|
|
swiftInterfaceStorage->textualModuleDetails
|
|
.CASBridgingHeaderIncludeTreeRootID = ID.str();
|
|
break;
|
|
}
|
|
case swift::ModuleDependencyKind::SwiftSource: {
|
|
auto swiftSourceStorage =
|
|
cast<SwiftSourceModuleDependenciesStorage>(storage.get());
|
|
swiftSourceStorage->textualModuleDetails
|
|
.CASBridgingHeaderIncludeTreeRootID = ID.str();
|
|
break;
|
|
}
|
|
default:
|
|
llvm_unreachable("Unexpected dependency kind");
|
|
}
|
|
}
|
|
|
|
void ModuleDependencyInfo::setChainedBridgingHeaderBuffer(StringRef path,
|
|
StringRef buffer) {
|
|
switch (getKind()) {
|
|
case swift::ModuleDependencyKind::SwiftSource: {
|
|
auto swiftSourceStorage =
|
|
cast<SwiftSourceModuleDependenciesStorage>(storage.get());
|
|
swiftSourceStorage->setChainedBridgingHeaderBuffer(path, buffer);
|
|
break;
|
|
}
|
|
default:
|
|
llvm_unreachable("Unexpected dependency kind");
|
|
}
|
|
}
|
|
|
|
void ModuleDependencyInfo::addSourceFile(StringRef sourceFile) {
|
|
switch (getKind()) {
|
|
case swift::ModuleDependencyKind::SwiftSource: {
|
|
auto swiftSourceStorage =
|
|
cast<SwiftSourceModuleDependenciesStorage>(storage.get());
|
|
swiftSourceStorage->sourceFiles.push_back(sourceFile.str());
|
|
break;
|
|
}
|
|
default:
|
|
llvm_unreachable("Unexpected dependency kind");
|
|
}
|
|
}
|
|
|
|
void ModuleDependencyInfo::setOutputPathAndHash(StringRef outputPath,
|
|
StringRef hash) {
|
|
switch (getKind()) {
|
|
case swift::ModuleDependencyKind::SwiftInterface: {
|
|
auto swiftInterfaceStorage =
|
|
cast<SwiftInterfaceModuleDependenciesStorage>(storage.get());
|
|
swiftInterfaceStorage->moduleOutputPath = outputPath.str();
|
|
swiftInterfaceStorage->contextHash = hash.str();
|
|
break;
|
|
}
|
|
default:
|
|
llvm_unreachable("Unexpected dependency kind");
|
|
}
|
|
}
|
|
|
|
SwiftDependencyScanningService::SwiftDependencyScanningService() {
|
|
ClangScanningService.emplace(
|
|
clang::tooling::dependencies::ScanningMode::DependencyDirectivesScan,
|
|
clang::tooling::dependencies::ScanningOutputFormat::FullTree,
|
|
clang::CASOptions(),
|
|
/* CAS (llvm::cas::ObjectStore) */ nullptr,
|
|
/* Cache (llvm::cas::ActionCache) */ nullptr,
|
|
/* SharedFS */ nullptr,
|
|
// ScanningOptimizations::Default excludes the current working
|
|
// directory optimization. Clang needs to communicate with
|
|
// the build system to handle the optimization safely.
|
|
// Swift can handle the working directory optimizaiton
|
|
// already so it is safe to turn on all optimizations.
|
|
clang::tooling::dependencies::ScanningOptimizations::All);
|
|
}
|
|
|
|
bool
|
|
swift::dependencies::checkImportNotTautological(const ImportPath::Module modulePath,
|
|
const SourceLoc importLoc,
|
|
const SourceFile &SF,
|
|
bool isExported) {
|
|
if (modulePath.front().Item != SF.getParentModule()->getName() ||
|
|
// Overlays use an @_exported self-import to load their clang module.
|
|
isExported ||
|
|
// Imports of your own submodules are allowed in cross-language libraries.
|
|
modulePath.size() != 1 ||
|
|
// SIL files self-import to get decls from the rest of the module.
|
|
SF.Kind == SourceFileKind::SIL)
|
|
return true;
|
|
|
|
ASTContext &ctx = SF.getASTContext();
|
|
|
|
StringRef filename = llvm::sys::path::filename(SF.getFilename());
|
|
if (filename.empty())
|
|
ctx.Diags.diagnose(importLoc, diag::sema_import_current_module,
|
|
modulePath.front().Item);
|
|
else
|
|
ctx.Diags.diagnose(importLoc, diag::sema_import_current_module_with_file,
|
|
filename, modulePath.front().Item);
|
|
|
|
return false;
|
|
}
|
|
|
|
void swift::dependencies::registerCxxInteropLibraries(
|
|
const llvm::Triple &Target, StringRef mainModuleName, bool hasStaticCxx,
|
|
bool hasStaticCxxStdlib, CXXStdlibKind cxxStdlibKind,
|
|
std::function<void(const LinkLibrary &)> RegistrationCallback) {
|
|
|
|
switch (cxxStdlibKind) {
|
|
case CXXStdlibKind::Libcxx:
|
|
RegistrationCallback(
|
|
LinkLibrary{"c++", LibraryKind::Library, /*static=*/false});
|
|
break;
|
|
case CXXStdlibKind::Libstdcxx:
|
|
RegistrationCallback(
|
|
LinkLibrary{"stdc++", LibraryKind::Library, /*static=*/false});
|
|
break;
|
|
case CXXStdlibKind::Msvcprt:
|
|
// FIXME: should we be explicitly linking in msvcprt or will the module do
|
|
// so?
|
|
break;
|
|
case CXXStdlibKind::Unknown:
|
|
// FIXME: we should probably emit a warning or a note here.
|
|
break;
|
|
}
|
|
|
|
// Do not try to link Cxx with itself.
|
|
if (mainModuleName != CXX_MODULE_NAME)
|
|
RegistrationCallback(
|
|
LinkLibrary{"swiftCxx", LibraryKind::Library, hasStaticCxx});
|
|
|
|
// Do not try to link CxxStdlib with the C++ standard library, Cxx or
|
|
// itself.
|
|
if (llvm::none_of(llvm::ArrayRef<StringRef>{CXX_MODULE_NAME, "CxxStdlib", "std"},
|
|
[mainModuleName](StringRef Name) {
|
|
return mainModuleName == Name;
|
|
})) {
|
|
// Only link with CxxStdlib on platforms where the overlay is available.
|
|
if (Target.isOSDarwin() || Target.isOSLinux() || Target.isOSWindows() ||
|
|
Target.isOSFreeBSD())
|
|
RegistrationCallback(LinkLibrary{"swiftCxxStdlib", LibraryKind::Library,
|
|
hasStaticCxxStdlib});
|
|
}
|
|
}
|
|
|
|
void
|
|
swift::dependencies::registerBackDeployLibraries(
|
|
const IRGenOptions &IRGenOpts,
|
|
std::function<void(const LinkLibrary&)> RegistrationCallback) {
|
|
auto addBackDeployLib = [&](llvm::VersionTuple version,
|
|
StringRef libraryName, bool forceLoad) {
|
|
std::optional<llvm::VersionTuple> compatibilityVersion;
|
|
if (libraryName == "swiftCompatibilityDynamicReplacements") {
|
|
compatibilityVersion = IRGenOpts.
|
|
AutolinkRuntimeCompatibilityDynamicReplacementLibraryVersion;
|
|
} else if (libraryName == "swiftCompatibilityConcurrency") {
|
|
compatibilityVersion =
|
|
IRGenOpts.AutolinkRuntimeCompatibilityConcurrencyLibraryVersion;
|
|
} else {
|
|
compatibilityVersion = IRGenOpts.
|
|
AutolinkRuntimeCompatibilityLibraryVersion;
|
|
}
|
|
|
|
if (!compatibilityVersion)
|
|
return;
|
|
|
|
if (*compatibilityVersion > version)
|
|
return;
|
|
|
|
RegistrationCallback(
|
|
{libraryName, LibraryKind::Library, /*static=*/true, forceLoad});
|
|
};
|
|
|
|
#define BACK_DEPLOYMENT_LIB(Version, Filter, LibraryName, ForceLoad) \
|
|
addBackDeployLib(llvm::VersionTuple Version, LibraryName, ForceLoad);
|
|
#include "swift/Frontend/BackDeploymentLibs.def"
|
|
}
|
|
|
|
bool SwiftDependencyScanningService::setupCachingDependencyScanningService(
|
|
CompilerInstance &Instance) {
|
|
if (!Instance.getInvocation().getCASOptions().EnableCaching)
|
|
return false;
|
|
|
|
if (CASOpts) {
|
|
// If CASOption matches, the service is initialized already.
|
|
if (*CASOpts == Instance.getInvocation().getCASOptions().CASOpts)
|
|
return false;
|
|
|
|
// CASOption mismatch, return error.
|
|
Instance.getDiags().diagnose(SourceLoc(), diag::error_cas_conflict_options);
|
|
return true;
|
|
}
|
|
|
|
// Setup CAS.
|
|
CASOpts = Instance.getInvocation().getCASOptions().CASOpts;
|
|
|
|
ClangScanningService.emplace(
|
|
clang::tooling::dependencies::ScanningMode::DependencyDirectivesScan,
|
|
clang::tooling::dependencies::ScanningOutputFormat::FullIncludeTree,
|
|
Instance.getInvocation().getCASOptions().CASOpts,
|
|
Instance.getSharedCASInstance(), Instance.getSharedCacheInstance(),
|
|
/*CachingOnDiskFileSystem=*/nullptr,
|
|
// The current working directory optimization (off by default)
|
|
// should not impact CAS. We set the optization to all to be
|
|
// consistent with the non-CAS case.
|
|
clang::tooling::dependencies::ScanningOptimizations::All);
|
|
|
|
return false;
|
|
}
|
|
|
|
ModuleDependenciesCache::ModuleDependenciesCache(
|
|
const std::string &mainScanModuleName, const std::string &scannerContextHash)
|
|
: mainScanModuleName(mainScanModuleName),
|
|
scannerContextHash(scannerContextHash),
|
|
scanInitializationTime(std::chrono::system_clock::now()) {
|
|
for (auto kind = ModuleDependencyKind::FirstKind;
|
|
kind != ModuleDependencyKind::LastKind; ++kind)
|
|
ModuleDependenciesMap.insert({kind, ModuleNameToDependencyMap()});
|
|
}
|
|
|
|
const ModuleNameToDependencyMap &
|
|
ModuleDependenciesCache::getDependenciesMap(
|
|
ModuleDependencyKind kind) const {
|
|
auto it = ModuleDependenciesMap.find(kind);
|
|
assert(it != ModuleDependenciesMap.end() &&
|
|
"invalid dependency kind");
|
|
return it->second;
|
|
}
|
|
|
|
ModuleNameToDependencyMap &
|
|
ModuleDependenciesCache::getDependenciesMap(
|
|
ModuleDependencyKind kind) {
|
|
auto it = ModuleDependenciesMap.find(kind);
|
|
assert(it != ModuleDependenciesMap.end() &&
|
|
"invalid dependency kind");
|
|
return it->second;
|
|
}
|
|
|
|
std::optional<const ModuleDependencyInfo *>
|
|
ModuleDependenciesCache::findDependency(
|
|
const ModuleDependencyID moduleID) const {
|
|
return findDependency(moduleID.ModuleName, moduleID.Kind);
|
|
}
|
|
|
|
std::optional<const ModuleDependencyInfo *>
|
|
ModuleDependenciesCache::findDependency(
|
|
StringRef moduleName, std::optional<ModuleDependencyKind> kind) const {
|
|
if (!kind) {
|
|
for (auto kind = ModuleDependencyKind::FirstKind;
|
|
kind != ModuleDependencyKind::LastKind; ++kind) {
|
|
auto dep = findDependency(moduleName, kind);
|
|
if (dep.has_value())
|
|
return dep.value();
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
assert(kind.has_value() && "Expected dependencies kind for lookup.");
|
|
std::optional<const ModuleDependencyInfo *> optionalDep = std::nullopt;
|
|
const auto &map = getDependenciesMap(kind.value());
|
|
auto known = map.find(moduleName);
|
|
if (known != map.end())
|
|
optionalDep = &(known->second);
|
|
|
|
// During a scan, only produce the cached source module info for the current
|
|
// module under scan.
|
|
if (optionalDep.has_value()) {
|
|
auto dep = optionalDep.value();
|
|
if (dep->getAsSwiftSourceModule() &&
|
|
moduleName != mainScanModuleName &&
|
|
moduleName != "MainModuleCrossImportOverlays") {
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
return optionalDep;
|
|
}
|
|
|
|
std::optional<const ModuleDependencyInfo *>
|
|
ModuleDependenciesCache::findSwiftDependency(StringRef moduleName) const {
|
|
if (auto found = findDependency(moduleName, ModuleDependencyKind::SwiftInterface))
|
|
return found;
|
|
if (auto found = findDependency(moduleName, ModuleDependencyKind::SwiftBinary))
|
|
return found;
|
|
if (auto found = findDependency(moduleName, ModuleDependencyKind::SwiftSource))
|
|
return found;
|
|
return std::nullopt;
|
|
}
|
|
|
|
const ModuleDependencyInfo &ModuleDependenciesCache::findKnownDependency(
|
|
const ModuleDependencyID &moduleID) const {
|
|
|
|
auto dep = findDependency(moduleID);
|
|
assert(dep && "dependency unknown");
|
|
return **dep;
|
|
}
|
|
|
|
bool ModuleDependenciesCache::hasDependency(const ModuleDependencyID &moduleID) const {
|
|
return hasDependency(moduleID.ModuleName, moduleID.Kind);
|
|
}
|
|
|
|
bool ModuleDependenciesCache::hasDependency(
|
|
StringRef moduleName, std::optional<ModuleDependencyKind> kind) const {
|
|
return findDependency(moduleName, kind).has_value();
|
|
}
|
|
|
|
bool ModuleDependenciesCache::hasDependency(StringRef moduleName) const {
|
|
for (auto kind = ModuleDependencyKind::FirstKind;
|
|
kind != ModuleDependencyKind::LastKind; ++kind)
|
|
if (findDependency(moduleName, kind).has_value())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool ModuleDependenciesCache::hasSwiftDependency(StringRef moduleName) const {
|
|
return findSwiftDependency(moduleName).has_value();
|
|
}
|
|
|
|
void ModuleDependenciesCache::recordDependency(
|
|
StringRef moduleName, ModuleDependencyInfo dependency) {
|
|
auto dependenciesKind = dependency.getKind();
|
|
auto &map = getDependenciesMap(dependenciesKind);
|
|
map.insert({moduleName, dependency});
|
|
}
|
|
|
|
void ModuleDependenciesCache::recordClangDependencies(
|
|
const clang::tooling::dependencies::ModuleDepsGraph &dependencies,
|
|
DiagnosticEngine &diags,
|
|
BridgeClangDependencyCallback bridgeClangModule) {
|
|
for (const auto &dep : dependencies)
|
|
recordClangDependency(dep, diags, bridgeClangModule);
|
|
}
|
|
|
|
void ModuleDependenciesCache::recordClangDependency(
|
|
const clang::tooling::dependencies::ModuleDeps &dependency,
|
|
DiagnosticEngine &diags,
|
|
BridgeClangDependencyCallback bridgeClangModule) {
|
|
auto depID =
|
|
ModuleDependencyID{dependency.ID.ModuleName, ModuleDependencyKind::Clang};
|
|
if (!hasDependency(depID)) {
|
|
recordDependency(dependency.ID.ModuleName, bridgeClangModule(dependency));
|
|
addSeenClangModule(dependency.ID);
|
|
return;
|
|
}
|
|
|
|
auto priorClangModuleDetails =
|
|
findKnownDependency(depID).getAsClangModule();
|
|
DEBUG_ASSERT(priorClangModuleDetails);
|
|
auto priorContextHash = priorClangModuleDetails->contextHash;
|
|
auto newContextHash = dependency.ID.ContextHash;
|
|
if (priorContextHash != newContextHash) {
|
|
// This situation means that within the same scanning action, Clang
|
|
// Dependency Scanner has produced two different variants of the same
|
|
// module. This is not supposed to happen, but we are currently
|
|
// hunting down the rare cases where it does, seemingly due to
|
|
// differences in Clang Scanner direct by-name queries and transitive
|
|
// header lookup queries.
|
|
//
|
|
// Emit a failure diagnostic here that is hopefully more actionable
|
|
// for the time being.
|
|
diags.diagnose(SourceLoc(),
|
|
diag::dependency_scan_unexpected_variant,
|
|
dependency.ID.ModuleName);
|
|
diags.diagnose(
|
|
SourceLoc(),
|
|
diag::dependency_scan_unexpected_variant_context_hash_note,
|
|
priorContextHash, newContextHash);
|
|
diags.diagnose(
|
|
SourceLoc(),
|
|
diag::dependency_scan_unexpected_variant_module_map_note,
|
|
priorClangModuleDetails->moduleMapFile, dependency.ClangModuleMapFile);
|
|
|
|
auto newClangModuleDetails = bridgeClangModule(dependency).getAsClangModule();
|
|
auto diagnoseExtraCommandLineFlags =
|
|
[&diags](const ClangModuleDependencyStorage *checkModuleDetails,
|
|
const ClangModuleDependencyStorage *baseModuleDetails,
|
|
bool isNewlyDiscovered) -> void {
|
|
std::unordered_set<std::string> baseCommandLineSet(
|
|
baseModuleDetails->buildCommandLine.begin(),
|
|
baseModuleDetails->buildCommandLine.end());
|
|
for (const auto &checkArg : checkModuleDetails->buildCommandLine)
|
|
if (baseCommandLineSet.find(checkArg) == baseCommandLineSet.end())
|
|
diags.diagnose(
|
|
SourceLoc(),
|
|
diag::dependency_scan_unexpected_variant_extra_arg_note,
|
|
isNewlyDiscovered, checkArg);
|
|
};
|
|
diagnoseExtraCommandLineFlags(priorClangModuleDetails,
|
|
newClangModuleDetails, true);
|
|
diagnoseExtraCommandLineFlags(newClangModuleDetails,
|
|
priorClangModuleDetails, false);
|
|
}
|
|
}
|
|
|
|
void ModuleDependenciesCache::updateDependency(
|
|
ModuleDependencyID moduleID, ModuleDependencyInfo dependencyInfo) {
|
|
auto &map = getDependenciesMap(moduleID.Kind);
|
|
assert(map.find(moduleID.ModuleName) != map.end() && "Not yet added to map");
|
|
map.insert_or_assign(moduleID.ModuleName, std::move(dependencyInfo));
|
|
}
|
|
|
|
void ModuleDependenciesCache::removeDependency(ModuleDependencyID moduleID) {
|
|
auto &map = getDependenciesMap(moduleID.Kind);
|
|
map.erase(moduleID.ModuleName);
|
|
}
|
|
|
|
void ModuleDependenciesCache::setImportedSwiftDependencies(
|
|
ModuleDependencyID moduleID,
|
|
const ArrayRef<ModuleDependencyID> dependencyIDs) {
|
|
auto dependencyInfo = findKnownDependency(moduleID);
|
|
assert(dependencyInfo.getImportedSwiftDependencies().empty());
|
|
#ifndef NDEBUG
|
|
for (const auto &depID : dependencyIDs)
|
|
assert(depID.Kind != ModuleDependencyKind::Clang);
|
|
#endif
|
|
// Copy the existing info to a mutable one we can then replace it with, after
|
|
// setting its overlay dependencies.
|
|
auto updatedDependencyInfo = dependencyInfo;
|
|
updatedDependencyInfo.setImportedSwiftDependencies(dependencyIDs);
|
|
updateDependency(moduleID, updatedDependencyInfo);
|
|
}
|
|
void ModuleDependenciesCache::setImportedClangDependencies(
|
|
ModuleDependencyID moduleID,
|
|
const ArrayRef<ModuleDependencyID> dependencyIDs) {
|
|
auto dependencyInfo = findKnownDependency(moduleID);
|
|
assert(dependencyInfo.getImportedClangDependencies().empty());
|
|
#ifndef NDEBUG
|
|
for (const auto &depID : dependencyIDs)
|
|
assert(depID.Kind == ModuleDependencyKind::Clang);
|
|
#endif
|
|
// Copy the existing info to a mutable one we can then replace it with, after
|
|
// setting its overlay dependencies.
|
|
auto updatedDependencyInfo = dependencyInfo;
|
|
updatedDependencyInfo.setImportedClangDependencies(dependencyIDs);
|
|
updateDependency(moduleID, updatedDependencyInfo);
|
|
}
|
|
void ModuleDependenciesCache::setHeaderClangDependencies(
|
|
ModuleDependencyID moduleID,
|
|
const ArrayRef<ModuleDependencyID> dependencyIDs) {
|
|
auto dependencyInfo = findKnownDependency(moduleID);
|
|
#ifndef NDEBUG
|
|
for (const auto &depID : dependencyIDs)
|
|
assert(depID.Kind == ModuleDependencyKind::Clang);
|
|
#endif
|
|
// Copy the existing info to a mutable one we can then replace it with, after
|
|
// setting its overlay dependencies.
|
|
auto updatedDependencyInfo = dependencyInfo;
|
|
updatedDependencyInfo.setHeaderClangDependencies(dependencyIDs);
|
|
updateDependency(moduleID, updatedDependencyInfo);
|
|
}
|
|
void ModuleDependenciesCache::setSwiftOverlayDependencies(
|
|
ModuleDependencyID moduleID,
|
|
const ArrayRef<ModuleDependencyID> dependencyIDs) {
|
|
auto dependencyInfo = findKnownDependency(moduleID);
|
|
assert(dependencyInfo.getSwiftOverlayDependencies().empty());
|
|
#ifndef NDEBUG
|
|
for (const auto &depID : dependencyIDs)
|
|
assert(depID.Kind != ModuleDependencyKind::Clang);
|
|
#endif
|
|
// Copy the existing info to a mutable one we can then replace it with, after
|
|
// setting its overlay dependencies.
|
|
auto updatedDependencyInfo = dependencyInfo;
|
|
updatedDependencyInfo.setSwiftOverlayDependencies(dependencyIDs);
|
|
updateDependency(moduleID, updatedDependencyInfo);
|
|
}
|
|
void ModuleDependenciesCache::setCrossImportOverlayDependencies(
|
|
ModuleDependencyID moduleID,
|
|
const ModuleDependencyIDCollectionView dependencyIDs) {
|
|
auto dependencyInfo = findKnownDependency(moduleID);
|
|
assert(dependencyInfo.getCrossImportOverlayDependencies().empty());
|
|
// Copy the existing info to a mutable one we can then replace it with,
|
|
// after setting its overlay dependencies.
|
|
auto updatedDependencyInfo = dependencyInfo;
|
|
updatedDependencyInfo.setCrossImportOverlayDependencies(dependencyIDs);
|
|
updateDependency(moduleID, updatedDependencyInfo);
|
|
}
|
|
|
|
ModuleDependencyIDCollectionView ModuleDependenciesCache::getAllDependencies(
|
|
const ModuleDependencyID &moduleID) const {
|
|
const auto &moduleInfo = findKnownDependency(moduleID);
|
|
return ModuleDependencyIDCollectionView(
|
|
moduleInfo.getImportedSwiftDependencies(),
|
|
moduleInfo.getHeaderClangDependencies(),
|
|
moduleInfo.getSwiftOverlayDependencies(),
|
|
moduleInfo.getCrossImportOverlayDependencies(),
|
|
moduleInfo.getImportedClangDependencies());
|
|
}
|
|
|
|
void ModuleDependenciesCache::addVisibleClangModules(
|
|
ModuleDependencyID moduleID, const std::vector<std::string> &moduleNames) {
|
|
if (moduleNames.empty())
|
|
return;
|
|
auto dependencyInfo = findKnownDependency(moduleID);
|
|
auto updatedDependencyInfo = dependencyInfo;
|
|
updatedDependencyInfo.addVisibleClangModules(moduleNames);
|
|
updateDependency(moduleID, updatedDependencyInfo);
|
|
}
|
|
|
|
llvm::StringSet<> &ModuleDependenciesCache::getVisibleClangModules(
|
|
ModuleDependencyID moduleID) const {
|
|
ASSERT(moduleID.Kind == ModuleDependencyKind::SwiftSource ||
|
|
moduleID.Kind == ModuleDependencyKind::SwiftInterface ||
|
|
moduleID.Kind == ModuleDependencyKind::SwiftBinary);
|
|
return findKnownDependency(moduleID).getVisibleClangModules();
|
|
}
|
|
|
|
ModuleDependencyIDCollectionView
|
|
ModuleDependenciesCache::getDirectImportedDependencies(
|
|
const ModuleDependencyID &moduleID) const {
|
|
const auto &moduleInfo = findKnownDependency(moduleID);
|
|
return ModuleDependencyIDCollectionView(
|
|
moduleInfo.getImportedSwiftDependencies(),
|
|
moduleInfo.getImportedClangDependencies());
|
|
}
|
|
|
|
ModuleDependencyIDCollectionView
|
|
ModuleDependenciesCache::getAllClangDependencies(
|
|
const ModuleDependencyID &moduleID) const {
|
|
const auto &moduleInfo = findKnownDependency(moduleID);
|
|
return ModuleDependencyIDCollectionView(
|
|
moduleInfo.getImportedClangDependencies(),
|
|
moduleInfo.getHeaderClangDependencies());
|
|
}
|
|
|
|
llvm::ArrayRef<ModuleDependencyID>
|
|
ModuleDependenciesCache::getImportedSwiftDependencies(
|
|
const ModuleDependencyID &moduleID) const {
|
|
const auto &moduleInfo = findKnownDependency(moduleID);
|
|
assert(moduleInfo.isSwiftModule());
|
|
return moduleInfo.getImportedSwiftDependencies();
|
|
}
|
|
|
|
llvm::ArrayRef<ModuleDependencyID>
|
|
ModuleDependenciesCache::getImportedClangDependencies(
|
|
const ModuleDependencyID &moduleID) const {
|
|
const auto &moduleInfo = findKnownDependency(moduleID);
|
|
return moduleInfo.getImportedClangDependencies();
|
|
}
|
|
|
|
llvm::ArrayRef<ModuleDependencyID>
|
|
ModuleDependenciesCache::getHeaderClangDependencies(
|
|
const ModuleDependencyID &moduleID) const {
|
|
const auto &moduleInfo = findKnownDependency(moduleID);
|
|
assert(moduleInfo.isSwiftModule());
|
|
return moduleInfo.getHeaderClangDependencies();
|
|
}
|
|
|
|
llvm::ArrayRef<ModuleDependencyID>
|
|
ModuleDependenciesCache::getSwiftOverlayDependencies(
|
|
const ModuleDependencyID &moduleID) const {
|
|
const auto &moduleInfo = findKnownDependency(moduleID);
|
|
assert(moduleInfo.isSwiftModule());
|
|
return moduleInfo.getSwiftOverlayDependencies();
|
|
}
|
|
|
|
llvm::ArrayRef<ModuleDependencyID>
|
|
ModuleDependenciesCache::getCrossImportOverlayDependencies(
|
|
const ModuleDependencyID &moduleID) const {
|
|
const auto &moduleInfo = findKnownDependency(moduleID);
|
|
assert(moduleInfo.isSwiftSourceModule());
|
|
return moduleInfo.getCrossImportOverlayDependencies();
|
|
}
|