mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Fix comment in ArtsToFrontendOptionsConverter re getOutputFilenamesFromCommandLineOrFilelist
667 lines
24 KiB
C++
667 lines
24 KiB
C++
//===--- ArgsToFrontendOptionsConverter -----------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2018 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/Frontend/ArgsToFrontendOptionsConverter.h"
|
|
|
|
#include "swift/AST/DiagnosticsFrontend.h"
|
|
#include "swift/Basic/Platform.h"
|
|
#include "swift/Frontend/ArgsToFrontendInputsConverter.h"
|
|
#include "swift/Frontend/Frontend.h"
|
|
#include "swift/Option/Options.h"
|
|
#include "swift/Option/SanitizerOptions.h"
|
|
#include "swift/Parse/Lexer.h"
|
|
#include "swift/Strings.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/Triple.h"
|
|
#include "llvm/Option/Arg.h"
|
|
#include "llvm/Option/ArgList.h"
|
|
#include "llvm/Option/Option.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/LineIterator.h"
|
|
#include "llvm/Support/Path.h"
|
|
|
|
using namespace swift;
|
|
using namespace llvm::opt;
|
|
|
|
// This is a separate function so that it shows up in stack traces.
|
|
LLVM_ATTRIBUTE_NOINLINE
|
|
static void debugFailWithAssertion() {
|
|
// This assertion should always fail, per the user's request, and should
|
|
// not be converted to llvm_unreachable.
|
|
assert(0 && "This is an assertion!");
|
|
}
|
|
|
|
// This is a separate function so that it shows up in stack traces.
|
|
LLVM_ATTRIBUTE_NOINLINE
|
|
static void debugFailWithCrash() { LLVM_BUILTIN_TRAP; }
|
|
|
|
bool ArgsToFrontendOptionsConverter::convert() {
|
|
using namespace options;
|
|
|
|
handleDebugCrashGroupArguments();
|
|
|
|
if (const Arg *A = Args.getLastArg(OPT_dump_api_path)) {
|
|
Opts.DumpAPIPath = A->getValue();
|
|
}
|
|
if (const Arg *A = Args.getLastArg(OPT_group_info_path)) {
|
|
Opts.GroupInfoPath = A->getValue();
|
|
}
|
|
if (const Arg *A = Args.getLastArg(OPT_index_store_path)) {
|
|
Opts.IndexStorePath = A->getValue();
|
|
}
|
|
Opts.IndexSystemModules |= Args.hasArg(OPT_index_system_modules);
|
|
|
|
Opts.EmitVerboseSIL |= Args.hasArg(OPT_emit_verbose_sil);
|
|
Opts.EmitSortedSIL |= Args.hasArg(OPT_emit_sorted_sil);
|
|
|
|
Opts.EnableTesting |= Args.hasArg(OPT_enable_testing);
|
|
Opts.EnableResilience |= Args.hasArg(OPT_enable_resilience);
|
|
|
|
computePrintStatsOptions();
|
|
computeDebugTimeOptions();
|
|
computeTBDOptions();
|
|
|
|
setUnsignedIntegerArgument(OPT_warn_long_function_bodies, 10,
|
|
Opts.WarnLongFunctionBodies);
|
|
setUnsignedIntegerArgument(OPT_warn_long_expression_type_checking, 10,
|
|
Opts.WarnLongExpressionTypeChecking);
|
|
setUnsignedIntegerArgument(OPT_solver_expression_time_threshold_EQ, 10,
|
|
Opts.SolverExpressionTimeThreshold);
|
|
|
|
computePlaygroundOptions();
|
|
|
|
// This can be enabled independently of the playground transform.
|
|
Opts.PCMacro |= Args.hasArg(OPT_pc_macro);
|
|
|
|
computeHelpOptions();
|
|
if (ArgsToFrontendInputsConverter(Diags, Args, Opts.InputsAndOutputs)
|
|
.convert())
|
|
return true;
|
|
|
|
Opts.ParseStdlib |= Args.hasArg(OPT_parse_stdlib);
|
|
|
|
if (const Arg *A = Args.getLastArg(OPT_verify_generic_signatures)) {
|
|
Opts.VerifyGenericSignaturesInModule = A->getValue();
|
|
}
|
|
|
|
computeDumpScopeMapLocations();
|
|
Opts.RequestedAction = determineRequestedAction();
|
|
|
|
if (Opts.RequestedAction == FrontendOptions::ActionType::Immediate &&
|
|
Opts.InputsAndOutputs.hasPrimaryInputs()) {
|
|
Diags.diagnose(SourceLoc(), diag::error_immediate_mode_primary_file);
|
|
return true;
|
|
}
|
|
|
|
if (setUpForSILOrLLVM())
|
|
return true;
|
|
|
|
if (computeModuleName())
|
|
return true;
|
|
|
|
if (computeOutputFilenames())
|
|
return true;
|
|
determineSupplementaryOutputFilenames();
|
|
|
|
if (checkForUnusedOutputPaths())
|
|
return true;
|
|
|
|
if (const Arg *A = Args.getLastArg(OPT_module_link_name)) {
|
|
Opts.ModuleLinkName = A->getValue();
|
|
}
|
|
|
|
Opts.AlwaysSerializeDebuggingOptions |=
|
|
Args.hasArg(OPT_serialize_debugging_options);
|
|
Opts.EnableSourceImport |= Args.hasArg(OPT_enable_source_import);
|
|
Opts.ImportUnderlyingModule |= Args.hasArg(OPT_import_underlying_module);
|
|
Opts.EnableSerializationNestedTypeLookupTable &=
|
|
!Args.hasArg(OPT_disable_serialization_nested_type_lookup_table);
|
|
|
|
computeImportObjCHeaderOptions();
|
|
computeImplicitImportModuleNames();
|
|
computeLLVMArgs();
|
|
|
|
return false;
|
|
}
|
|
|
|
void ArgsToFrontendOptionsConverter::handleDebugCrashGroupArguments() {
|
|
using namespace options;
|
|
|
|
if (const Arg *A = Args.getLastArg(OPT_debug_crash_Group)) {
|
|
Option Opt = A->getOption();
|
|
if (Opt.matches(OPT_debug_assert_immediately)) {
|
|
debugFailWithAssertion();
|
|
} else if (Opt.matches(OPT_debug_crash_immediately)) {
|
|
debugFailWithCrash();
|
|
} else if (Opt.matches(OPT_debug_assert_after_parse)) {
|
|
// Set in FrontendOptions
|
|
Opts.CrashMode = FrontendOptions::DebugCrashMode::AssertAfterParse;
|
|
} else if (Opt.matches(OPT_debug_crash_after_parse)) {
|
|
// Set in FrontendOptions
|
|
Opts.CrashMode = FrontendOptions::DebugCrashMode::CrashAfterParse;
|
|
} else {
|
|
llvm_unreachable("Unknown debug_crash_Group option!");
|
|
}
|
|
}
|
|
}
|
|
|
|
void ArgsToFrontendOptionsConverter::computePrintStatsOptions() {
|
|
using namespace options;
|
|
Opts.PrintStats |= Args.hasArg(OPT_print_stats);
|
|
Opts.PrintClangStats |= Args.hasArg(OPT_print_clang_stats);
|
|
#if defined(NDEBUG) && !defined(LLVM_ENABLE_STATS)
|
|
if (Opts.PrintStats || Opts.PrintClangStats)
|
|
Diags.diagnose(SourceLoc(), diag::stats_disabled);
|
|
#endif
|
|
}
|
|
|
|
void ArgsToFrontendOptionsConverter::computeDebugTimeOptions() {
|
|
using namespace options;
|
|
Opts.DebugTimeFunctionBodies |= Args.hasArg(OPT_debug_time_function_bodies);
|
|
Opts.DebugTimeExpressionTypeChecking |=
|
|
Args.hasArg(OPT_debug_time_expression_type_checking);
|
|
Opts.DebugTimeCompilation |= Args.hasArg(OPT_debug_time_compilation);
|
|
if (const Arg *A = Args.getLastArg(OPT_stats_output_dir)) {
|
|
Opts.StatsOutputDir = A->getValue();
|
|
if (Args.getLastArg(OPT_trace_stats_events)) {
|
|
Opts.TraceStats = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ArgsToFrontendOptionsConverter::computeTBDOptions() {
|
|
using namespace options;
|
|
if (const Arg *A = Args.getLastArg(OPT_validate_tbd_against_ir_EQ)) {
|
|
using Mode = FrontendOptions::TBDValidationMode;
|
|
StringRef value = A->getValue();
|
|
if (value == "none") {
|
|
Opts.ValidateTBDAgainstIR = Mode::None;
|
|
} else if (value == "missing") {
|
|
Opts.ValidateTBDAgainstIR = Mode::MissingFromTBD;
|
|
} else if (value == "all") {
|
|
Opts.ValidateTBDAgainstIR = Mode::All;
|
|
} else {
|
|
Diags.diagnose(SourceLoc(), diag::error_unsupported_option_argument,
|
|
A->getOption().getPrefixedName(), value);
|
|
}
|
|
}
|
|
if (const Arg *A = Args.getLastArg(OPT_tbd_install_name)) {
|
|
Opts.TBDInstallName = A->getValue();
|
|
}
|
|
}
|
|
|
|
void ArgsToFrontendOptionsConverter::setUnsignedIntegerArgument(
|
|
options::ID optionID, unsigned max, unsigned &valueToSet) {
|
|
if (const Arg *A = Args.getLastArg(optionID)) {
|
|
unsigned attempt;
|
|
if (StringRef(A->getValue()).getAsInteger(max, attempt)) {
|
|
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
|
|
A->getAsString(Args), A->getValue());
|
|
} else {
|
|
valueToSet = attempt;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ArgsToFrontendOptionsConverter::computePlaygroundOptions() {
|
|
using namespace options;
|
|
Opts.PlaygroundTransform |= Args.hasArg(OPT_playground);
|
|
if (Args.hasArg(OPT_disable_playground_transform))
|
|
Opts.PlaygroundTransform = false;
|
|
Opts.PlaygroundHighPerformance |=
|
|
Args.hasArg(OPT_playground_high_performance);
|
|
}
|
|
|
|
void ArgsToFrontendOptionsConverter::computeHelpOptions() {
|
|
using namespace options;
|
|
if (const Arg *A = Args.getLastArg(OPT_help, OPT_help_hidden)) {
|
|
if (A->getOption().matches(OPT_help)) {
|
|
Opts.PrintHelp = true;
|
|
} else if (A->getOption().matches(OPT_help_hidden)) {
|
|
Opts.PrintHelpHidden = true;
|
|
} else {
|
|
llvm_unreachable("Unknown help option parsed");
|
|
}
|
|
}
|
|
}
|
|
|
|
void ArgsToFrontendOptionsConverter::computeDumpScopeMapLocations() {
|
|
using namespace options;
|
|
const Arg *A = Args.getLastArg(OPT_modes_Group);
|
|
if (!A || !A->getOption().matches(OPT_dump_scope_maps))
|
|
return;
|
|
StringRef value = A->getValue();
|
|
if (value == "expanded") {
|
|
// Note: fully expanded the scope map.
|
|
return;
|
|
}
|
|
// Parse a comma-separated list of line:column for lookups to
|
|
// perform (and dump the result of).
|
|
SmallVector<StringRef, 4> locations;
|
|
value.split(locations, ',');
|
|
|
|
bool invalid = false;
|
|
for (auto location : locations) {
|
|
auto lineColumnStr = location.split(':');
|
|
unsigned line, column;
|
|
if (lineColumnStr.first.getAsInteger(10, line) ||
|
|
lineColumnStr.second.getAsInteger(10, column)) {
|
|
Diags.diagnose(SourceLoc(), diag::error_invalid_source_location_str,
|
|
location);
|
|
invalid = true;
|
|
continue;
|
|
}
|
|
Opts.DumpScopeMapLocations.push_back({line, column});
|
|
}
|
|
|
|
if (!invalid && Opts.DumpScopeMapLocations.empty())
|
|
Diags.diagnose(SourceLoc(), diag::error_no_source_location_scope_map);
|
|
}
|
|
|
|
FrontendOptions::ActionType
|
|
ArgsToFrontendOptionsConverter::determineRequestedAction() const {
|
|
using namespace options;
|
|
const Arg *A = Args.getLastArg(OPT_modes_Group);
|
|
if (!A) {
|
|
// We don't have a mode, so determine a default.
|
|
if (Args.hasArg(OPT_emit_module, OPT_emit_module_path)) {
|
|
// We've been told to emit a module, but have no other mode indicators.
|
|
// As a result, put the frontend into EmitModuleOnly mode.
|
|
// (Setting up module output will be handled below.)
|
|
return FrontendOptions::ActionType::EmitModuleOnly;
|
|
}
|
|
return FrontendOptions::ActionType::NoneAction;
|
|
}
|
|
Option Opt = A->getOption();
|
|
if (Opt.matches(OPT_emit_object))
|
|
return FrontendOptions::ActionType::EmitObject;
|
|
if (Opt.matches(OPT_emit_assembly))
|
|
return FrontendOptions::ActionType::EmitAssembly;
|
|
if (Opt.matches(OPT_emit_ir))
|
|
return FrontendOptions::ActionType::EmitIR;
|
|
if (Opt.matches(OPT_emit_bc))
|
|
return FrontendOptions::ActionType::EmitBC;
|
|
if (Opt.matches(OPT_emit_sil))
|
|
return FrontendOptions::ActionType::EmitSIL;
|
|
if (Opt.matches(OPT_emit_silgen))
|
|
return FrontendOptions::ActionType::EmitSILGen;
|
|
if (Opt.matches(OPT_emit_sib))
|
|
return FrontendOptions::ActionType::EmitSIB;
|
|
if (Opt.matches(OPT_emit_sibgen))
|
|
return FrontendOptions::ActionType::EmitSIBGen;
|
|
if (Opt.matches(OPT_emit_pch))
|
|
return FrontendOptions::ActionType::EmitPCH;
|
|
if (Opt.matches(OPT_emit_imported_modules))
|
|
return FrontendOptions::ActionType::EmitImportedModules;
|
|
if (Opt.matches(OPT_parse))
|
|
return FrontendOptions::ActionType::Parse;
|
|
if (Opt.matches(OPT_typecheck))
|
|
return FrontendOptions::ActionType::Typecheck;
|
|
if (Opt.matches(OPT_dump_parse))
|
|
return FrontendOptions::ActionType::DumpParse;
|
|
if (Opt.matches(OPT_dump_ast))
|
|
return FrontendOptions::ActionType::DumpAST;
|
|
if (Opt.matches(OPT_emit_syntax))
|
|
return FrontendOptions::ActionType::EmitSyntax;
|
|
if (Opt.matches(OPT_merge_modules))
|
|
return FrontendOptions::ActionType::MergeModules;
|
|
if (Opt.matches(OPT_dump_scope_maps))
|
|
return FrontendOptions::ActionType::DumpScopeMaps;
|
|
if (Opt.matches(OPT_dump_type_refinement_contexts))
|
|
return FrontendOptions::ActionType::DumpTypeRefinementContexts;
|
|
if (Opt.matches(OPT_dump_interface_hash))
|
|
return FrontendOptions::ActionType::DumpInterfaceHash;
|
|
if (Opt.matches(OPT_print_ast))
|
|
return FrontendOptions::ActionType::PrintAST;
|
|
|
|
if (Opt.matches(OPT_repl) || Opt.matches(OPT_deprecated_integrated_repl))
|
|
return FrontendOptions::ActionType::REPL;
|
|
if (Opt.matches(OPT_interpret))
|
|
return FrontendOptions::ActionType::Immediate;
|
|
|
|
llvm_unreachable("Unhandled mode option");
|
|
}
|
|
|
|
bool ArgsToFrontendOptionsConverter::setUpForSILOrLLVM() {
|
|
using namespace options;
|
|
bool treatAsSIL =
|
|
Args.hasArg(OPT_parse_sil) || Opts.InputsAndOutputs.shouldTreatAsSIL();
|
|
bool treatAsLLVM = Opts.InputsAndOutputs.shouldTreatAsLLVM();
|
|
|
|
if (Opts.InputsAndOutputs.verifyInputs(
|
|
Diags, treatAsSIL,
|
|
Opts.RequestedAction == FrontendOptions::ActionType::REPL,
|
|
Opts.RequestedAction == FrontendOptions::ActionType::NoneAction)) {
|
|
return true;
|
|
}
|
|
if (Opts.RequestedAction == FrontendOptions::ActionType::Immediate) {
|
|
Opts.ImmediateArgv.push_back(
|
|
Opts.InputsAndOutputs.getFilenameOfFirstInput()); // argv[0]
|
|
if (const Arg *A = Args.getLastArg(OPT__DASH_DASH)) {
|
|
for (unsigned i = 0, e = A->getNumValues(); i != e; ++i) {
|
|
Opts.ImmediateArgv.push_back(A->getValue(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (treatAsSIL)
|
|
Opts.InputKind = InputFileKind::IFK_SIL;
|
|
else if (treatAsLLVM)
|
|
Opts.InputKind = InputFileKind::IFK_LLVM_IR;
|
|
else if (Args.hasArg(OPT_parse_as_library))
|
|
Opts.InputKind = InputFileKind::IFK_Swift_Library;
|
|
else if (Opts.RequestedAction == FrontendOptions::ActionType::REPL)
|
|
Opts.InputKind = InputFileKind::IFK_Swift_REPL;
|
|
else
|
|
Opts.InputKind = InputFileKind::IFK_Swift;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ArgsToFrontendOptionsConverter::computeModuleName() {
|
|
const Arg *A = Args.getLastArg(options::OPT_module_name);
|
|
if (A) {
|
|
Opts.ModuleName = A->getValue();
|
|
} else if (Opts.ModuleName.empty()) {
|
|
// The user did not specify a module name, so determine a default fallback
|
|
// based on other options.
|
|
|
|
// Note: this code path will only be taken when running the frontend
|
|
// directly; the driver should always pass -module-name when invoking the
|
|
// frontend.
|
|
if (computeFallbackModuleName())
|
|
return true;
|
|
}
|
|
|
|
if (Lexer::isIdentifier(Opts.ModuleName) &&
|
|
(Opts.ModuleName != STDLIB_NAME || Opts.ParseStdlib)) {
|
|
return false;
|
|
}
|
|
if (!FrontendOptions::needsProperModuleName(Opts.RequestedAction) ||
|
|
Opts.isCompilingExactlyOneSwiftFile()) {
|
|
Opts.ModuleName = "main";
|
|
return false;
|
|
}
|
|
auto DID = (Opts.ModuleName == STDLIB_NAME) ? diag::error_stdlib_module_name
|
|
: diag::error_bad_module_name;
|
|
Diags.diagnose(SourceLoc(), DID, Opts.ModuleName, A == nullptr);
|
|
Opts.ModuleName = "__bad__";
|
|
return false; // FIXME: Must continue to run to pass the tests, but should not
|
|
// have to.
|
|
}
|
|
|
|
bool ArgsToFrontendOptionsConverter::computeFallbackModuleName() {
|
|
if (Opts.RequestedAction == FrontendOptions::ActionType::REPL) {
|
|
// Default to a module named "REPL" if we're in REPL mode.
|
|
Opts.ModuleName = "REPL";
|
|
return false;
|
|
}
|
|
// In order to pass some tests, must leave ModuleName empty.
|
|
if (!Opts.InputsAndOutputs.hasInputs()) {
|
|
Opts.ModuleName = StringRef();
|
|
// FIXME: This is a bug that should not happen, but does in tests.
|
|
// The compiler should bail out earlier, where "no frontend action was
|
|
// selected".
|
|
return false;
|
|
}
|
|
ArrayRef<std::string> outputFilenames =
|
|
getOutputFilenamesFromCommandLineOrFilelist();
|
|
|
|
bool isOutputAUniqueOrdinaryFile =
|
|
outputFilenames.size() == 1 && outputFilenames[0] != "-" &&
|
|
!llvm::sys::fs::is_directory(outputFilenames[0]);
|
|
std::string nameToStem =
|
|
isOutputAUniqueOrdinaryFile
|
|
? outputFilenames[0]
|
|
: Opts.InputsAndOutputs.getFilenameOfFirstInput().str();
|
|
Opts.ModuleName = llvm::sys::path::stem(nameToStem);
|
|
return false;
|
|
}
|
|
|
|
bool ArgsToFrontendOptionsConverter::computeOutputFilenames() {
|
|
assert(Opts.OutputFilenames.empty() &&
|
|
"Output filename should not be set at this point");
|
|
if (!FrontendOptions::doesActionProduceOutput(Opts.RequestedAction)) {
|
|
return false;
|
|
}
|
|
ArrayRef<std::string> outputFilenamesFromCommandLineOrFilelist =
|
|
getOutputFilenamesFromCommandLineOrFilelist();
|
|
|
|
if (outputFilenamesFromCommandLineOrFilelist.size() > 1) {
|
|
// WMO, threaded with N files (also someday batch mode).
|
|
Opts.OutputFilenames = outputFilenamesFromCommandLineOrFilelist;
|
|
return false;
|
|
}
|
|
|
|
if (outputFilenamesFromCommandLineOrFilelist.empty()) {
|
|
// When the Frontend is invoked without going through the driver
|
|
// (e.g. for testing), it is convenient to derive output filenames from
|
|
// input.
|
|
return deriveOutputFilenameFromInputFile();
|
|
}
|
|
|
|
StringRef outputFilename = outputFilenamesFromCommandLineOrFilelist[0];
|
|
if (!llvm::sys::fs::is_directory(outputFilename)) {
|
|
// Could be -primary-file (1), or -wmo (non-threaded w/ N (input) files)
|
|
Opts.OutputFilenames = outputFilenamesFromCommandLineOrFilelist;
|
|
return false;
|
|
}
|
|
// Only used for testing & when invoking frontend directly.
|
|
return deriveOutputFilenameForDirectory(outputFilename);
|
|
}
|
|
|
|
bool ArgsToFrontendOptionsConverter::deriveOutputFilenameFromInputFile() {
|
|
if (Opts.InputsAndOutputs.isReadingFromStdin() ||
|
|
FrontendOptions::doesActionProduceTextualOutput(Opts.RequestedAction)) {
|
|
Opts.setOutputFilenameToStdout();
|
|
return false;
|
|
}
|
|
std::string baseName = determineBaseNameOfOutput();
|
|
if (baseName.empty()) {
|
|
if (Opts.RequestedAction != FrontendOptions::ActionType::REPL &&
|
|
Opts.RequestedAction != FrontendOptions::ActionType::Immediate &&
|
|
Opts.RequestedAction != FrontendOptions::ActionType::NoneAction) {
|
|
Diags.diagnose(SourceLoc(), diag::error_no_output_filename_specified);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
deriveOutputFilenameFromParts("", baseName);
|
|
return false;
|
|
}
|
|
|
|
bool ArgsToFrontendOptionsConverter::deriveOutputFilenameForDirectory(
|
|
StringRef outputDir) {
|
|
|
|
std::string baseName = determineBaseNameOfOutput();
|
|
if (baseName.empty()) {
|
|
Diags.diagnose(SourceLoc(), diag::error_implicit_output_file_is_directory,
|
|
outputDir);
|
|
return true;
|
|
}
|
|
deriveOutputFilenameFromParts(outputDir, baseName);
|
|
return false;
|
|
}
|
|
|
|
void ArgsToFrontendOptionsConverter::deriveOutputFilenameFromParts(
|
|
StringRef dir, StringRef base) {
|
|
assert(!base.empty());
|
|
llvm::SmallString<128> path(dir);
|
|
llvm::sys::path::append(path, base);
|
|
StringRef suffix = FrontendOptions::suffixForPrincipalOutputFileForAction(
|
|
Opts.RequestedAction);
|
|
llvm::sys::path::replace_extension(path, suffix);
|
|
Opts.OutputFilenames.push_back(path.str());
|
|
}
|
|
|
|
std::string ArgsToFrontendOptionsConverter::determineBaseNameOfOutput() const {
|
|
std::string nameToStem;
|
|
if (Opts.InputsAndOutputs.hasPrimaryInputs()) {
|
|
nameToStem = Opts.InputsAndOutputs.getRequiredUniquePrimaryInput().file();
|
|
} else if (auto UserSpecifiedModuleName =
|
|
Args.getLastArg(options::OPT_module_name)) {
|
|
nameToStem = UserSpecifiedModuleName->getValue();
|
|
} else if (Opts.InputsAndOutputs.hasSingleInput()) {
|
|
nameToStem = Opts.InputsAndOutputs.getFilenameOfFirstInput();
|
|
} else
|
|
nameToStem = "";
|
|
|
|
return llvm::sys::path::stem(nameToStem).str();
|
|
}
|
|
|
|
ArrayRef<std::string>
|
|
ArgsToFrontendOptionsConverter::getOutputFilenamesFromCommandLineOrFilelist() {
|
|
if (cachedOutputFilenamesFromCommandLineOrFilelist) {
|
|
return *cachedOutputFilenamesFromCommandLineOrFilelist;
|
|
}
|
|
|
|
if (const Arg *A = Args.getLastArg(options::OPT_output_filelist)) {
|
|
assert(!Args.hasArg(options::OPT_o) &&
|
|
"don't use -o with -output-filelist");
|
|
cachedOutputFilenamesFromCommandLineOrFilelist.emplace(
|
|
readOutputFileList(A->getValue()));
|
|
} else {
|
|
cachedOutputFilenamesFromCommandLineOrFilelist.emplace(
|
|
Args.getAllArgValues(options::OPT_o));
|
|
}
|
|
return *cachedOutputFilenamesFromCommandLineOrFilelist;
|
|
}
|
|
|
|
/// Try to read an output file list file.
|
|
std::vector<std::string> ArgsToFrontendOptionsConverter::readOutputFileList(
|
|
const StringRef filelistPath) const {
|
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer =
|
|
llvm::MemoryBuffer::getFile(filelistPath);
|
|
if (!buffer) {
|
|
Diags.diagnose(SourceLoc(), diag::cannot_open_file, filelistPath,
|
|
buffer.getError().message());
|
|
}
|
|
std::vector<std::string> outputFiles;
|
|
for (StringRef line : make_range(llvm::line_iterator(*buffer.get()), {})) {
|
|
outputFiles.push_back(line.str());
|
|
}
|
|
return outputFiles;
|
|
}
|
|
|
|
void ArgsToFrontendOptionsConverter::determineSupplementaryOutputFilenames() {
|
|
using namespace options;
|
|
auto determineOutputFilename =
|
|
[&](std::string &output, OptSpecifier optWithoutPath,
|
|
OptSpecifier optWithPath, const char *extension, bool useMainOutput) {
|
|
if (const Arg *A = Args.getLastArg(optWithPath)) {
|
|
Args.ClaimAllArgs(optWithoutPath);
|
|
output = A->getValue();
|
|
return;
|
|
}
|
|
|
|
if (!Args.hasArg(optWithoutPath))
|
|
return;
|
|
|
|
if (useMainOutput && !Opts.OutputFilenames.empty()) {
|
|
output = Opts.getSingleOutputFilename();
|
|
return;
|
|
}
|
|
|
|
if (!output.empty())
|
|
return;
|
|
|
|
llvm::SmallString<128> path(Opts.originalPath());
|
|
llvm::sys::path::replace_extension(path, extension);
|
|
output = path.str();
|
|
};
|
|
|
|
determineOutputFilename(Opts.DependenciesFilePath, OPT_emit_dependencies,
|
|
OPT_emit_dependencies_path, "d", false);
|
|
determineOutputFilename(
|
|
Opts.ReferenceDependenciesFilePath, OPT_emit_reference_dependencies,
|
|
OPT_emit_reference_dependencies_path, "swiftdeps", false);
|
|
determineOutputFilename(Opts.SerializedDiagnosticsPath,
|
|
OPT_serialize_diagnostics,
|
|
OPT_serialize_diagnostics_path, "dia", false);
|
|
determineOutputFilename(Opts.ObjCHeaderOutputPath, 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);
|
|
|
|
determineOutputFilename(Opts.TBDPath, OPT_emit_tbd, OPT_emit_tbd_path, "tbd",
|
|
false);
|
|
|
|
if (const Arg *A = Args.getLastArg(OPT_emit_fixits_path)) {
|
|
Opts.FixitsOutputPath = A->getValue();
|
|
}
|
|
|
|
bool isSIB = Opts.RequestedAction == FrontendOptions::ActionType::EmitSIB ||
|
|
Opts.RequestedAction == FrontendOptions::ActionType::EmitSIBGen;
|
|
bool canUseMainOutputForModule =
|
|
Opts.RequestedAction == FrontendOptions::ActionType::MergeModules ||
|
|
Opts.RequestedAction == FrontendOptions::ActionType::EmitModuleOnly ||
|
|
isSIB;
|
|
auto ext = isSIB ? SIB_EXTENSION : SERIALIZED_MODULE_EXTENSION;
|
|
auto sibOpt = Opts.RequestedAction == FrontendOptions::ActionType::EmitSIB
|
|
? OPT_emit_sib
|
|
: OPT_emit_sibgen;
|
|
determineOutputFilename(Opts.ModuleOutputPath,
|
|
isSIB ? sibOpt : OPT_emit_module,
|
|
OPT_emit_module_path, ext, canUseMainOutputForModule);
|
|
|
|
determineOutputFilename(Opts.ModuleDocOutputPath, OPT_emit_module_doc,
|
|
OPT_emit_module_doc_path,
|
|
SERIALIZED_MODULE_DOC_EXTENSION, false);
|
|
}
|
|
|
|
bool ArgsToFrontendOptionsConverter::checkForUnusedOutputPaths() const {
|
|
if (Opts.hasUnusedDependenciesFilePath()) {
|
|
Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_dependencies);
|
|
return true;
|
|
}
|
|
if (Opts.hasUnusedObjCHeaderOutputPath()) {
|
|
Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_header);
|
|
return true;
|
|
}
|
|
if (Opts.hasUnusedLoadedModuleTracePath()) {
|
|
Diags.diagnose(SourceLoc(),
|
|
diag::error_mode_cannot_emit_loaded_module_trace);
|
|
return true;
|
|
}
|
|
if (Opts.hasUnusedModuleOutputPath()) {
|
|
Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_module);
|
|
return true;
|
|
}
|
|
if (Opts.hasUnusedModuleDocOutputPath()) {
|
|
Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_module_doc);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ArgsToFrontendOptionsConverter::computeImportObjCHeaderOptions() {
|
|
using namespace options;
|
|
if (const Arg *A = Args.getLastArgNoClaim(OPT_import_objc_header)) {
|
|
Opts.ImplicitObjCHeaderPath = A->getValue();
|
|
Opts.SerializeBridgingHeader |= !Opts.InputsAndOutputs.hasPrimaryInputs() &&
|
|
!Opts.ModuleOutputPath.empty();
|
|
}
|
|
}
|
|
void ArgsToFrontendOptionsConverter::computeImplicitImportModuleNames() {
|
|
using namespace options;
|
|
for (const Arg *A : Args.filtered(OPT_import_module)) {
|
|
Opts.ImplicitImportModuleNames.push_back(A->getValue());
|
|
}
|
|
}
|
|
void ArgsToFrontendOptionsConverter::computeLLVMArgs() {
|
|
using namespace options;
|
|
for (const Arg *A : Args.filtered(OPT_Xllvm)) {
|
|
Opts.LLVMArgs.push_back(A->getValue());
|
|
}
|
|
}
|