//===-------- ParseableOutput.cpp - Helpers for parseable output ----------===// // // 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/Basic/ParseableOutput.h" #include "swift/Basic/FileTypes.h" #include "swift/Basic/JSONSerialization.h" #include "swift/Basic/TaskQueue.h" #include "swift/Driver/Action.h" #include "swift/Driver/Job.h" #include "llvm/Option/Arg.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" using namespace swift::parseable_output; using namespace swift::driver; using namespace swift::sys; using namespace swift; namespace { struct CommandInput { std::string Path; CommandInput() {} CommandInput(StringRef Path) : Path(Path) {} }; using OutputPair = std::pair; } // end anonymous namespace namespace swift { namespace json { template <> struct ScalarTraits { static void output(const CommandInput &value, llvm::raw_ostream &os) { os << value.Path; } static bool mustQuote(StringRef) { return true; } }; template <> struct ScalarEnumerationTraits { static void enumeration(Output &out, file_types::ID &value) { file_types::forAllTypes([&](file_types::ID ty) { std::string typeName = file_types::getTypeName(ty).str(); out.enumCase(value, typeName.c_str(), ty); }); } }; template <> struct ObjectTraits> { static void mapping(Output &out, std::pair &value) { out.mapRequired("type", value.first); out.mapRequired("path", value.second); } }; template struct ArrayTraits> { static size_t size(Output &out, SmallVector &seq) { return seq.size(); } static T &element(Output &out, SmallVector &seq, size_t index) { if (index >= seq.size()) seq.resize(index + 1); return seq[index]; } }; } // namespace json } // namespace swift namespace { Action::Kind mapFrontendInvocationToActionKind(const CompilerInvocation &Invocation) { auto Executable = llvm::sys::path::filename( Invocation.getFrontendOptions().MainExecutablePath); assert(Executable.str() == "swift-frontend" && "Expected a swift-frontend invocation."); Action::Kind ActionKind; FrontendOptions::ActionType ActionType = Invocation.getFrontendOptions().RequestedAction; switch (ActionType) { case FrontendOptions::ActionType::REPL: ActionKind = Action::Kind::REPLJob; break; case FrontendOptions::ActionType::MergeModules: ActionKind = Action::Kind::MergeModuleJob; break; case FrontendOptions::ActionType::Immediate: ActionKind = Action::Kind::InterpretJob; break; case FrontendOptions::ActionType::TypecheckModuleFromInterface: ActionKind = Action::Kind::VerifyModuleInterfaceJob; break; case FrontendOptions::ActionType::EmitPCH: ActionKind = Action::Kind::GeneratePCHJob; break; case FrontendOptions::ActionType::EmitIR: case FrontendOptions::ActionType::EmitBC: case FrontendOptions::ActionType::EmitAssembly: case FrontendOptions::ActionType::EmitObject: { // Whether or not these actions correspond to a "compile" job or a // "backend" job, depends on the input kind. if (Invocation.getFrontendOptions().InputsAndOutputs.shouldTreatAsLLVM()) ActionKind = Action::Kind::BackendJob; else ActionKind = Action::Kind::CompileJob; } break; default: ActionKind = Action::Kind::CompileJob; break; } // The following Driver actions do not correspond to possible Frontend // invocations: // ModuleWrapJob, AutolinkExtractJob, GenerateDSYMJob, VerifyDebugInfoJob, // StaticLinkJob, DynamicLinkJob return ActionKind; } class Message { std::string Kind; std::string Name; public: Message(StringRef Kind, StringRef Name) : Kind(Kind), Name(Name) {} virtual ~Message() = default; virtual void provideMapping(swift::json::Output &out) { out.mapRequired("kind", Kind); out.mapRequired("name", Name); } }; class CommandBasedMessage : public Message { public: CommandBasedMessage(StringRef Kind, const driver::Job &Cmd) : Message(Kind, Cmd.getSource().getClassName()) {} }; class InvocationBasedMessage : public Message { public: InvocationBasedMessage(StringRef Kind, const CompilerInvocation &Invocation) : Message(Kind, Action::getClassName( mapFrontendInvocationToActionKind(Invocation))) {} }; class DetailedCommandBasedMessage : public CommandBasedMessage { std::string Executable; SmallVector Arguments; std::string CommandLine; SmallVector Inputs; SmallVector Outputs; public: DetailedCommandBasedMessage(StringRef Kind, const driver::Job &Cmd) : CommandBasedMessage(Kind, Cmd) { Executable = Cmd.getExecutable(); for (const auto &A : Cmd.getArguments()) { Arguments.push_back(A); } llvm::raw_string_ostream wrapper(CommandLine); Cmd.printCommandLine(wrapper, ""); wrapper.flush(); for (const Action *A : Cmd.getSource().getInputs()) { if (const auto *IA = dyn_cast(A)) Inputs.push_back(CommandInput(IA->getInputArg().getValue())); } for (const driver::Job *J : Cmd.getInputs()) { auto OutFiles = J->getOutput().getPrimaryOutputFilenames(); if (const auto *BJAction = dyn_cast(&Cmd.getSource())) { Inputs.push_back(CommandInput(OutFiles[BJAction->getInputIndex()])); } else { for (llvm::StringRef FileName : OutFiles) { Inputs.push_back(CommandInput(FileName)); } } } // TODO: set up Outputs appropriately. file_types::ID PrimaryOutputType = Cmd.getOutput().getPrimaryOutputType(); if (PrimaryOutputType != file_types::TY_Nothing) { for (llvm::StringRef OutputFileName : Cmd.getOutput().getPrimaryOutputFilenames()) { Outputs.push_back(OutputPair(PrimaryOutputType, OutputFileName.str())); } } file_types::forAllTypes([&](file_types::ID Ty) { for (auto Output : Cmd.getOutput().getAdditionalOutputsForType(Ty)) { Outputs.push_back(OutputPair(Ty, Output.str())); } }); } void provideMapping(swift::json::Output &out) override { Message::provideMapping(out); out.mapRequired("command", CommandLine); // Deprecated, do not document out.mapRequired("command_executable", Executable); out.mapRequired("command_arguments", Arguments); out.mapOptional("inputs", Inputs); out.mapOptional("outputs", Outputs); } }; class DetailedInvocationBasedMessage : public InvocationBasedMessage { ArrayRef Args; std::string Executable; SmallVector Arguments; std::string CommandLine; SmallVector Inputs; SmallVector Outputs; public: DetailedInvocationBasedMessage(StringRef Kind, const CompilerInvocation &Invocation, ArrayRef Args) : InvocationBasedMessage(Kind, Invocation), Args(Args) { const auto &IO = Invocation.getFrontendOptions().InputsAndOutputs; // This frontend invocation correpsonds to a BatchJob: // A container of multiple StandardCompile Jobs. if (IO.hasMultiplePrimaryInputs()) { llvm::dbgs() << "Batch JOB.\n"; } else { llvm::dbgs() << "Single JOB.\n"; } // Command line and arguments Executable = Invocation.getFrontendOptions().MainExecutablePath; CommandLine += Executable; for (const auto &A : Args) { Arguments.push_back(A); CommandLine += std::string(" ") + A; } // Primary Inputs IO.forEachPrimaryInput([&](const InputFile &input) -> bool { Inputs.push_back(CommandInput(input.getFileName())); return false; }); // Outputs IO.forEachOutputFilename([&](const StringRef &output) { Outputs.push_back(OutputPair(file_types::lookupTypeForExtension( llvm::sys::path::extension(output)), output.str())); }); // Supplementary outputs IO.forEachPrimaryInput([&](const InputFile &input) -> bool { const auto &primarySpecificFiles = input.getPrimarySpecificPaths(); const auto &supplementaryOutputPaths = primarySpecificFiles.SupplementaryOutputs; supplementaryOutputPaths.forEachSetOutput([&](const std::string &output) { Outputs.push_back(OutputPair(file_types::lookupTypeForExtension( llvm::sys::path::extension(output)), output)); }); return false; }); } void provideMapping(swift::json::Output &out) override { Message::provideMapping(out); out.mapRequired("command", CommandLine); // Deprecated, do not document out.mapRequired("command_executable", Executable); out.mapRequired("command_arguments", Arguments); out.mapOptional("inputs", Inputs); out.mapOptional("outputs", Outputs); } }; class TaskBasedMessage : public CommandBasedMessage { int64_t Pid; public: TaskBasedMessage(StringRef Kind, const driver::Job &Cmd, int64_t Pid) : CommandBasedMessage(Kind, Cmd), Pid(Pid) {} void provideMapping(swift::json::Output &out) override { CommandBasedMessage::provideMapping(out); out.mapRequired("pid", Pid); } }; class FinishedInvocationBasedMessage : public InvocationBasedMessage { int64_t Pid; public: FinishedInvocationBasedMessage(StringRef Kind, const CompilerInvocation &Invocation, int64_t Pid, StringRef Output, int ExitStatus) : InvocationBasedMessage(Kind, Invocation), Pid(Pid) {} void provideMapping(swift::json::Output &out) override { InvocationBasedMessage::provideMapping(out); out.mapRequired("pid", Pid); } }; class BeganCommandMessage : public DetailedCommandBasedMessage { int64_t Pid; TaskProcessInformation ProcInfo; public: BeganCommandMessage(const driver::Job &Cmd, int64_t Pid, TaskProcessInformation ProcInfo) : DetailedCommandBasedMessage("began", Cmd), Pid(Pid), ProcInfo(ProcInfo) {} void provideMapping(swift::json::Output &out) override { DetailedCommandBasedMessage::provideMapping(out); out.mapRequired("pid", Pid); out.mapRequired("process", ProcInfo); } }; class BeganInvocationMessage : public DetailedInvocationBasedMessage { int64_t Pid; TaskProcessInformation ProcInfo; public: BeganInvocationMessage(const CompilerInvocation &Invocation, ArrayRef Args, int64_t Pid, TaskProcessInformation ProcInfo) : DetailedInvocationBasedMessage("began", Invocation, Args), Pid(Pid), ProcInfo(ProcInfo) {} void provideMapping(swift::json::Output &out) override { DetailedInvocationBasedMessage::provideMapping(out); out.mapRequired("pid", Pid); out.mapRequired("process", ProcInfo); } }; class TaskOutputMessage : public TaskBasedMessage { std::string Output; TaskProcessInformation ProcInfo; public: TaskOutputMessage(StringRef Kind, const driver::Job &Cmd, int64_t Pid, StringRef Output, TaskProcessInformation ProcInfo) : TaskBasedMessage(Kind, Cmd, Pid), Output(Output), ProcInfo(ProcInfo) {} void provideMapping(swift::json::Output &out) override { TaskBasedMessage::provideMapping(out); out.mapOptional("output", Output, std::string()); out.mapRequired("process", ProcInfo); } }; class FinishedCommandMessage : public TaskOutputMessage { int ExitStatus; public: FinishedCommandMessage(const driver::Job &Cmd, int64_t Pid, StringRef Output, TaskProcessInformation ProcInfo, int ExitStatus) : TaskOutputMessage("finished", Cmd, Pid, Output, ProcInfo), ExitStatus(ExitStatus) {} void provideMapping(swift::json::Output &out) override { TaskOutputMessage::provideMapping(out); out.mapRequired("exit-status", ExitStatus); } }; class SignalledMessage : public TaskOutputMessage { std::string ErrorMsg; Optional Signal; public: SignalledMessage(const driver::Job &Cmd, int64_t Pid, StringRef Output, StringRef ErrorMsg, Optional Signal, TaskProcessInformation ProcInfo) : TaskOutputMessage("signalled", Cmd, Pid, Output, ProcInfo), ErrorMsg(ErrorMsg), Signal(Signal) {} void provideMapping(swift::json::Output &out) override { TaskOutputMessage::provideMapping(out); out.mapOptional("error-message", ErrorMsg, std::string()); out.mapOptional("signal", Signal); } }; class SkippedMessage : public DetailedCommandBasedMessage { public: SkippedMessage(const driver::Job &Cmd) : DetailedCommandBasedMessage("skipped", Cmd) {} }; } // end anonymous namespace namespace swift { namespace json { template <> struct ObjectTraits { static void mapping(Output &out, Message &msg) { msg.provideMapping(out); } }; } // namespace json } // namespace swift static void emitMessage(raw_ostream &os, Message &msg) { std::string JSONString; llvm::raw_string_ostream BufferStream(JSONString); json::Output yout(BufferStream); yout << msg; BufferStream.flush(); os << JSONString.length() << '\n'; os << JSONString << '\n'; } void parseable_output::emitBeganMessage( raw_ostream &os, const driver::Job &Cmd, int64_t Pid, TaskProcessInformation ProcInfo) { BeganCommandMessage msg(Cmd, Pid, ProcInfo); emitMessage(os, msg); } void parseable_output::emitBeganMessage( raw_ostream &os, const CompilerInvocation &Invocation, ArrayRef Args, int64_t Pid, TaskProcessInformation ProcInfo) { BeganInvocationMessage msg(Invocation, Args, Pid, ProcInfo); emitMessage(os, msg); } void parseable_output::emitFinishedMessage( raw_ostream &os, const driver::Job &Cmd, int64_t Pid, int ExitStatus, StringRef Output, TaskProcessInformation ProcInfo) { FinishedCommandMessage msg(Cmd, Pid, Output, ProcInfo, ExitStatus); emitMessage(os, msg); } void parseable_output::emitSignalledMessage( raw_ostream &os, const driver::Job &Cmd, int64_t Pid, StringRef ErrorMsg, StringRef Output, Optional Signal, TaskProcessInformation ProcInfo) { SignalledMessage msg(Cmd, Pid, Output, ErrorMsg, Signal, ProcInfo); emitMessage(os, msg); } void parseable_output::emitSkippedMessage(raw_ostream &os, const driver::Job &Cmd) { SkippedMessage msg(Cmd); emitMessage(os, msg); }