//===-- Driver.cpp - Swift compiler driver --------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file contains implementations of parts of the compiler driver. // //===----------------------------------------------------------------------===// #include "swift/Driver/Driver.h" #include "Tools.h" #include "ToolChains.h" #include "swift/Strings.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsDriver.h" #include "swift/AST/DiagnosticsFrontend.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/Version.h" #include "swift/Basic/Range.h" #include "swift/Driver/Action.h" #include "swift/Driver/Compilation.h" #include "swift/Driver/Job.h" #include "swift/Driver/Options.h" #include "swift/Driver/OutputFileMap.h" #include "swift/Driver/ToolChain.h" #include "swift/Parse/Lexer.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/OptTable.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" #include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" #include using namespace swift; using namespace swift::driver; using namespace llvm::opt; Driver::Driver(StringRef DriverExecutable, DiagnosticEngine &Diags) : Opts(createDriverOptTable()), Diags(Diags), DriverExecutable(DriverExecutable), DefaultTargetTriple(llvm::sys::getDefaultTargetTriple()) { Name = llvm::sys::path::stem(DriverExecutable); Dir = llvm::sys::path::parent_path(DriverExecutable); } Driver::~Driver() { llvm::DeleteContainerSeconds(ToolChains); } std::unique_ptr Driver::buildCompilation( ArrayRef Args) { llvm::PrettyStackTraceString CrashInfo("Compilation construction"); std::unique_ptr ArgList(parseArgStrings(Args.slice(1))); if (Diags.hadAnyError()) return nullptr; bool DriverPrintActions = ArgList->hasArg(options::OPT_driver_print_actions); bool DriverPrintOutputFileMap = ArgList->hasArg(options::OPT_driver_print_output_file_map); DriverPrintBindings = ArgList->hasArg(options::OPT_driver_print_bindings); bool DriverPrintJobs = ArgList->hasArg(options::OPT_driver_print_jobs); bool DriverSkipExecution = ArgList->hasArg(options::OPT_driver_skip_execution); std::unique_ptr TranslatedArgList( translateInputArgs(*ArgList)); if (const Arg *A = ArgList->getLastArg(options::OPT_target)) DefaultTargetTriple = A->getValue(); const ToolChain &TC = getToolChain(*ArgList); if (Diags.hadAnyError()) return nullptr; if (!handleImmediateArgs(*TranslatedArgList, TC)) { return nullptr; } // Construct the list of inputs. InputList Inputs; buildInputs(TC, *TranslatedArgList, Inputs); if (Diags.hadAnyError()) return nullptr; // Determine the OutputInfo for the driver. OutputInfo OI; buildOutputInfo(*TranslatedArgList, Inputs, OI); if (Diags.hadAnyError()) return nullptr; assert(OI.CompilerOutputType != types::ID::TY_INVALID && "buildOutputInfo() must set a valid output type!"); if (OI.CompilerMode == OutputInfo::Mode::REPL) // REPL mode expects no input files, so suppress the error. SuppressNoInputFilesError = true; // Construct the graph of Actions. ActionList Actions; buildActions(TC, *TranslatedArgList, Inputs, OI, Actions); if (Diags.hadAnyError()) return nullptr; if (DriverPrintActions) { printActions(Actions); return nullptr; } unsigned NumberOfParallelCommands = 1; if (const Arg *A = ArgList->getLastArg(options::OPT_j)) { if (StringRef(A->getValue()).getAsInteger(10, NumberOfParallelCommands)) { Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, A->getAsString(*ArgList), A->getValue()); return nullptr; } } std::unique_ptr OFM; buildOutputFileMap(*TranslatedArgList, OFM); if (Diags.hadAnyError()) return nullptr; if (DriverPrintOutputFileMap) { if (OFM) OFM->dump(llvm::errs(), true); else Diags.diagnose(SourceLoc(), diag::error_no_output_file_map_specified); return nullptr; } OutputLevel Level = OutputLevel::Normal; if (TranslatedArgList->hasArg(options::OPT_v)) Level = OutputLevel::Verbose; std::unique_ptr C(new Compilation(*this, TC, Diags, Level, std::move(ArgList), std::move(TranslatedArgList), NumberOfParallelCommands, DriverSkipExecution)); buildJobs(Actions, OI, OFM.get(), *C); if (Diags.hadAnyError()) return nullptr; if (DriverPrintBindings) return nullptr; if (DriverPrintJobs) { printJobs(C->getJobs()); return nullptr; } return C; } static Arg *makeInputArg(const DerivedArgList &Args, OptTable &Opts, StringRef Value) { Arg *A = new Arg(Opts.getOption(options::OPT_INPUT), Value, Args.getBaseArgs().MakeIndex(Value), Value.data()); A->claim(); return A; } InputArgList *Driver::parseArgStrings(ArrayRef Args) { unsigned IncludedFlagsBitmask = 0; unsigned ExcludedFlagsBitmask = options::NoDriverOption; unsigned MissingArgIndex, MissingArgCount; InputArgList *ArgList = getOpts().ParseArgs(Args.begin(), Args.end(), MissingArgIndex, MissingArgCount, IncludedFlagsBitmask, ExcludedFlagsBitmask); // Check for missing argument error. if (MissingArgCount) { Diags.diagnose(SourceLoc(), diag::error_missing_arg_value, ArgList->getArgString(MissingArgIndex), MissingArgCount); return nullptr; } // Check for unknown arguments. for (const Arg *A : make_range(ArgList->filtered_begin(options::OPT_UNKNOWN), ArgList->filtered_end())) { Diags.diagnose(SourceLoc(), diag::error_unknown_arg, A->getAsString(*ArgList)); } return ArgList; } DerivedArgList *Driver::translateInputArgs(const InputArgList &ArgList) const { DerivedArgList *DAL = new DerivedArgList(ArgList); bool ImmediateMode = ArgList.hasArgNoClaim(options::OPT_i); for (Arg *A : ArgList) { // If we're not in immediate mode, pick up inputs via the -- option. if (!ImmediateMode && A->getOption().matches(options::OPT__DASH_DASH)) { A->claim(); for (unsigned i = 0, e = A->getNumValues(); i != e; ++i) { DAL->append(makeInputArg(*DAL, *Opts, A->getValue(i))); } continue; } DAL->append(A); } return DAL; } /// \brief Check that the file referenced by Value exists. If it doesn't, /// issue a diagnostic and return false. static bool diagnoseInputExistence(const Driver &D, const DerivedArgList &Args, DiagnosticEngine &Diags, StringRef Value) { // FIXME: provide opt-out for checking input file existence // stdin always exists. if (Value == "-") return true; llvm::SmallString<64> Path(Value); if (Arg *WorkDir = Args.getLastArg(options::OPT_working_directory)) { if (!llvm::sys::path::is_absolute(Path.str())) { Path.assign(WorkDir->getValue()); llvm::sys::path::append(Path, Value); } } if (llvm::sys::fs::exists(Twine(Path))) return true; Diags.diagnose(SourceLoc(), diag::error_no_such_file_or_directory, Value); return false; } void Driver::buildInputs(const ToolChain &TC, const DerivedArgList &Args, InputList &Inputs) const { types::ID InputType = types::TY_Nothing; Arg *InputTypeArg = nullptr; for (Arg *A : Args) { if (A->getOption().getKind() == Option::InputClass) { const char *Value = A->getValue(); types::ID Ty = types::TY_INVALID; if (InputType == types::TY_Nothing) { // If there was an explicit arg for this, claim it. if (InputTypeArg) InputTypeArg->claim(); // stdin must be handled specially. if (memcmp(Value, "-", 2) == 0) { // By default, treat stdin as Swift input. // FIXME: should we limit this inference to specific modes? Ty = types::TY_Swift; } else { // Otherwise lookup by extension. if (const char *Ext = strrchr(Value, '.')) { Ty = TC.lookupTypeForExtension(Ext + 1); } if (Ty == types::TY_INVALID) { // FIXME: should we adjust this inference in certain modes? Ty = types::TY_Object; } } } else { assert(InputTypeArg && "InputType set w/o InputTypeArg"); InputTypeArg->claim(); Ty = InputType; } if (diagnoseInputExistence(*this, Args, Diags, Value)) Inputs.push_back(std::make_pair(Ty, A)); } // FIXME: add -x support (or equivalent) } } static bool maybeBuildingExecutable(const OutputInfo &OI, const DerivedArgList &Args, const Driver::InputList &Inputs) { switch (OI.LinkAction) { case LinkKind::Executable: return true; case LinkKind::DynamicLibrary: return false; case LinkKind::None: break; } if (Args.hasArg(options::OPT_parse_as_library, options::OPT_parse_stdlib)) return false; return Inputs.size() == 1; } void Driver::buildOutputInfo(const DerivedArgList &Args, const InputList &Inputs, OutputInfo &OI) const { // By default, the driver does not link its output; this will be updated // appropariately below if linking is required. if (Args.hasArg(options::OPT_force_single_frontend_invocation)) OI.CompilerMode = OutputInfo::Mode::SingleCompile; const Arg *const OutputModeArg = Args.getLastArg(options::OPT_modes_Group); if (!OutputModeArg) { if (Args.hasArg(options::OPT_emit_module, options::OPT_emit_module_path)) { OI.CompilerOutputType = types::TY_SwiftModuleFile; } else if (Inputs.empty() && !Args.hasArg(options::OPT_v)) { // No inputs and no mode arguments imply REPL mode // (Treat -v as a mode, since -v and no other mode arguments means // "print the version and exit".) OI.CompilerOutputType = types::TY_Nothing; OI.CompilerMode = OutputInfo::Mode::REPL; } else { OI.LinkAction = LinkKind::Executable; OI.CompilerOutputType = types::TY_Object; } } else { switch (OutputModeArg->getOption().getID()) { case options::OPT_emit_executable: OI.LinkAction = LinkKind::Executable; OI.CompilerOutputType = types::TY_Object; break; case options::OPT_emit_library: OI.LinkAction = LinkKind::DynamicLibrary; OI.CompilerOutputType = types::TY_Object; break; case options::OPT_c: OI.CompilerOutputType = types::TY_Object; break; case options::OPT_S: OI.CompilerOutputType = types::TY_Assembly; break; case options::OPT_emit_sil: OI.CompilerOutputType = types::TY_SIL; break; case options::OPT_emit_silgen: OI.CompilerOutputType = types::TY_RawSIL; break; case options::OPT_emit_ir: OI.CompilerOutputType = types::TY_LLVM_IR; break; case options::OPT_emit_bc: OI.CompilerOutputType = types::TY_LLVM_BC; break; case options::OPT_parse: case options::OPT_dump_parse: case options::OPT_dump_ast: case options::OPT_print_ast: OI.CompilerOutputType = types::TY_Nothing; break; case options::OPT_i: OI.CompilerOutputType = types::TY_Nothing; OI.CompilerMode = OutputInfo::Mode::Immediate; break; case options::OPT_repl: OI.CompilerOutputType = types::TY_Nothing; OI.CompilerMode = OutputInfo::Mode::REPL; break; default: llvm_unreachable("unknown mode"); } } assert(OI.CompilerOutputType != types::ID::TY_INVALID); bool shouldEmitObjCHeader = Args.hasArg(options::OPT_emit_objc_header, options::OPT_emit_objc_header_path); if (shouldEmitObjCHeader && OI.CompilerMode == OutputInfo::Mode::SingleCompile) { Diags.diagnose(SourceLoc(), diag::error_emit_objc_header_with_single_compile); } if (Args.hasArg(options::OPT_emit_module, options::OPT_emit_module_path)) { // The user has requested a module, so generate one and treat it as // top-level output. OI.ShouldGenerateModule = true; OI.ShouldTreatModuleAsTopLevelOutput = true; } else if ((Args.hasArg(options::OPT_g) && OI.shouldLink()) || shouldEmitObjCHeader) { // An option has been passed which requires a module, but the user hasn't // requested one. Generate a module, but treat it as an intermediate output. OI.ShouldGenerateModule = true; OI.ShouldTreatModuleAsTopLevelOutput = false; } else { // No options require a module, so don't generate one. OI.ShouldGenerateModule = false; OI.ShouldTreatModuleAsTopLevelOutput = false; } if (OI.ShouldGenerateModule && (OI.CompilerMode == OutputInfo::Mode::REPL || OI.CompilerMode == OutputInfo::Mode::Immediate)) { Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_module); return; } if (const Arg *A = Args.getLastArg(options::OPT_module_name)) { OI.ModuleName = A->getValue(); } else if (OI.CompilerMode == OutputInfo::Mode::REPL) { // REPL mode should always use the REPL module. OI.ModuleName = "REPL"; } else if (const Arg *A = Args.getLastArg(options::OPT_o)) { OI.ModuleName = llvm::sys::path::stem(A->getValue()); } else if (Inputs.size() == 1) { OI.ModuleName = llvm::sys::path::stem(Inputs.front().second->getValue()); } if (!Lexer::isIdentifier(OI.ModuleName) || (OI.ModuleName == STDLIB_NAME && !Args.hasArg(options::OPT_parse_stdlib))) { OI.ModuleNameIsFallback = true; if (OI.CompilerOutputType == types::TY_Nothing || maybeBuildingExecutable(OI, Args, Inputs)) OI.ModuleName = "main"; else if (!Inputs.empty() || OI.CompilerMode == OutputInfo::Mode::REPL) { // Having an improper module name is only bad if we have inputs or if // we're in REPL mode. auto DID = (OI.ModuleName == STDLIB_NAME) ? diag::error_stdlib_module_name : diag::error_bad_module_name; Diags.diagnose(SourceLoc(), DID, OI.ModuleName, !Args.hasArg(options::OPT_module_name)); OI.ModuleName = "__bad__"; } } { if (const Arg *A = Args.getLastArg(options::OPT_sdk)) { OI.SDKPath = A->getValue(); } else { const char *SDKROOT = getenv("SDKROOT"); if (SDKROOT) OI.SDKPath = SDKROOT; } if (!OI.SDKPath.empty()) { if (!llvm::sys::fs::exists(OI.SDKPath)) { Diags.diagnose(SourceLoc(), diag::warning_no_such_sdk, OI.SDKPath); } } } } void Driver::buildActions(const ToolChain &TC, const DerivedArgList &Args, const InputList &Inputs, const OutputInfo &OI, ActionList &Actions) const { if (!SuppressNoInputFilesError && Inputs.empty()) { Diags.diagnose(SourceLoc(), diag::error_no_input_files); return; } ActionList CompileActions; switch (OI.CompilerMode) { case OutputInfo::Mode::StandardCompile: { for (const InputPair &Input : Inputs) { types::ID InputType = Input.first; const Arg *InputArg = Input.second; std::unique_ptr Current(new InputAction(*InputArg, InputType)); if (InputType == types::TY_Swift || InputType == types::TY_SIL) Current.reset(new CompileJobAction(Current.release(), OI.CompilerOutputType)); CompileActions.push_back(Current.release()); } break; } case OutputInfo::Mode::SingleCompile: case OutputInfo::Mode::Immediate: { std::unique_ptr CA(new CompileJobAction(OI.CompilerOutputType)); for (const InputPair &Input : Inputs) { types::ID InputType = Input.first; const Arg *InputArg = Input.second; CA->addInput(new InputAction(*InputArg, InputType)); } CompileActions.push_back(CA.release()); break; } case OutputInfo::Mode::REPL: { if (!Inputs.empty()) { // REPL mode requires no inputs. Diags.diagnose(SourceLoc(), diag::error_repl_requires_no_input_files); return; } CompileActions.push_back(new CompileJobAction(OI.CompilerOutputType)); break; } } if (CompileActions.empty()) // If there are no compile actions, don't attempt to set up any downstream // actions. return; std::unique_ptr MergeModuleAction; if (OI.ShouldGenerateModule && OI.CompilerMode != OutputInfo::Mode::SingleCompile) { // We're performing multiple compilations; set up a merge module step // so we generate a single swiftmodule as output. MergeModuleAction.reset(new MergeModuleJobAction(CompileActions)); } if (OI.shouldLink()) { Action *LinkAction = new LinkJobAction(CompileActions, OI.LinkAction); if (MergeModuleAction) { // We have a MergeModuleJobAction; this needs to be an input to the // LinkJobAction. It shares inputs with the LinkAction, so tell it that it // no longer owns its inputs. MergeModuleAction->setOwnsInputs(false); if (Args.hasArg(options::OPT_g)) LinkAction->addInput(MergeModuleAction.release()); else Actions.push_back(MergeModuleAction.release()); } Actions.push_back(LinkAction); } else if (MergeModuleAction) { Actions.push_back(MergeModuleAction.release()); } else { Actions = CompileActions; } } bool Driver::handleImmediateArgs(const ArgList &Args, const ToolChain &TC) { if (Args.hasArg(options::OPT_help)) { printHelp(false); return false; } if (Args.hasArg(options::OPT_help_hidden)) { printHelp(true); return false; } if (Args.hasArg(options::OPT__version)) { // Follow gcc/clang behavior and use stdout for --version and stderr for -v. printVersion(TC, llvm::outs()); return false; } if (Args.hasArg(options::OPT_v)) { printVersion(TC, llvm::errs()); SuppressNoInputFilesError = true; } return true; } void Driver::buildOutputFileMap(const llvm::opt::DerivedArgList &Args, std::unique_ptr &OFM) const { if (const Arg *A = Args.getLastArg(options::OPT_output_file_map)) { // TODO: perform some preflight checks to ensure the file exists. OFM = OutputFileMap::loadFromPath(A->getValue()); if (!OFM) // TODO: emit diagnostic with error string Diags.diagnose(SourceLoc(), diag::error_unable_to_load_output_file_map); } else { // We don't have an OutputFileMap, so reset the unique_ptr. OFM.reset(); } } void Driver::buildJobs(const ActionList &Actions, const OutputInfo &OI, const OutputFileMap *OFM, Compilation &C) const { llvm::PrettyStackTraceString CrashInfo("Building compilation jobs"); const DerivedArgList &Args = C.getArgs(); const ToolChain &TC = C.getDefaultToolChain(); JobCacheMap JobCache; Arg *FinalOutput = Args.getLastArg(options::OPT_o); if (FinalOutput) { unsigned NumOutputs = 0; for (const Action *A : Actions) { types::ID Type = A->getType(); if (Type != types::TY_Nothing && Type != types::TY_SwiftModuleFile) { // Only increment NumOutputs if this is an output which must have its // path specified using -o. // (Module outputs can be specified using -module-output-path, or will // be inferred if there are other top-level outputs.) ++NumOutputs; } } if (NumOutputs > 1) { Diags.diagnose(SourceLoc(), diag::error_cannot_specify__o_for_multiple_outputs); FinalOutput = nullptr; } } // Collect the list of architectures. llvm::StringSet<> ArchNames; if (TC.getTriple().isOSDarwin()) { for (const Arg *A : Args){ if (A->getOption().matches(options::OPT_arch)) { ArchNames.insert(A->getValue()); } } } for (const Action *A : Actions) { Job *J = buildJobsForAction(C, A, OI, OFM, C.getDefaultToolChain(), true, JobCache); C.addJob(J); } } static StringRef getBaseInputForJob(Job *J) { if (Command *Cmd = dyn_cast(J)) { return Cmd->getOutput().getBaseInput(); } else if (JobList *JL = dyn_cast(J)) { return getBaseInputForJob(JL->getJobs()[0]); } else { llvm_unreachable("Unknown Job class; cannot get base input"); } } static void printJobOutputs(const Job *J) { if (const Command *Cmd = dyn_cast(J)) { llvm::outs() << '"' << Cmd->getOutput().getPrimaryOutputFilename() << '"'; } else if (const JobList *JL = dyn_cast(J)) { for (unsigned long i = 0, e = JL->size(); i != e; ++i) { printJobOutputs(JL->getJobs()[i]); if (i+1 != e) { llvm::outs() << ", "; } } } else { llvm_unreachable("Unknown Job class"); } } static StringRef getOutputFilename(const JobAction *JA, const OutputInfo &OI, const TypeToPathMap *OutputMap, const llvm::opt::DerivedArgList &Args, bool AtTopLevel, StringRef BaseInput, DiagnosticEngine &Diags, llvm::SmallString<128> &Buffer) { if (JA->getType() == types::TY_Nothing) return {}; // If available, check the OutputMap first. if (OutputMap) { auto iter = OutputMap->find(JA->getType()); if (iter != OutputMap->end()) return iter->second; } // Process Action-specific output-specifying options next, // since we didn't find anything applicable in the OutputMap. if (isa(JA)) { if (const Arg *A = Args.getLastArg(options::OPT_emit_module_path)) return A->getValue(); if (OI.ShouldTreatModuleAsTopLevelOutput) { if (const Arg *A = Args.getLastArg(options::OPT_o)) { if (OI.CompilerOutputType == types::TY_SwiftModuleFile) return A->getValue(); // Otherwise, put the module next to the top-level output. Buffer = A->getValue(); llvm::sys::path::remove_filename(Buffer); llvm::sys::path::append(Buffer, OI.ModuleName); llvm::sys::path::replace_extension(Buffer, SERIALIZED_MODULE_EXTENSION); return Buffer.str(); } // A top-level output wasn't specified, so just output to // .swiftmodule. Buffer = OI.ModuleName; llvm::sys::path::replace_extension(Buffer, SERIALIZED_MODULE_EXTENSION); return Buffer.str(); } } // We don't have an output from an Action-specific command line option, // so figure one out using the defaults. if (AtTopLevel) { if (Arg *FinalOutput = Args.getLastArg(options::OPT_o)) return FinalOutput->getValue(); if (types::isTextual(JA->getType())) return "-"; } assert(!BaseInput.empty() && "A Command which produces output must have a BaseInput!"); StringRef BaseName(BaseInput); if (isa(JA) || OI.CompilerMode == OutputInfo::Mode::SingleCompile || JA->getType() == types::TY_Image) BaseName = OI.ModuleName; // We don't yet have a name, assign one. if (!AtTopLevel) { // We should output to a temporary file, since we're not at // the top level. StringRef Stem = llvm::sys::path::stem(BaseName); StringRef Suffix = types::getTypeTempSuffix(JA->getType()); llvm::error_code EC = llvm::sys::fs::createTemporaryFile(Stem, Suffix, Buffer); if (EC) { Diags.diagnose(SourceLoc(), diag::error_unable_to_make_temporary_file, EC.message()); return {}; } return Buffer.str(); } if (JA->getType() == types::TY_Image) { if (JA->size() == 1 && OI.ModuleNameIsFallback && BaseInput != "-") BaseName = llvm::sys::path::stem(BaseInput); return BaseName; } StringRef Suffix = types::getTypeTempSuffix(JA->getType()); assert(Suffix.data() && "All types used for output should have a suffix."); Buffer = llvm::sys::path::filename(BaseName); llvm::sys::path::replace_extension(Buffer, Suffix); return Buffer.str(); } Job *Driver::buildJobsForAction(const Compilation &C, const Action *A, const OutputInfo &OI, const OutputFileMap *OFM, const ToolChain &TC, bool AtTopLevel, JobCacheMap &JobCache) const { // 1. See if we've already got this cached. std::pair Key(A, &TC); { auto CacheIter = JobCache.find(Key); if (CacheIter != JobCache.end()) { return CacheIter->second; } } // 2. Build up the list of input jobs. ActionList InputActions; std::unique_ptr InputJobs(new JobList); InputJobs->setOwnsJobs(A->getOwnsInputs()); for (Action *Input : *A) { if (isa(Input)) { InputActions.push_back(Input); } else { InputJobs->addJob(buildJobsForAction(C, Input, OI, OFM, C.getDefaultToolChain(), false, JobCache)); } } // 3. Select the right tool for the job. const JobAction *JA = cast(A); const Tool *T = TC.selectTool(*JA); if (!T) return nullptr; // 4. Determine the CommandOutput for the job. StringRef BaseInput; if (!InputActions.empty()) { // Use the first InputAction as our BaseInput. InputAction *IA = cast(InputActions[0]); BaseInput = IA->getInputArg().getValue(); } else if (!InputJobs->empty()) { // Use the first Job's BaseInput as our BaseInput. Job *J = InputJobs->getJobs()[0]; BaseInput = getBaseInputForJob(J); } const TypeToPathMap *OutputMap = nullptr; if (OFM && isa(JA) && OI.CompilerMode != OutputInfo::Mode::SingleCompile) OutputMap = OFM->getOutputMapForInput(BaseInput); llvm::SmallString<128> Buf; StringRef OutputFile = getOutputFilename(JA, OI, OutputMap, C.getArgs(), AtTopLevel, BaseInput, Diags, Buf); std::unique_ptr Output(new CommandOutput(JA->getType(), OutputFile, BaseInput)); // Choose the swiftmodule output path. if (OI.ShouldGenerateModule && isa(JA) && Output->getPrimaryOutputType() != types::TY_SwiftModuleFile) { StringRef OFMModuleOutputPath; if (OutputMap) { auto iter = OutputMap->find(types::TY_SwiftModuleFile); if (iter != OutputMap->end()) OFMModuleOutputPath = iter->second; } const Arg *A = C.getArgs().getLastArg(options::OPT_emit_module_path); if (!OFMModuleOutputPath.empty()) { // Prefer a path from the OutputMap. Output->setAdditionalOutputForType(types::TY_SwiftModuleFile, OFMModuleOutputPath); } else if (A && OI.CompilerMode == OutputInfo::Mode::SingleCompile) { // We're performing a single compilation (and thus no merge module step), // so prefer to use -emit-module-path, if present. Output->setAdditionalOutputForType(types::TY_SwiftModuleFile, A->getValue()); } else if (OI.CompilerMode == OutputInfo::Mode::SingleCompile && OI.ShouldTreatModuleAsTopLevelOutput) { // We're performing a single compile and don't have -emit-module-path, // but have been told to treat the module as a top-level output. // Determine an appropriate path. if (const Arg *A = C.getArgs().getLastArg(options::OPT_o)) { // Put the module next to the top-level output. llvm::SmallString<128> Path(A->getValue()); llvm::sys::path::remove_filename(Path); llvm::sys::path::append(Path, OI.ModuleName); llvm::sys::path::replace_extension(Path, SERIALIZED_MODULE_EXTENSION); Output->setAdditionalOutputForType(types::TY_SwiftModuleFile, Path); } else { // A top-level output wasn't specified, so just output to // .swiftmodule. llvm::SmallString<128> Path(OI.ModuleName); llvm::sys::path::replace_extension(Path, SERIALIZED_MODULE_EXTENSION); Output->setAdditionalOutputForType(types::TY_SwiftModuleFile, Path); } } else { // We're only generating the module as an intermediate, so put it next // to the primary output of the compile command. llvm::SmallString<128> Path(Output->getPrimaryOutputFilename()); llvm::sys::path::replace_extension(Path, SERIALIZED_MODULE_EXTENSION); Output->setAdditionalOutputForType(types::ID::TY_SwiftModuleFile, Path); } } // Choose the swiftdoc output path. if (OI.ShouldGenerateModule && (isa(JA) || isa(JA))) { StringRef OFMModuleDocOutputPath; if (OutputMap) { auto iter = OutputMap->find(types::TY_SwiftModuleDocFile); if (iter != OutputMap->end()) OFMModuleDocOutputPath = iter->second; } if (!OFMModuleDocOutputPath.empty()) { // Prefer a path from the OutputMap. Output->setAdditionalOutputForType(types::TY_SwiftModuleDocFile, OFMModuleDocOutputPath); } else { // Otherwise, put it next to the swiftmodule file. llvm::SmallString<128> Path( Output->getAnyOutputForType(types::TY_SwiftModuleFile)); llvm::sys::path::replace_extension(Path, SERIALIZED_MODULE_DOC_EXTENSION); Output->setAdditionalOutputForType(types::TY_SwiftModuleDocFile, Path); } } // Choose the serialized diagnostics output path. if (C.getArgs().hasArg(options::OPT_serialize_diagnostics) && isa(JA)) { StringRef OFMSerializeDiagnosticsPath; if (OutputMap) { auto iter = OutputMap->find(types::TY_SerializedDiagnostics); if (iter != OutputMap->end()) OFMSerializeDiagnosticsPath = iter->second; } if (!OFMSerializeDiagnosticsPath.empty()) { // Prefer a path from the OutputMap. Output->setAdditionalOutputForType(types::TY_SerializedDiagnostics, OFMSerializeDiagnosticsPath); } else { // Put the serialized diagnostics next to the primary output file. llvm::SmallString<128> Path; if (Output->getPrimaryOutputType() != types::TY_Nothing) Path = Output->getPrimaryOutputFilename(); else if (!Output->getBaseInput().empty()) Path = llvm::sys::path::stem(Output->getBaseInput()); else Path = OI.ModuleName; llvm::sys::path::replace_extension(Path, "dia"); Output->setAdditionalOutputForType(types::TY_SerializedDiagnostics, Path); } // Remove any existing diagnostics files so that clients can detect their // presence to determine if a command was run. StringRef OutputPath = Output->getAnyOutputForType(types::TY_SerializedDiagnostics); if (llvm::sys::fs::can_write(OutputPath) && llvm::sys::fs::is_regular_file(OutputPath)) llvm::sys::fs::remove(OutputPath); } // Choose the Objective-C header output path. if (isa(JA) && C.getArgs().hasArg(options::OPT_emit_objc_header, options::OPT_emit_objc_header_path)) { StringRef ObjCHeaderPath; if (OutputMap) { auto iter = OutputMap->find(types::TY_ObjCHeader); if (iter != OutputMap->end()) ObjCHeaderPath = iter->second; } if (ObjCHeaderPath.empty()) if (auto A = C.getArgs().getLastArg(options::OPT_emit_objc_header_path)) ObjCHeaderPath = A->getValue(); if (!ObjCHeaderPath.empty()) { Output->setAdditionalOutputForType(types::TY_ObjCHeader, ObjCHeaderPath); } else { // Put the header next to the primary output file. // FIXME: That's not correct if the user /just/ passed -emit-header // and not -emit-module. llvm::SmallString<128> Path; if (Output->getPrimaryOutputType() != types::TY_Nothing) Path = Output->getPrimaryOutputFilename(); else if (!Output->getBaseInput().empty()) Path = llvm::sys::path::stem(Output->getBaseInput()); else Path = OI.ModuleName; llvm::sys::path::replace_extension(Path, "h"); Output->setAdditionalOutputForType(types::TY_ObjCHeader, Path); } } if (DriverPrintBindings) { llvm::outs() << "# \"" << T->getToolChain().getTripleString() << '"' << " - \"" << T->getName() << "\", inputs: ["; // print inputs for (unsigned i = 0, e = InputActions.size(); i != e; ++i) { const InputAction *IA = cast(InputActions[i]); llvm::outs() << '"' << IA->getInputArg().getValue() << '"'; if (i+1 != e || !InputJobs->empty()) llvm::outs() << ", "; } printJobOutputs(InputJobs.get()); llvm::outs() << "], output: {" << types::getTypeName(Output->getPrimaryOutputType()) << ": \"" << Output->getPrimaryOutputFilename() << '"'; for (unsigned i = (types::TY_INVALID + 1), e = types::TY_LAST; i != e; ++i){ StringRef AdditionalOutput = Output->getAdditionalOutputForType((types::ID)i); if (!AdditionalOutput.empty()) { llvm::outs() << ", " << types::getTypeName((types::ID)i) << ": \"" << AdditionalOutput << '"'; } } llvm::outs() << "}\n"; } // 5. Construct a Job which produces the right CommandOutput. Job *J = T->constructJob(*JA, std::move(InputJobs), std::move(Output), InputActions, C.getArgs(), OI); // 6. Add it to the JobCache, so we don't construct the same Job multiple // times. JobCache[Key] = J; return J; } static unsigned printActions(const Action *A, llvm::DenseMap &Ids) { if (Ids.count(A)) return Ids[A]; std::string str; llvm::raw_string_ostream os(str); os << Action::getClassName(A->getKind()) << ", "; if (const InputAction *IA = dyn_cast(A)) { os << "\"" << IA->getInputArg().getValue() << "\""; } else { os << "{"; for (auto it = A->begin(), ie = A->end(); it != ie;) { os << printActions(*it, Ids); ++it; if (it != ie) os << ", "; } os << "}"; } unsigned Id = Ids.size(); Ids[A] = Id; llvm::errs() << Id << ": " << os.str() << ", " << types::getTypeName(A->getType()) << "\n"; return Id; } void Driver::printActions(const ActionList &Actions) const { llvm::DenseMap Ids; for (const Action *A : Actions) { ::printActions(A, Ids); } } static void printJob(const Job *J, llvm::DenseSet &VisitedJobs) { if (!VisitedJobs.insert(J).second) return; if (const JobList *JL = dyn_cast(J)) { for (const Job *Job : *JL) { printJob(Job, VisitedJobs); } } else if (const Command *Cmd = dyn_cast(J)) { const JobList &Inputs = Cmd->getInputs(); printJob(&Inputs, VisitedJobs); Cmd->printCommandLine(llvm::outs()); } else { llvm_unreachable("Unknown JobClass"); } } void Driver::printJobs(const JobList &Jobs) const { llvm::DenseSet VisitedJobs; for (const Job *J : Jobs) { printJob(J, VisitedJobs); } } void Driver::printVersion(const ToolChain &TC, raw_ostream &OS) const { OS << version::getSwiftFullVersion() << '\n'; OS << "Target: " << TC.getTripleString() << '\n'; } void Driver::printHelp(bool ShowHidden) const { unsigned IncludedFlagsBitmask = 0; unsigned ExcludedFlagsBitmask = options::NoDriverOption; if (!ShowHidden) ExcludedFlagsBitmask |= HelpHidden; getOpts().PrintHelp(llvm::outs(), Name.c_str(), "Swift compiler", IncludedFlagsBitmask, ExcludedFlagsBitmask); } std::string Driver::getProgramPath(StringRef Name, const ToolChain &TC) const { // TODO: perform ToolChain-specific lookup std::string P(llvm::sys::FindProgramByName(Name)); if (!P.empty()) { return P; } return Name; } static void setTargetFromArch(DiagnosticEngine &diags, llvm::Triple &target, StringRef archName) { llvm::Triple::ArchType archValue = tools::darwin::getArchTypeForDarwinArchName(archName); if (archValue != llvm::Triple::UnknownArch) { target.setArch(archValue); } else { diags.diagnose(SourceLoc(), diag::error_invalid_arch, archName); } } static llvm::Triple computeTargetTriple(DiagnosticEngine &diags, StringRef DefaultTargetTriple, const ArgList &Args, StringRef DarwinArchName) { // FIXME: need to check -target for overrides llvm::Triple target(llvm::Triple::normalize(DefaultTargetTriple)); // Handle Darwin-specific options available here. if (target.isOSDarwin()) { // If an explict Darwin arch name is given, that trumps all. if (!DarwinArchName.empty()) { setTargetFromArch(diags, target, DarwinArchName); // Handle the Darwin '-arch' flag. } else if (Arg *A = Args.getLastArg(options::OPT_arch)) { setTargetFromArch(diags, target, A->getValue()); } } // TODO: handle other target/pseudo-target flags as necessary. return target; } const ToolChain &Driver::getToolChain(const ArgList &Args, StringRef DarwinArchName) const { llvm::Triple Target = computeTargetTriple(Diags, DefaultTargetTriple, Args, DarwinArchName); ToolChain *&TC = ToolChains[Target.str()]; if (!TC) { switch (Target.getOS()) { case llvm::Triple::Darwin: case llvm::Triple::MacOSX: case llvm::Triple::IOS: TC = new toolchains::Darwin(*this, Target); break; default: llvm_unreachable("No tool chain available for Triple"); } } return *TC; }