Files
swift-mirror/lib/DependencyScan/ScanDependencies.cpp
Artem Chikin 082fc48b25 [Dependency Scanning] Refactor dependency scanner to return an in-memory format as a result
This commit refactors ScanDependencies.cpp to split the functionality into two functional groups:
- Scan execution code that performs the mechanics of the scan and produces an in-memory result
- Dependency scanner entry-points used when the scanning action is invoked as a `swift-frontend` execution mode
This commit also adds the aforementioned in-memory dependency scanning result in `FullDependencies.h`, modeled after the InterModuleDependencyGraph representation in swift-driver
2021-01-07 09:08:20 -08:00

1196 lines
46 KiB
C++

//===--- ScanDependencies.cpp -- Scans the dependencies of a module -------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 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/DependencyScan/ScanDependencies.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/AST/Module.h"
#include "swift/AST/ModuleDependencies.h"
#include "swift/AST/ModuleLoader.h"
#include "swift/AST/SourceFile.h"
#include "swift/Basic/Defer.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/STLExtras.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "swift/DependencyScan/FullDependencies.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/FrontendOptions.h"
#include "swift/Frontend/ModuleInterfaceLoader.h"
#include "swift/Strings.h"
#include "clang/Basic/Module.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"
#include <set>
using namespace swift;
using namespace swift::dependencies;
using namespace llvm::yaml;
namespace {
static std::string getScalaNodeText(Node *N) {
SmallString<32> Buffer;
return cast<ScalarNode>(N)->getValue(Buffer).str();
}
/// Parse an entry like this, where the "platforms" key-value pair is optional:
/// {
/// "swiftModuleName": "Foo",
/// "arguments": "-target 10.15",
/// "output": "../Foo.json"
/// },
static bool parseBatchInputEntries(ASTContext &Ctx, llvm::StringSaver &saver,
Node *Node,
std::vector<BatchScanInput> &result) {
auto *SN = cast<SequenceNode>(Node);
if (!SN)
return true;
for (auto It = SN->begin(); It != SN->end(); ++It) {
auto *MN = cast<MappingNode>(&*It);
BatchScanInput entry;
Optional<std::set<int8_t>> Platforms;
for (auto &Pair : *MN) {
auto Key = getScalaNodeText(Pair.getKey());
auto *Value = Pair.getValue();
if (Key == "clangModuleName") {
entry.moduleName = saver.save(getScalaNodeText(Value));
entry.isSwift = false;
} else if (Key == "swiftModuleName") {
entry.moduleName = saver.save(getScalaNodeText(Value));
entry.isSwift = true;
} else if (Key == "arguments") {
entry.arguments = saver.save(getScalaNodeText(Value));
} else if (Key == "output") {
entry.outputPath = saver.save(getScalaNodeText(Value));
} else {
// Future proof.
continue;
}
}
if (entry.moduleName.empty())
return true;
if (entry.outputPath.empty())
return true;
result.emplace_back(std::move(entry));
}
return false;
}
static Optional<std::vector<BatchScanInput>>
parseBatchScanInputFile(ASTContext &ctx, StringRef batchInputPath,
llvm::StringSaver &saver) {
assert(!batchInputPath.empty());
namespace yaml = llvm::yaml;
std::vector<BatchScanInput> result;
// Load the input file.
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
llvm::MemoryBuffer::getFile(batchInputPath);
if (!FileBufOrErr) {
ctx.Diags.diagnose(SourceLoc(), diag::batch_scan_input_file_missing,
batchInputPath);
return None;
}
StringRef Buffer = FileBufOrErr->get()->getBuffer();
// Use a new source manager instead of the one from ASTContext because we
// don't want the Json file to be persistent.
SourceManager SM;
yaml::Stream Stream(llvm::MemoryBufferRef(Buffer, batchInputPath),
SM.getLLVMSourceMgr());
for (auto DI = Stream.begin(); DI != Stream.end(); ++DI) {
assert(DI != Stream.end() && "Failed to read a document");
yaml::Node *N = DI->getRoot();
assert(N && "Failed to find a root");
if (parseBatchInputEntries(ctx, saver, N, result)) {
ctx.Diags.diagnose(SourceLoc(), diag::batch_scan_input_file_corrupted,
batchInputPath);
return None;
}
}
return result;
}
/// Find all of the imported Clang modules starting with the given module name.
static void findAllImportedClangModules(ASTContext &ctx, StringRef moduleName,
ModuleDependenciesCache &cache,
std::vector<std::string> &allModules,
llvm::StringSet<> &knownModules) {
if (!knownModules.insert(moduleName).second)
return;
allModules.push_back(moduleName.str());
auto dependencies =
cache.findDependencies(moduleName, ModuleDependenciesKind::Clang);
if (!dependencies)
return;
for (const auto &dep : dependencies->getModuleDependencies()) {
findAllImportedClangModules(ctx, dep, cache, allModules, knownModules);
}
}
/// Resolve the direct dependencies of the given module.
static std::vector<ModuleDependencyID>
resolveDirectDependencies(CompilerInstance &instance, ModuleDependencyID module,
ModuleDependenciesCache &cache,
InterfaceSubContextDelegate &ASTDelegate) {
auto &ctx = instance.getASTContext();
auto knownDependencies = *cache.findDependencies(module.first, module.second);
auto isSwift = knownDependencies.isSwiftTextualModule();
// Find the dependencies of every module this module directly depends on.
std::set<ModuleDependencyID> result;
for (auto dependsOn : knownDependencies.getModuleDependencies()) {
// Figure out what kind of module we need.
bool onlyClangModule = !isSwift || module.first == dependsOn;
// Retrieve the dependencies for this module.
if (auto found = ctx.getModuleDependencies(dependsOn, onlyClangModule,
cache, ASTDelegate)) {
result.insert({dependsOn, found->getKind()});
}
}
if (isSwift) {
// A record of all of the Clang modules referenced from this Swift module.
std::vector<std::string> allClangModules;
llvm::StringSet<> knownModules;
// If the Swift module has a bridging header, add those dependencies.
if (knownDependencies.getBridgingHeader()) {
auto clangImporter =
static_cast<ClangImporter *>(ctx.getClangModuleLoader());
if (!clangImporter->addBridgingHeaderDependencies(module.first, cache)) {
// Grab the updated module dependencies.
// FIXME: This is such a hack.
knownDependencies =
*cache.findDependencies(module.first, module.second);
// Add the Clang modules referenced from the bridging header to the
// set of Clang modules we know about.
auto swiftDeps = knownDependencies.getAsSwiftTextualModule();
for (const auto &clangDep : swiftDeps->bridgingModuleDependencies) {
findAllImportedClangModules(ctx, clangDep, cache, allClangModules,
knownModules);
}
}
}
// Find all of the Clang modules this Swift module depends on.
for (const auto &dep : result) {
if (dep.second != ModuleDependenciesKind::Clang)
continue;
findAllImportedClangModules(ctx, dep.first, cache, allClangModules,
knownModules);
}
// Look for overlays for each of the Clang modules. The Swift module
// directly depends on these.
for (const auto &clangDep : allClangModules) {
if (auto found = ctx.getModuleDependencies(
clangDep, /*onlyClangModule=*/false, cache, ASTDelegate)) {
// ASTContext::getModuleDependencies returns dependencies for a module
// with a given name. This Clang module may have the same name as the
// Swift module we are resolving, so we need to make sure we don't add a
// dependency from a Swift module to itself.
if ((found->getKind() == ModuleDependenciesKind::SwiftTextual ||
found->getKind() == ModuleDependenciesKind::SwiftBinary ||
found->getKind() == ModuleDependenciesKind::SwiftPlaceholder) &&
clangDep != module.first) {
result.insert({clangDep, found->getKind()});
}
}
}
}
return std::vector<ModuleDependencyID>(result.begin(), result.end());
}
static void discoverCrosssImportOverlayDependencies(
CompilerInstance &instance, StringRef mainModuleName,
ArrayRef<ModuleDependencyID> allDependencies,
ModuleDependenciesCache &cache, InterfaceSubContextDelegate &ASTDelegate,
llvm::function_ref<void(ModuleDependencyID)> action) {
// Modules explicitly imported. Only these can be secondary module.
llvm::SetVector<Identifier> newOverlays;
for (auto dep : allDependencies) {
auto moduleName = dep.first;
auto dependencies = *cache.findDependencies(moduleName, dep.second);
// Collect a map from secondary module name to cross-import overlay names.
auto overlayMap = dependencies.collectCrossImportOverlayNames(
instance.getASTContext(), moduleName);
if (overlayMap.empty())
continue;
std::for_each(allDependencies.begin(), allDependencies.end(),
[&](ModuleDependencyID Id) {
// check if any explicitly imported modules can serve as a
// secondary module, and add the overlay names to the
// dependencies list.
for (auto overlayName : overlayMap[Id.first]) {
if (std::find_if(allDependencies.begin(),
allDependencies.end(),
[&](ModuleDependencyID Id) {
return Id.first == overlayName.str();
}) == allDependencies.end()) {
newOverlays.insert(overlayName);
}
}
});
}
// No new cross-import overlays are found, return.
if (newOverlays.empty())
return;
// Construct a dummy main to resolve the newly discovered cross import
// overlays.
StringRef dummyMainName = "DummyMainModuleForResolvingCrossImportOverlays";
auto dummyMainDependencies = ModuleDependencies::forMainSwiftModule({});
// Update main module's dependencies to include these new overlays.
auto mainDep = *cache.findDependencies(mainModuleName,
ModuleDependenciesKind::SwiftTextual);
std::for_each(newOverlays.begin(), newOverlays.end(),
[&](Identifier modName) {
dummyMainDependencies.addModuleDependency(modName.str());
mainDep.addModuleDependency(modName.str());
});
cache.updateDependencies(
{mainModuleName.str(), ModuleDependenciesKind::SwiftTextual}, mainDep);
// Record the dummy main module's direct dependencies. The dummy main module
// only directly depend on these newly discovered overlay modules.
cache.recordDependencies(dummyMainName, dummyMainDependencies);
llvm::SetVector<ModuleDependencyID, std::vector<ModuleDependencyID>,
std::set<ModuleDependencyID>>
allModules;
// Seed the all module list from the dummpy main module.
allModules.insert({dummyMainName.str(), dummyMainDependencies.getKind()});
// Explore the dependencies of every module.
for (unsigned currentModuleIdx = 0; currentModuleIdx < allModules.size();
++currentModuleIdx) {
auto module = allModules[currentModuleIdx];
auto discoveredModules =
resolveDirectDependencies(instance, module, cache, ASTDelegate);
allModules.insert(discoveredModules.begin(), discoveredModules.end());
}
// Report any discovered modules to the clients, which include all overlays
// and their dependencies.
std::for_each(/* +1 to exclude dummy main*/ allModules.begin() + 1,
allModules.end(), action);
}
/// Write a single JSON field.
template <typename T>
void writeJSONSingleField(llvm::raw_ostream &out, StringRef fieldName,
const T &value, unsigned indentLevel,
bool trailingComma);
/// Write a string value as JSON.
void writeJSONValue(llvm::raw_ostream &out, StringRef value,
unsigned indentLevel) {
out << "\"";
out << value;
out << "\"";
}
/// Write a boolean value as JSON.
void writeJSONValue(llvm::raw_ostream &out, bool value, unsigned indentLevel) {
out.write_escaped(value ? "true" : "false");
}
/// Write a module identifier.
void writeJSONValue(llvm::raw_ostream &out, const ModuleDependencyID &module,
unsigned indentLevel) {
out << "{\n";
std::string moduleKind;
if (module.second == ModuleDependenciesKind::SwiftTextual)
moduleKind = "swift";
else if (module.second == ModuleDependenciesKind::SwiftBinary)
// FIXME: rename to be consistent in the clients (swift-driver)
moduleKind = "swiftPrebuiltExternal";
else if (module.second == ModuleDependenciesKind::SwiftPlaceholder)
moduleKind = "swiftPlaceholder";
else
moduleKind = "clang";
writeJSONSingleField(out, moduleKind, module.first, indentLevel + 1,
/*trailingComma=*/false);
out.indent(indentLevel * 2);
out << "}";
}
/// Write a JSON array.
template <typename T>
void writeJSONValue(llvm::raw_ostream &out, ArrayRef<T> values,
unsigned indentLevel) {
out << "[\n";
for (const auto &value : values) {
out.indent((indentLevel + 1) * 2);
writeJSONValue(out, value, indentLevel + 1);
if (&value != &values.back()) {
out << ",";
}
out << "\n";
}
out.indent(indentLevel * 2);
out << "]";
}
/// Write a JSON array.
template <typename T>
void writeJSONValue(llvm::raw_ostream &out, const std::vector<T> &values,
unsigned indentLevel) {
writeJSONValue(out, llvm::makeArrayRef(values), indentLevel);
}
/// Write a single JSON field.
template <typename T>
void writeJSONSingleField(llvm::raw_ostream &out, StringRef fieldName,
const T &value, unsigned indentLevel,
bool trailingComma) {
out.indent(indentLevel * 2);
writeJSONValue(out, fieldName, indentLevel);
out << ": ";
writeJSONValue(out, value, indentLevel);
if (trailingComma)
out << ",";
out << "\n";
}
static void
writePrescanJSON(llvm::raw_ostream &out,
const std::vector<std::string> &moduleDependencies) {
// Write out a JSON containing all main module imports.
out << "{\n";
SWIFT_DEFER { out << "}\n"; };
writeJSONSingleField(out, "imports", moduleDependencies, 0, false);
}
static void writeJSON(llvm::raw_ostream &out,
const FullDependencies &fullDependencies) {
// Write out a JSON description of all of the dependencies.
out << "{\n";
SWIFT_DEFER { out << "}\n"; };
// Name of the main module.
writeJSONSingleField(out, "mainModuleName", fullDependencies.MainModuleName,
/*indentLevel=*/1, /*trailingComma=*/true);
// Write out all of the modules.
out << " \"modules\": [\n";
SWIFT_DEFER { out << " ]\n"; };
for (auto &moduleInfo : fullDependencies.Modules) {
auto &directDependencies = moduleInfo.DirectDependencies;
// The module we are describing.
out.indent(2 * 2);
writeJSONValue(out, moduleInfo.ID, 2);
out << ",\n";
out.indent(2 * 2);
out << "{\n";
auto swiftPlaceholderDeps =
moduleInfo.Details.getAsPlaceholderDependencyModule();
auto swiftTextualDeps = moduleInfo.Details.getAsSwiftTextualModule();
auto swiftBinaryDeps = moduleInfo.Details.getAsSwiftBinaryModule();
auto clangDeps = moduleInfo.Details.getAsClangModule();
// Module path.
const char *modulePathSuffix = clangDeps ? ".pcm" : ".swiftmodule";
std::string modulePath;
if (swiftPlaceholderDeps)
modulePath = swiftPlaceholderDeps->CompiledModulePath;
else if (swiftBinaryDeps)
modulePath = swiftBinaryDeps->CompiledModulePath;
else
modulePath = moduleInfo.ID.first + modulePathSuffix;
writeJSONSingleField(out, "modulePath", modulePath, /*indentLevel=*/3,
/*trailingComma=*/true);
// Source files.
if (swiftTextualDeps || clangDeps) {
writeJSONSingleField(out, "sourceFiles", moduleInfo.SourceFiles, 3,
/*trailingComma=*/true);
}
// Direct dependencies.
if (swiftTextualDeps || swiftBinaryDeps || clangDeps)
writeJSONSingleField(out, "directDependencies", directDependencies, 3,
/*trailingComma=*/true);
// Swift and Clang-specific details.
out.indent(3 * 2);
out << "\"details\": {\n";
out.indent(4 * 2);
if (swiftTextualDeps) {
out << "\"swift\": {\n";
/// Swift interface file, if there is one. The main module, for
/// example, will not have an interface file.
if (!swiftTextualDeps->ModuleInterfacePath.empty()) {
writeJSONSingleField(out, "moduleInterfacePath",
swiftTextualDeps->ModuleInterfacePath, 5,
/*trailingComma=*/true);
writeJSONSingleField(out, "contextHash",
swiftTextualDeps->ContextHash, 5,
/*trailingComma=*/true);
out.indent(5 * 2);
out << "\"commandLine\": [\n";
for (auto &arg : swiftTextualDeps->CommandLine) {
out.indent(6 * 2);
out << "\"" << arg << "\"";
if (&arg != &swiftTextualDeps->CommandLine.back())
out << ",";
out << "\n";
}
out.indent(5 * 2);
out << "],\n";
out.indent(5 * 2);
out << "\"compiledModuleCandidates\": [\n";
for (auto &candidate : swiftTextualDeps->CompiledModuleCandidates) {
out.indent(6 * 2);
out << "\"" << candidate << "\"";
if (&candidate != &swiftTextualDeps->CompiledModuleCandidates.back())
out << ",";
out << "\n";
}
out.indent(5 * 2);
out << "],\n";
}
writeJSONSingleField(
out, "isFramework", swiftTextualDeps->IsFramework, 5,
/*trailingComma=*/!swiftTextualDeps->ExtraPcmArgs.empty() ||
!(swiftTextualDeps->BridgingHeaderPath.empty()));
if (!swiftTextualDeps->ExtraPcmArgs.empty()) {
out.indent(5 * 2);
out << "\"extraPcmArgs\": [\n";
for (auto &arg : swiftTextualDeps->ExtraPcmArgs) {
out.indent(6 * 2);
out << "\"" << arg << "\"";
if (&arg != &swiftTextualDeps->ExtraPcmArgs.back())
out << ",";
out << "\n";
}
out.indent(5 * 2);
out << (!(swiftTextualDeps->BridgingHeaderPath.empty()) ? "],\n"
: "]\n");
}
/// Bridging header and its source file dependencies, if any.
if (!swiftTextualDeps->BridgingHeaderPath.empty()) {
out.indent(5 * 2);
out << "\"bridgingHeader\": {\n";
writeJSONSingleField(out, "path", swiftTextualDeps->BridgingHeaderPath,
6,
/*trailingComma=*/true);
writeJSONSingleField(out, "sourceFiles",
swiftTextualDeps->BridgingSourceFiles, 6,
/*trailingComma=*/true);
writeJSONSingleField(out, "moduleDependencies",
swiftTextualDeps->BridgingModuleDependencies, 6,
/*trailingComma=*/false);
out.indent(5 * 2);
out << "}\n";
}
} else if (swiftPlaceholderDeps) {
out << "\"swiftPlaceholder\": {\n";
// Module doc file
if (!swiftPlaceholderDeps->ModuleDocPath.empty())
writeJSONSingleField(out, "moduleDocPath",
swiftPlaceholderDeps->ModuleDocPath,
/*indentLevel=*/5,
/*trailingComma=*/true);
// Module Source Info file
if (!swiftPlaceholderDeps->ModuleSourceInfoPath.empty())
writeJSONSingleField(out, "moduleSourceInfoPath",
swiftPlaceholderDeps->ModuleSourceInfoPath,
/*indentLevel=*/5,
/*trailingComma=*/false);
} else if (swiftBinaryDeps) {
out << "\"swiftPrebuiltExternal\": {\n";
assert(!swiftBinaryDeps->CompiledModulePath.empty() &&
"Expected .swiftmodule for a Binary Swift Module Dependency.");
writeJSONSingleField(out, "compiledModulePath",
swiftBinaryDeps->CompiledModulePath,
/*indentLevel=*/5,
/*trailingComma=*/true);
// Module doc file
if (!swiftBinaryDeps->ModuleDocPath.empty())
writeJSONSingleField(out, "moduleDocPath",
swiftBinaryDeps->ModuleDocPath,
/*indentLevel=*/5,
/*trailingComma=*/true);
// Module Source Info file
if (!swiftBinaryDeps->ModuleSourceInfoPath.empty())
writeJSONSingleField(out, "moduleSourceInfoPath",
swiftBinaryDeps->ModuleSourceInfoPath,
/*indentLevel=*/5,
/*trailingComma=*/false);
} else {
out << "\"clang\": {\n";
// Module map file.
writeJSONSingleField(out, "moduleMapPath", clangDeps->ModuleMapPath, 5,
/*trailingComma=*/true);
// Context hash.
writeJSONSingleField(out, "contextHash", clangDeps->ContextHash, 5,
/*trailingComma=*/true);
// Command line.
writeJSONSingleField(out, "commandLine", clangDeps->CommandLine, 5,
/*trailingComma=*/false);
}
out.indent(4 * 2);
out << "}\n";
out.indent(3 * 2);
out << "}\n";
out.indent(2 * 2);
out << "}";
if (&moduleInfo != &fullDependencies.Modules.back())
out << ",";
out << "\n";
}
}
static FullDependencies
generateFullDependencyGraph(CompilerInstance &instance,
ModuleDependenciesCache &cache,
InterfaceSubContextDelegate &ASTDelegate,
ArrayRef<ModuleDependencyID> allModules) {
std::string mainModuleName = allModules.front().first;
FullDependencies DependencyScanResult{mainModuleName};
for (const auto &module : allModules) {
// Grab the completed module dependencies.
auto moduleDeps = *cache.findDependencies(module.first, module.second);
// Collect all the required pieces to build a ModuleInfo
auto swiftPlaceholderDeps = moduleDeps.getAsPlaceholderDependencyModule();
auto swiftTextualDeps = moduleDeps.getAsSwiftTextualModule();
auto swiftBinaryDeps = moduleDeps.getAsSwiftBinaryModule();
auto clangDeps = moduleDeps.getAsClangModule();
// ModulePath
const char *modulePathSuffix =
moduleDeps.isSwiftModule() ? ".swiftmodule" : ".pcm";
std::string modulePath;
if (swiftPlaceholderDeps)
modulePath = swiftPlaceholderDeps->compiledModulePath;
else if (swiftBinaryDeps)
modulePath = swiftBinaryDeps->compiledModulePath;
else
modulePath = module.first + modulePathSuffix;
// SourceFiles
std::vector<std::string> sourceFiles;
if (swiftTextualDeps) {
sourceFiles = swiftTextualDeps->sourceFiles;
} else if (clangDeps) {
sourceFiles = clangDeps->fileDependencies;
}
// DirectDependencies
auto directDependencies = resolveDirectDependencies(
instance, ModuleDependencyID(module.first, module.second), cache,
ASTDelegate);
// Generate a ModuleDetails object based on the dependency kind
auto getModuleDetails = [&]() -> ModuleDetails {
if (swiftTextualDeps) {
SwiftTextualModuleDetails details{
swiftTextualDeps->swiftInterfaceFile.getValueOr(""),
swiftTextualDeps->compiledModuleCandidates,
swiftTextualDeps->bridgingHeaderFile.getValueOr(""),
swiftTextualDeps->bridgingSourceFiles,
swiftTextualDeps->bridgingModuleDependencies,
swiftTextualDeps->buildCommandLine,
swiftTextualDeps->extraPCMArgs,
swiftTextualDeps->contextHash,
swiftTextualDeps->isFramework};
return {details};
} else if (swiftPlaceholderDeps) {
SwiftPlaceholderModuleDetails details{
swiftPlaceholderDeps->compiledModulePath,
swiftPlaceholderDeps->moduleDocPath,
swiftPlaceholderDeps->sourceInfoPath};
return {details};
} else if (swiftBinaryDeps) {
SwiftBinaryModuleDetails details{swiftBinaryDeps->compiledModulePath,
swiftBinaryDeps->moduleDocPath,
swiftBinaryDeps->sourceInfoPath};
return {details};
} else {
ClangModuleDetails details{clangDeps->moduleMapFile,
clangDeps->contextHash,
clangDeps->nonPathCommandLine};
return {details};
}
};
DependencyScanResult.Modules.emplace_back(
ModuleInfo{module, modulePath, sourceFiles, directDependencies,
getModuleDetails()});
}
return DependencyScanResult;
}
static bool diagnoseCycle(CompilerInstance &instance,
ModuleDependenciesCache &cache,
ModuleDependencyID mainId,
InterfaceSubContextDelegate &astDelegate) {
llvm::SetVector<ModuleDependencyID, std::vector<ModuleDependencyID>,
std::set<ModuleDependencyID>>
openSet;
llvm::SetVector<ModuleDependencyID, std::vector<ModuleDependencyID>,
std::set<ModuleDependencyID>>
closeSet;
// Start from the main module.
openSet.insert(mainId);
while (!openSet.empty()) {
auto &lastOpen = openSet.back();
auto beforeSize = openSet.size();
for (auto dep :
resolveDirectDependencies(instance, lastOpen, cache, astDelegate)) {
if (closeSet.count(dep))
continue;
if (openSet.insert(dep)) {
break;
} else {
// Find a cycle, diagnose.
auto startIt = std::find(openSet.begin(), openSet.end(), dep);
assert(startIt != openSet.end());
llvm::SmallString<64> buffer;
for (auto it = startIt; it != openSet.end(); ++it) {
buffer.append(it->first);
buffer.append((it->second == ModuleDependenciesKind::SwiftTextual ||
it->second == ModuleDependenciesKind::SwiftBinary)
? ".swiftmodule"
: ".pcm");
buffer.append(" -> ");
}
buffer.append(startIt->first);
buffer.append(
(startIt->second == ModuleDependenciesKind::SwiftTextual ||
startIt->second == ModuleDependenciesKind::SwiftBinary)
? ".swiftmodule"
: ".pcm");
instance.getASTContext().Diags.diagnose(
SourceLoc(), diag::scanner_find_cycle, buffer.str());
return true;
}
}
// No new node added. We can close this node
if (openSet.size() == beforeSize) {
closeSet.insert(openSet.back());
openSet.pop_back();
} else {
assert(openSet.size() == beforeSize + 1);
}
}
assert(openSet.empty());
return false;
}
using CompilerArgInstanceCacheMap =
llvm::StringMap<std::pair<std::unique_ptr<CompilerInstance>,
std::unique_ptr<ModuleDependenciesCache>>>;
static bool
forEachBatchEntry(CompilerInstance &instance, ModuleDependenciesCache &cache,
llvm::StringSaver &saver,
const std::vector<BatchScanInput> &batchInput,
llvm::function_ref<void(BatchScanInput, CompilerInstance &,
ModuleDependenciesCache &)>
scanningAction) {
const CompilerInvocation &invok = instance.getInvocation();
// Keep track of all compiler instances and dependency caches we have
// created.
// TODO: Re-use a single cache across all invocations, once `alreadySeen`
// state is no longer shared.
CompilerArgInstanceCacheMap subInstanceMap;
auto &diags = instance.getDiags();
ForwardingDiagnosticConsumer FDC(instance.getDiags());
for (auto &entry : batchInput) {
CompilerInstance *pInstance = nullptr;
ModuleDependenciesCache *pCache = nullptr;
if (entry.arguments.empty()) {
// Use the compiler's instance if no arguments are specified.
pInstance = &instance;
pCache = &cache;
} else if (subInstanceMap.count(entry.arguments)) {
// Use the previously created instance if we've seen the arguments
// before.
pInstance = subInstanceMap[entry.arguments].first.get();
pCache = subInstanceMap[entry.arguments].second.get();
} else {
// Create a new instance by the arguments and save it in the map.
subInstanceMap.insert(
{entry.arguments,
std::make_pair(std::make_unique<CompilerInstance>(),
std::make_unique<ModuleDependenciesCache>())});
pInstance = subInstanceMap[entry.arguments].first.get();
pCache = subInstanceMap[entry.arguments].second.get();
SmallVector<const char *, 4> args;
llvm::cl::TokenizeGNUCommandLine(entry.arguments, saver, args);
CompilerInvocation subInvok = invok;
pInstance->addDiagnosticConsumer(&FDC);
if (subInvok.parseArgs(args, diags)) {
instance.getDiags().diagnose(
SourceLoc(), diag::scanner_arguments_invalid, entry.arguments);
return true;
}
if (pInstance->setup(subInvok)) {
instance.getDiags().diagnose(
SourceLoc(), diag::scanner_arguments_invalid, entry.arguments);
return true;
}
}
assert(pInstance);
assert(pCache);
scanningAction(entry, *pInstance, *pCache);
}
return false;
}
static ModuleDependencies
identifyMainModuleDependencies(CompilerInstance &instance,
ModuleDependenciesCache &cache) {
ModuleDecl *mainModule = instance.getMainModule();
// Main module file name.
auto newExt = file_types::getExtension(file_types::TY_SwiftModuleFile);
llvm::SmallString<32> mainModulePath = mainModule->getName().str();
llvm::sys::path::replace_extension(mainModulePath, newExt);
std::string apinotesVer =
(llvm::Twine("-fapinotes-swift-version=") +
instance.getASTContext()
.LangOpts.EffectiveLanguageVersion.asAPINotesVersionString())
.str();
// Compute the dependencies of the main module.
auto mainDependencies = ModuleDependencies::forMainSwiftModule(
{// ExtraPCMArgs
"-Xcc", "-target", "-Xcc",
instance.getASTContext().LangOpts.Target.str(), "-Xcc", apinotesVer});
// Compute Implicit dependencies of the main module
{
llvm::StringSet<> alreadyAddedModules;
for (auto fileUnit : mainModule->getFiles()) {
auto sf = dyn_cast<SourceFile>(fileUnit);
if (!sf)
continue;
mainDependencies.addModuleDependencies(*sf, alreadyAddedModules);
}
const auto &importInfo = mainModule->getImplicitImportInfo();
// Swift standard library.
switch (importInfo.StdlibKind) {
case ImplicitStdlibKind::None:
case ImplicitStdlibKind::Builtin:
break;
case ImplicitStdlibKind::Stdlib:
mainDependencies.addModuleDependency("Swift", &alreadyAddedModules);
break;
}
// Add any implicit module names.
for (const auto &import : importInfo.AdditionalUnloadedImports) {
mainDependencies.addModuleDependency(import.module.getModulePath(),
&alreadyAddedModules);
}
// Already-loaded, implicitly imported module names.
for (const auto &import : importInfo.AdditionalImports) {
mainDependencies.addModuleDependency(
import.module.importedModule->getNameStr(), &alreadyAddedModules);
}
// Add the bridging header.
if (!importInfo.BridgingHeaderPath.empty()) {
mainDependencies.addBridgingHeader(importInfo.BridgingHeaderPath);
}
// If we are to import the underlying Clang module of the same name,
// add a dependency with the same name to trigger the search.
if (importInfo.ShouldImportUnderlyingModule) {
mainDependencies.addModuleDependency(mainModule->getName().str(),
&alreadyAddedModules);
}
}
return mainDependencies;
}
} // namespace
bool swift::dependencies::scanDependencies(CompilerInstance &instance) {
ASTContext &Context = instance.getASTContext();
const FrontendOptions &opts = instance.getInvocation().getFrontendOptions();
std::string path = opts.InputsAndOutputs.getSingleOutputFilename();
std::error_code EC;
llvm::raw_fd_ostream out(path, EC, llvm::sys::fs::F_None);
// `-scan-dependencies` invocations use a single new instance
// of a module cache
ModuleDependenciesCache cache;
if (out.has_error() || EC) {
Context.Diags.diagnose(SourceLoc(), diag::error_opening_output, path,
EC.message());
out.clear_error();
return true;
}
// Execute scan, and write JSON output to the output stream
auto dependenciesOrErr = performModuleScan(instance, cache);
if (dependenciesOrErr.getError())
return true;
auto dependencies = std::move(*dependenciesOrErr);
// Write out the JSON description.
writeJSON(out, dependencies);
// This process succeeds regardless of whether any errors occurred.
// FIXME: We shouldn't need this, but it's masking bugs in our scanning
// logic where we don't create a fresh context when scanning Swift interfaces
// that includes their own command-line flags.
Context.Diags.resetHadAnyError();
return false;
}
bool swift::dependencies::prescanDependencies(CompilerInstance &instance) {
ASTContext &Context = instance.getASTContext();
const FrontendOptions &opts = instance.getInvocation().getFrontendOptions();
std::string path = opts.InputsAndOutputs.getSingleOutputFilename();
std::error_code EC;
llvm::raw_fd_ostream out(path, EC, llvm::sys::fs::F_None);
// `-scan-dependencies` invocations use a single new instance
// of a module cache
ModuleDependenciesCache cache;
if (out.has_error() || EC) {
Context.Diags.diagnose(SourceLoc(), diag::error_opening_output, path,
EC.message());
out.clear_error();
return true;
}
// Execute import prescan, and write JSON output to the output stream
auto mainDependencies = identifyMainModuleDependencies(instance, cache);
// Serialize and output main module dependencies only and exit.
writePrescanJSON(out, mainDependencies.getModuleDependencies());
// This process succeeds regardless of whether any errors occurred.
// FIXME: We shouldn't need this, but it's masking bugs in our scanning
// logic where we don't create a fresh context when scanning Swift interfaces
// that includes their own command-line flags.
Context.Diags.resetHadAnyError();
return false;
}
bool swift::dependencies::batchScanDependencies(
CompilerInstance &instance, llvm::StringRef batchInputFile) {
// The primary cache used for scans carried out with the compiler instance
// we have created
ModuleDependenciesCache cache;
(void)instance.getMainModule();
llvm::BumpPtrAllocator alloc;
llvm::StringSaver saver(alloc);
auto batchInput =
parseBatchScanInputFile(instance.getASTContext(), batchInputFile, saver);
if (!batchInput.hasValue())
return true;
auto batchScanResults =
performBatchModuleScan(instance, cache, saver, *batchInput);
// Write the result JSON to the specified output path, for each entry
auto ientries = batchInput->cbegin();
auto iresults = batchScanResults.cbegin();
for (; ientries != batchInput->end() and iresults != batchScanResults.end();
++ientries, ++iresults) {
std::error_code EC;
llvm::raw_fd_ostream out((*ientries).outputPath, EC, llvm::sys::fs::F_None);
if ((*iresults).getError())
return true;
writeJSON(out, **iresults);
}
return false;
}
bool swift::dependencies::batchPrescanDependencies(
CompilerInstance &instance, llvm::StringRef batchInputFile) {
// The primary cache used for scans carried out with the compiler instance
// we have created
ModuleDependenciesCache cache;
(void)instance.getMainModule();
llvm::BumpPtrAllocator alloc;
llvm::StringSaver saver(alloc);
auto batchInput =
parseBatchScanInputFile(instance.getASTContext(), batchInputFile, saver);
if (!batchInput.hasValue())
return true;
auto batchPrescanResults =
performBatchModulePrescan(instance, cache, saver, *batchInput);
// Write the result JSON to the specified output path, for each entry
auto ientries = batchInput->cbegin();
auto iresults = batchPrescanResults.cbegin();
for (;
ientries != batchInput->end() and iresults != batchPrescanResults.end();
++ientries, ++iresults) {
std::error_code EC;
llvm::raw_fd_ostream out((*ientries).outputPath, EC, llvm::sys::fs::F_None);
if ((*iresults).getError())
return true;
writePrescanJSON(out, **iresults);
}
return false;
}
llvm::ErrorOr<FullDependencies>
swift::dependencies::performModuleScan(CompilerInstance &instance,
ModuleDependenciesCache &cache) {
ModuleDecl *mainModule = instance.getMainModule();
// First, identify the dependencies of the main module
auto mainDependencies = identifyMainModuleDependencies(instance, cache);
// Add the main module.
StringRef mainModuleName = mainModule->getNameStr();
llvm::SetVector<ModuleDependencyID, std::vector<ModuleDependencyID>,
std::set<ModuleDependencyID>>
allModules;
allModules.insert({mainModuleName.str(), mainDependencies.getKind()});
cache.recordDependencies(mainModuleName, std::move(mainDependencies));
auto &ctx = instance.getASTContext();
auto ModuleCachePath = getModuleCachePathFromClang(
ctx.getClangModuleLoader()->getClangInstance());
auto &FEOpts = instance.getInvocation().getFrontendOptions();
ModuleInterfaceLoaderOptions LoaderOpts(FEOpts);
InterfaceSubContextDelegateImpl ASTDelegate(
ctx.SourceMgr, ctx.Diags, ctx.SearchPathOpts, ctx.LangOpts,
ctx.ClangImporterOpts, LoaderOpts,
/*buildModuleCacheDirIfAbsent*/ false, ModuleCachePath,
FEOpts.PrebuiltModuleCachePath,
FEOpts.SerializeModuleInterfaceDependencyHashes,
FEOpts.shouldTrackSystemDependencies());
// Explore the dependencies of every module.
for (unsigned currentModuleIdx = 0; currentModuleIdx < allModules.size();
++currentModuleIdx) {
auto module = allModules[currentModuleIdx];
auto discoveredModules =
resolveDirectDependencies(instance, module, cache, ASTDelegate);
allModules.insert(discoveredModules.begin(), discoveredModules.end());
}
// We have all explicit imports now, resolve cross import overlays.
discoverCrosssImportOverlayDependencies(
instance, mainModuleName,
/*All transitive dependencies*/ allModules.getArrayRef().slice(1), cache,
ASTDelegate, [&](ModuleDependencyID id) { allModules.insert(id); });
// Dignose cycle in dependency graph.
if (diagnoseCycle(instance, cache, /*MainModule*/ allModules.front(),
ASTDelegate))
return std::make_error_code(std::errc::not_supported);
llvm::dbgs() << "<<<\n";
auto dependencyGraph = generateFullDependencyGraph(
instance, cache, ASTDelegate, allModules.getArrayRef());
llvm::dbgs() << ">>>\n";
// Update the dependency tracker.
if (auto depTracker = instance.getDependencyTracker()) {
for (auto module : allModules) {
auto deps = cache.findDependencies(module.first, module.second);
if (!deps)
continue;
if (auto swiftDeps = deps->getAsSwiftTextualModule()) {
if (auto swiftInterfaceFile = swiftDeps->swiftInterfaceFile)
depTracker->addDependency(*swiftInterfaceFile, /*IsSystem=*/false);
for (const auto &sourceFile : swiftDeps->sourceFiles)
depTracker->addDependency(sourceFile, /*IsSystem=*/false);
for (const auto &bridgingSourceFile : swiftDeps->bridgingSourceFiles)
depTracker->addDependency(bridgingSourceFile, /*IsSystem=*/false);
} else if (auto clangDeps = deps->getAsClangModule()) {
if (!clangDeps->moduleMapFile.empty())
depTracker->addDependency(clangDeps->moduleMapFile,
/*IsSystem=*/false);
for (const auto &sourceFile : clangDeps->fileDependencies)
depTracker->addDependency(sourceFile, /*IsSystem=*/false);
}
}
}
return dependencyGraph;
}
std::vector<llvm::ErrorOr<FullDependencies>>
swift::dependencies::performBatchModuleScan(
CompilerInstance &instance, ModuleDependenciesCache &cache,
llvm::StringSaver &saver, const std::vector<BatchScanInput> &batchInput) {
std::vector<llvm::ErrorOr<FullDependencies>> batchScanResult;
batchScanResult.reserve(batchInput.size());
// Perform a full dependency scan for each batch entry module
forEachBatchEntry(
instance, cache, saver, batchInput,
[&batchScanResult](BatchScanInput entry, CompilerInstance &instance,
ModuleDependenciesCache &cache) {
StringRef moduleName = entry.moduleName;
bool isClang = !entry.isSwift;
ASTContext &ctx = instance.getASTContext();
auto &FEOpts = instance.getInvocation().getFrontendOptions();
ModuleInterfaceLoaderOptions LoaderOpts(FEOpts);
auto ModuleCachePath = getModuleCachePathFromClang(
ctx.getClangModuleLoader()->getClangInstance());
llvm::SetVector<ModuleDependencyID, std::vector<ModuleDependencyID>,
std::set<ModuleDependencyID>>
allModules;
InterfaceSubContextDelegateImpl ASTDelegate(
ctx.SourceMgr, ctx.Diags, ctx.SearchPathOpts, ctx.LangOpts,
ctx.ClangImporterOpts, LoaderOpts,
/*buildModuleCacheDirIfAbsent*/ false, ModuleCachePath,
FEOpts.PrebuiltModuleCachePath,
FEOpts.SerializeModuleInterfaceDependencyHashes,
FEOpts.shouldTrackSystemDependencies());
Optional<ModuleDependencies> rootDeps;
if (isClang) {
// Loading the clang module using Clang importer.
// This action will populate the cache with the main module's
// dependencies.
rootDeps = ctx.getModuleDependencies(moduleName, /*IsClang*/ true,
cache, ASTDelegate);
} else {
// Loading the swift module's dependencies.
rootDeps =
ctx.getSwiftModuleDependencies(moduleName, cache, ASTDelegate);
}
if (!rootDeps.hasValue()) {
// We cannot find the clang module, abort.
batchScanResult.push_back(
std::make_error_code(std::errc::invalid_argument));
return;
}
// Add the main module.
allModules.insert(
{moduleName.str(), isClang ? ModuleDependenciesKind::Clang
: ModuleDependenciesKind::SwiftTextual});
// Explore the dependencies of every module.
for (unsigned currentModuleIdx = 0;
currentModuleIdx < allModules.size(); ++currentModuleIdx) {
auto module = allModules[currentModuleIdx];
auto discoveredModules =
resolveDirectDependencies(instance, module, cache, ASTDelegate);
allModules.insert(discoveredModules.begin(), discoveredModules.end());
}
batchScanResult.push_back(generateFullDependencyGraph(
instance, cache, ASTDelegate, allModules.getArrayRef()));
});
return batchScanResult;
}
std::vector<llvm::ErrorOr<std::vector<std::string>>>
swift::dependencies::performBatchModulePrescan(
CompilerInstance &instance, ModuleDependenciesCache &cache,
llvm::StringSaver &saver, const std::vector<BatchScanInput> &batchInput) {
std::vector<llvm::ErrorOr<std::vector<std::string>>> batchPrescanResult;
// Perform a full dependency scan for each batch entry module
forEachBatchEntry(
instance, cache, saver, batchInput,
[&batchPrescanResult](BatchScanInput entry, CompilerInstance &instance,
ModuleDependenciesCache &cache) {
StringRef moduleName = entry.moduleName;
bool isClang = !entry.isSwift;
ASTContext &ctx = instance.getASTContext();
auto &FEOpts = instance.getInvocation().getFrontendOptions();
ModuleInterfaceLoaderOptions LoaderOpts(FEOpts);
auto ModuleCachePath = getModuleCachePathFromClang(
ctx.getClangModuleLoader()->getClangInstance());
llvm::SetVector<ModuleDependencyID, std::vector<ModuleDependencyID>,
std::set<ModuleDependencyID>>
allModules;
InterfaceSubContextDelegateImpl ASTDelegate(
ctx.SourceMgr, ctx.Diags, ctx.SearchPathOpts, ctx.LangOpts,
ctx.ClangImporterOpts, LoaderOpts,
/*buildModuleCacheDirIfAbsent*/ false, ModuleCachePath,
FEOpts.PrebuiltModuleCachePath,
FEOpts.SerializeModuleInterfaceDependencyHashes,
FEOpts.shouldTrackSystemDependencies());
Optional<ModuleDependencies> rootDeps;
if (isClang) {
// Loading the clang module using Clang importer.
// This action will populate the cache with the main module's
// dependencies.
rootDeps = ctx.getModuleDependencies(moduleName, /*IsClang*/
true, cache, ASTDelegate);
} else {
// Loading the swift module's dependencies.
rootDeps =
ctx.getSwiftModuleDependencies(moduleName, cache, ASTDelegate);
}
if (!rootDeps.hasValue()) {
// We cannot find the clang module, abort.
batchPrescanResult.push_back(
std::make_error_code(std::errc::invalid_argument));
return;
}
batchPrescanResult.push_back(rootDeps->getModuleDependencies());
});
return batchPrescanResult;
}