mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[Frontend] -frontend -emit-loaded-module-trace.
The -frontend jobs can output a JSON file summarizing the swiftmodules (etc.) they load during compilation.
This commit is contained in:
@@ -106,6 +106,8 @@ ERROR(error_mode_cannot_emit_dependencies,none,
|
||||
"this mode does not support emitting dependency files", ())
|
||||
ERROR(error_mode_cannot_emit_header,none,
|
||||
"this mode does not support emitting Objective-C headers", ())
|
||||
ERROR(error_mode_cannot_emit_loaded_module_trace,none,
|
||||
"this mode does not support emitting the loaded module trace", ())
|
||||
ERROR(error_mode_cannot_emit_module,none,
|
||||
"this mode does not support emitting modules", ())
|
||||
ERROR(error_mode_cannot_emit_module_doc,none,
|
||||
|
||||
@@ -445,6 +445,16 @@ private:
|
||||
void indent();
|
||||
};
|
||||
|
||||
template <typename T> struct ArrayTraits<std::vector<T>> {
|
||||
static size_t size(Output &out, std::vector<T> &seq) { return seq.size(); }
|
||||
|
||||
static T &element(Output &out, std::vector<T> &seq, size_t index) {
|
||||
if (index >= seq.size())
|
||||
seq.resize(index + 1);
|
||||
return seq[index];
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ScalarTraits<bool> {
|
||||
static void output(const bool &, llvm::raw_ostream &);
|
||||
|
||||
@@ -59,6 +59,7 @@ TYPE("swift-dependencies", SwiftDeps, "swiftdeps", "")
|
||||
TYPE("remap", Remapping, "remap", "")
|
||||
TYPE("imported-modules", ImportedModules, "importedmodules", "")
|
||||
TYPE("tbd", TBD, "tbd", "")
|
||||
TYPE("module-trace", ModuleTrace, "trace.json", "")
|
||||
|
||||
// Misc types
|
||||
TYPE("pcm", ClangModuleFile, "pcm", "")
|
||||
|
||||
@@ -116,6 +116,9 @@ public:
|
||||
/// The path to which we should output fixits as source edits.
|
||||
std::string FixitsOutputPath;
|
||||
|
||||
/// The path to which we should output a loaded module trace file.
|
||||
std::string LoadedModuleTracePath;
|
||||
|
||||
/// Arguments which should be passed in immediate mode.
|
||||
std::vector<std::string> ImmediateArgv;
|
||||
|
||||
|
||||
@@ -199,6 +199,16 @@ def emit_dependencies : Flag<["-"], "emit-dependencies">,
|
||||
Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>,
|
||||
HelpText<"Emit basic Make-compatible dependencies files">;
|
||||
|
||||
def emit_loaded_module_trace : Flag<["-"], "emit-loaded-module-trace">,
|
||||
Flags<[FrontendOption, NoInteractiveOption]>,
|
||||
HelpText<"Emit a JSON file containing information about what modules were loaded">;
|
||||
def emit_loaded_module_trace_path : Separate<["-"], "emit-loaded-module-trace-path">,
|
||||
Flags<[FrontendOption, NoInteractiveOption]>,
|
||||
HelpText<"Emit the loaded module trace JSON to <path>">,
|
||||
MetaVarName<"<path>">;
|
||||
def emit_loaded_module_trace_path_EQ : Joined<["-"], "emit-loaded-module-trace-path=">,
|
||||
Flags<[FrontendOption, NoInteractiveOption]>, Alias<emit_loaded_module_trace_path>;
|
||||
|
||||
def serialize_diagnostics : Flag<["-"], "serialize-diagnostics">,
|
||||
Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>,
|
||||
HelpText<"Serialize diagnostics in a binary format">;
|
||||
|
||||
@@ -1421,6 +1421,7 @@ void Driver::buildActions(const ToolChain &TC,
|
||||
case types::TY_PCH:
|
||||
case types::TY_ImportedModules:
|
||||
case types::TY_TBD:
|
||||
case types::TY_ModuleTrace:
|
||||
// We could in theory handle assembly or LLVM input, but let's not.
|
||||
// FIXME: What about LTO?
|
||||
Diags.diagnose(SourceLoc(), diag::error_unexpected_input_file,
|
||||
@@ -2072,6 +2073,10 @@ Job *Driver::buildJobsForAction(Compilation &C, const JobAction *JA,
|
||||
if (C.getArgs().hasArg(options::OPT_emit_dependencies)) {
|
||||
addAuxiliaryOutput(C, *Output, types::TY_Dependencies, OI, OutputMap);
|
||||
}
|
||||
if (C.getArgs().hasArg(options::OPT_emit_loaded_module_trace,
|
||||
options::OPT_emit_loaded_module_trace_path)) {
|
||||
addAuxiliaryOutput(C, *Output, types::TY_ModuleTrace, OI, OutputMap);
|
||||
}
|
||||
if (C.getIncrementalBuildEnabled()) {
|
||||
addAuxiliaryOutput(C, *Output, types::TY_SwiftDeps, OI, OutputMap);
|
||||
}
|
||||
|
||||
@@ -249,6 +249,7 @@ ToolChain::constructInvocation(const CompileJobAction &job,
|
||||
case types::TY_ObjCHeader:
|
||||
case types::TY_Image:
|
||||
case types::TY_SwiftDeps:
|
||||
case types::TY_ModuleTrace:
|
||||
llvm_unreachable("Output type can never be primary output.");
|
||||
case types::TY_INVALID:
|
||||
llvm_unreachable("Invalid type ID");
|
||||
@@ -381,6 +382,13 @@ ToolChain::constructInvocation(const CompileJobAction &job,
|
||||
Arguments.push_back(ReferenceDependenciesPath.c_str());
|
||||
}
|
||||
|
||||
const std::string &LoadedModuleTracePath =
|
||||
context.Output.getAdditionalOutputForType(types::TY_ModuleTrace);
|
||||
if (!LoadedModuleTracePath.empty()) {
|
||||
Arguments.push_back("-emit-loaded-module-trace-path");
|
||||
Arguments.push_back(LoadedModuleTracePath.c_str());
|
||||
}
|
||||
|
||||
const std::string &FixitsPath =
|
||||
context.Output.getAdditionalOutputForType(types::TY_Remapping);
|
||||
if (!FixitsPath.empty()) {
|
||||
@@ -511,6 +519,7 @@ ToolChain::constructInvocation(const BackendJobAction &job,
|
||||
case types::TY_Image:
|
||||
case types::TY_SwiftDeps:
|
||||
case types::TY_Remapping:
|
||||
case types::TY_ModuleTrace:
|
||||
llvm_unreachable("Output type can never be primary output.");
|
||||
case types::TY_INVALID:
|
||||
llvm_unreachable("Invalid type ID");
|
||||
|
||||
@@ -76,6 +76,7 @@ bool types::isTextual(ID Id) {
|
||||
case types::TY_AutolinkFile:
|
||||
case types::TY_ImportedModules:
|
||||
case types::TY_TBD:
|
||||
case types::TY_ModuleTrace:
|
||||
return true;
|
||||
case types::TY_Image:
|
||||
case types::TY_Object:
|
||||
@@ -127,6 +128,7 @@ bool types::isAfterLLVM(ID Id) {
|
||||
case types::TY_SwiftDeps:
|
||||
case types::TY_Nothing:
|
||||
case types::TY_Remapping:
|
||||
case types::TY_ModuleTrace:
|
||||
return false;
|
||||
case types::TY_INVALID:
|
||||
llvm_unreachable("Invalid type ID.");
|
||||
@@ -163,6 +165,7 @@ bool types::isPartOfSwiftCompilation(ID Id) {
|
||||
case types::TY_SwiftDeps:
|
||||
case types::TY_Nothing:
|
||||
case types::TY_Remapping:
|
||||
case types::TY_ModuleTrace:
|
||||
return false;
|
||||
case types::TY_INVALID:
|
||||
llvm_unreachable("Invalid type ID.");
|
||||
|
||||
@@ -676,6 +676,10 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
|
||||
OPT_emit_objc_header,
|
||||
OPT_emit_objc_header_path,
|
||||
"h", false);
|
||||
determineOutputFilename(Opts.LoadedModuleTracePath,
|
||||
OPT_emit_loaded_module_trace,
|
||||
OPT_emit_loaded_module_trace_path,
|
||||
"trace.json", false);
|
||||
|
||||
if (const Arg *A = Args.getLastArg(OPT_emit_fixits_path)) {
|
||||
Opts.FixitsOutputPath = A->getValue();
|
||||
@@ -765,6 +769,39 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
|
||||
}
|
||||
}
|
||||
|
||||
if (!Opts.LoadedModuleTracePath.empty()) {
|
||||
switch (Opts.RequestedAction) {
|
||||
case FrontendOptions::NoneAction:
|
||||
case FrontendOptions::Parse:
|
||||
case FrontendOptions::DumpParse:
|
||||
case FrontendOptions::DumpInterfaceHash:
|
||||
case FrontendOptions::DumpAST:
|
||||
case FrontendOptions::PrintAST:
|
||||
case FrontendOptions::DumpScopeMaps:
|
||||
case FrontendOptions::DumpTypeRefinementContexts:
|
||||
case FrontendOptions::Immediate:
|
||||
case FrontendOptions::REPL:
|
||||
case FrontendOptions::UpdateCode:
|
||||
Diags.diagnose(SourceLoc(),
|
||||
diag::error_mode_cannot_emit_loaded_module_trace);
|
||||
return true;
|
||||
case FrontendOptions::Typecheck:
|
||||
case FrontendOptions::EmitModuleOnly:
|
||||
case FrontendOptions::EmitPCH:
|
||||
case FrontendOptions::EmitSILGen:
|
||||
case FrontendOptions::EmitSIL:
|
||||
case FrontendOptions::EmitSIBGen:
|
||||
case FrontendOptions::EmitSIB:
|
||||
case FrontendOptions::EmitIR:
|
||||
case FrontendOptions::EmitBC:
|
||||
case FrontendOptions::EmitAssembly:
|
||||
case FrontendOptions::EmitObject:
|
||||
case FrontendOptions::EmitImportedModules:
|
||||
case FrontendOptions::EmitTBD:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Opts.ModuleOutputPath.empty() ||
|
||||
!Opts.ModuleDocOutputPath.empty()) {
|
||||
switch (Opts.RequestedAction) {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "ReferenceDependencies.h"
|
||||
#include "TBD.h"
|
||||
|
||||
#include "swift/Strings.h"
|
||||
#include "swift/Subsystems.h"
|
||||
#include "swift/AST/ASTScope.h"
|
||||
#include "swift/AST/DiagnosticsFrontend.h"
|
||||
@@ -36,10 +37,12 @@
|
||||
#include "swift/Basic/Dwarf.h"
|
||||
#include "swift/Basic/Edit.h"
|
||||
#include "swift/Basic/FileSystem.h"
|
||||
#include "swift/Basic/JSONSerialization.h"
|
||||
#include "swift/Basic/LLVMContext.h"
|
||||
#include "swift/Basic/SourceManager.h"
|
||||
#include "swift/Basic/Statistic.h"
|
||||
#include "swift/Basic/Timer.h"
|
||||
#include "swift/Basic/UUID.h"
|
||||
#include "swift/Frontend/DiagnosticVerifier.h"
|
||||
#include "swift/Frontend/Frontend.h"
|
||||
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
|
||||
@@ -54,6 +57,7 @@
|
||||
// FIXME: We're just using CompilerInstance::createOutputFile.
|
||||
// This API should be sunk down to LLVM.
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/APINotes/Types.h"
|
||||
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
@@ -71,6 +75,12 @@
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
|
||||
#if !defined(_MSC_VER) && !defined(__MINGW32__)
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
using namespace swift;
|
||||
|
||||
static std::string displayName(StringRef MainExecutablePath) {
|
||||
@@ -132,6 +142,78 @@ static bool emitMakeDependencies(DiagnosticEngine &diags,
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct LoadedModuleTraceFormat {
|
||||
std::string Name;
|
||||
std::string Arch;
|
||||
std::vector<std::string> SwiftModules;
|
||||
};
|
||||
}
|
||||
|
||||
namespace swift {
|
||||
namespace json {
|
||||
template <> struct ObjectTraits<LoadedModuleTraceFormat> {
|
||||
static void mapping(Output &out, LoadedModuleTraceFormat &contents) {
|
||||
out.mapRequired("name", contents.Name);
|
||||
out.mapRequired("arch", contents.Arch);
|
||||
out.mapRequired("swiftmodules", contents.SwiftModules);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static bool emitLoadedModuleTrace(ASTContext &ctxt,
|
||||
DependencyTracker &depTracker,
|
||||
const FrontendOptions &opts) {
|
||||
std::error_code EC;
|
||||
llvm::raw_fd_ostream out(opts.LoadedModuleTracePath, EC,
|
||||
llvm::sys::fs::F_None);
|
||||
|
||||
if (out.has_error() || EC) {
|
||||
ctxt.Diags.diagnose(SourceLoc(), diag::error_opening_output,
|
||||
opts.LoadedModuleTracePath, EC.message());
|
||||
out.clear_error();
|
||||
return true;
|
||||
}
|
||||
|
||||
llvm::SmallVector<std::string, 16> swiftModules;
|
||||
|
||||
// Canonicalise all the paths by opening them.
|
||||
for (auto &dep : depTracker.getDependencies()) {
|
||||
llvm::SmallString<256> buffer;
|
||||
StringRef realPath;
|
||||
int FD;
|
||||
// FIXME: appropriate error handling
|
||||
if (llvm::sys::fs::openFileForRead(dep, FD, &buffer)) {
|
||||
// Couldn't open the file now, so let's just assume the old path was
|
||||
// canonical (enough).
|
||||
realPath = dep;
|
||||
} else {
|
||||
realPath = buffer.str();
|
||||
// Not much we can do about failing to close.
|
||||
(void)close(FD);
|
||||
}
|
||||
|
||||
// Decide if this is a swiftmodule based on the extension of the raw
|
||||
// dependency path, as the true file may have a different one.
|
||||
auto ext = llvm::sys::path::extension(dep);
|
||||
if (ext.startswith(".") &&
|
||||
ext.drop_front() == SERIALIZED_MODULE_EXTENSION) {
|
||||
swiftModules.push_back(realPath);
|
||||
}
|
||||
}
|
||||
|
||||
LoadedModuleTraceFormat trace = {
|
||||
/*name=*/opts.ModuleName,
|
||||
/*arch=*/ctxt.LangOpts.Target.getArchName(),
|
||||
/*swiftmodules=*/reversePathSortedFilenames(swiftModules)};
|
||||
|
||||
json::Output jsonOutput(out, /*PrettyPrint=*/false);
|
||||
json::jsonize(jsonOutput, trace, /*Required=*/true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Writes SIL out to the given file.
|
||||
static bool writeSIL(SILModule &SM, ModuleDecl *M, bool EmitVerboseSIL,
|
||||
StringRef OutputFilename, bool SortSIL) {
|
||||
@@ -539,6 +621,10 @@ static bool performCompile(std::unique_ptr<CompilerInstance> &Instance,
|
||||
emitReferenceDependencies(Context.Diags, Instance->getPrimarySourceFile(),
|
||||
*Instance->getDependencyTracker(), opts);
|
||||
|
||||
if (!opts.LoadedModuleTracePath.empty())
|
||||
(void)emitLoadedModuleTrace(Context, *Instance->getDependencyTracker(),
|
||||
opts);
|
||||
|
||||
if (Context.hadError())
|
||||
return true;
|
||||
|
||||
@@ -1081,7 +1167,8 @@ int swift::performFrontend(ArrayRef<const char *> Args,
|
||||
|
||||
DependencyTracker depTracker;
|
||||
if (!Invocation.getFrontendOptions().DependenciesFilePath.empty() ||
|
||||
!Invocation.getFrontendOptions().ReferenceDependenciesFilePath.empty()) {
|
||||
!Invocation.getFrontendOptions().ReferenceDependenciesFilePath.empty() ||
|
||||
!Invocation.getFrontendOptions().LoadedModuleTracePath.empty()) {
|
||||
Instance->setDependencyTracker(&depTracker);
|
||||
}
|
||||
|
||||
|
||||
1
test/Driver/Inputs/loaded_module_trace_empty.swift
Normal file
1
test/Driver/Inputs/loaded_module_trace_empty.swift
Normal file
@@ -0,0 +1 @@
|
||||
// Don't need anything here, just for the module to exist.
|
||||
15
test/Driver/loaded_module_trace.swift
Normal file
15
test/Driver/loaded_module_trace.swift
Normal file
@@ -0,0 +1,15 @@
|
||||
// RUN: rm -rf %t && mkdir -p %t
|
||||
// RUN: %target-build-swift -emit-module -module-name Module %S/Inputs/loaded_module_trace_empty.swift -o %t/Module.swiftmodule
|
||||
// RUN: %target-swift-frontend -c %s -emit-loaded-module-trace -emit-loaded-module-trace-path - -I %t | %FileCheck %s
|
||||
|
||||
// CHECK: {
|
||||
// CHECK: "name":"loaded_module_trace"
|
||||
// CHECK: "arch":"{{[^"]*}}"
|
||||
// CHECK: "swiftmodules":[
|
||||
// CHECK: "{{[^"]*}}/Module.swiftmodule"
|
||||
// CHECK: "{{[^"]*}}/Swift.swiftmodule"
|
||||
// CHECK: "{{[^"]*}}/SwiftOnoneSupport.swiftmodule"
|
||||
// CHECK: ]
|
||||
// CHECK: }
|
||||
|
||||
import Module
|
||||
19
test/Driver/loaded_module_trace_foundation.swift
Normal file
19
test/Driver/loaded_module_trace_foundation.swift
Normal file
@@ -0,0 +1,19 @@
|
||||
// RUN: %target-swift-frontend -c %s -emit-loaded-module-trace -emit-loaded-module-trace-path - | %FileCheck %s
|
||||
|
||||
// REQUIRES: objc_interop
|
||||
|
||||
// CHECK: {
|
||||
// CHECK: "name":"loaded_module_trace_foundation"
|
||||
// CHECK: "arch":"{{[^"]*}}"
|
||||
// CHECK: "swiftmodules":[
|
||||
// CHECK: "{{[^"]*}}/ObjectiveC.swiftmodule"
|
||||
// CHECK: "{{[^"]*}}/Dispatch.swiftmodule"
|
||||
// CHECK: "{{[^"]*}}/Darwin.swiftmodule"
|
||||
// CHECK: "{{[^"]*}}/Foundation.swiftmodule"
|
||||
// CHECK: "{{[^"]*}}/Swift.swiftmodule"
|
||||
// CHECK: "{{[^"]*}}/IOKit.swiftmodule"
|
||||
// CHECK: "{{[^"]*}}/SwiftOnoneSupport.swiftmodule"
|
||||
// CHECK: ]
|
||||
// CHECK: }
|
||||
|
||||
import Foundation
|
||||
Reference in New Issue
Block a user