blob: f9616c285d756f0852ca2063614d8d8c4c12f766 [file] [log] [blame]
//===--- ArgsToFrontendOutputsConverter.cpp -------------------------------===//
// 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/ArgsToFrontendOutputsConverter.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/Basic/Platform.h"
#include "swift/Frontend/ArgsToFrontendInputsConverter.h"
#include "swift/Frontend/ArgsToFrontendOptionsConverter.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Option/Options.h"
#include "swift/Option/SanitizerOptions.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;
bool ArgsToFrontendOutputsConverter::convert(
std::vector<std::string> &mainOutputs,
std::vector<SupplementaryOutputPaths> &supplementaryOutputs) {
Optional<OutputFilesComputer> ofc =
OutputFilesComputer::create(Args, Diags, InputsAndOutputs);
if (!ofc)
return true;
Optional<std::vector<std::string>> mains = ofc->computeOutputFiles();
if (!mains)
return true;
Optional<std::vector<SupplementaryOutputPaths>> supplementaries =
SupplementaryOutputPathsComputer(Args, Diags, InputsAndOutputs, *mains,
ModuleName)
.computeOutputPaths();
if (!supplementaries)
return true;
mainOutputs = std::move(*mains);
supplementaryOutputs = std::move(*supplementaries);
return false;
}
Optional<std::vector<std::string>>
ArgsToFrontendOutputsConverter::readOutputFileList(const StringRef filelistPath,
DiagnosticEngine &diags) {
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());
return None;
}
std::vector<std::string> outputFiles;
for (StringRef line : make_range(llvm::line_iterator(*buffer.get()), {})) {
outputFiles.push_back(line.str());
}
return outputFiles;
}
Optional<std::vector<std::string>>
OutputFilesComputer::getOutputFilenamesFromCommandLineOrFilelist(
const ArgList &args, DiagnosticEngine &diags) {
if (const Arg *A = args.getLastArg(options::OPT_output_filelist)) {
assert(!args.hasArg(options::OPT_o) &&
"don't use -o with -output-filelist");
return ArgsToFrontendOutputsConverter::readOutputFileList(A->getValue(),
diags);
}
return args.getAllArgValues(options::OPT_o);
}
Optional<OutputFilesComputer>
OutputFilesComputer::create(const llvm::opt::ArgList &args,
DiagnosticEngine &diags,
const FrontendInputsAndOutputs &inputsAndOutputs) {
Optional<std::vector<std::string>> outputArguments =
getOutputFilenamesFromCommandLineOrFilelist(args, diags);
if (!outputArguments)
return None;
const StringRef outputDirectoryArgument =
outputArguments->size() == 1 &&
llvm::sys::fs::is_directory(outputArguments->front())
? StringRef(outputArguments->front())
: StringRef();
ArrayRef<std::string> outputFileArguments =
outputDirectoryArgument.empty() ? ArrayRef<std::string>(*outputArguments)
: ArrayRef<std::string>();
const StringRef firstInput =
inputsAndOutputs.hasSingleInput()
? StringRef(inputsAndOutputs.getFilenameOfFirstInput())
: StringRef();
const FrontendOptions::ActionType requestedAction =
ArgsToFrontendOptionsConverter::determineRequestedAction(args);
if (!outputFileArguments.empty() &&
outputFileArguments.size() !=
inputsAndOutputs.countOfInputsProducingMainOutputs()) {
diags.diagnose(
SourceLoc(),
diag::error_if_any_output_files_are_specified_they_all_must_be);
return None;
}
return OutputFilesComputer(
args, diags, inputsAndOutputs, std::move(outputFileArguments),
outputDirectoryArgument, firstInput, requestedAction,
args.getLastArg(options::OPT_module_name),
FrontendOptions::suffixForPrincipalOutputFileForAction(requestedAction),
FrontendOptions::doesActionProduceTextualOutput(requestedAction));
}
OutputFilesComputer::OutputFilesComputer(
const llvm::opt::ArgList &args, DiagnosticEngine &diags,
const FrontendInputsAndOutputs &inputsAndOutputs,
std::vector<std::string> outputFileArguments,
const StringRef outputDirectoryArgument, const StringRef firstInput,
const FrontendOptions::ActionType requestedAction,
const llvm::opt::Arg *moduleNameArg, const StringRef suffix,
const bool hasTextualOutput)
: Args(args), Diags(diags), InputsAndOutputs(inputsAndOutputs),
OutputFileArguments(outputFileArguments),
OutputDirectoryArgument(outputDirectoryArgument), FirstInput(firstInput),
RequestedAction(requestedAction), ModuleNameArg(moduleNameArg),
Suffix(suffix), HasTextualOutput(hasTextualOutput) {}
Optional<std::vector<std::string>>
OutputFilesComputer::computeOutputFiles() const {
std::vector<std::string> outputFiles;
unsigned i = 0;
bool hadError = InputsAndOutputs.forEachInputProducingAMainOutputFile(
[&](const InputFile &input) -> bool {
StringRef outputArg = OutputFileArguments.empty()
? StringRef()
: StringRef(OutputFileArguments[i++]);
Optional<std::string> outputFile = computeOutputFile(outputArg, input);
if (!outputFile)
return true;
outputFiles.push_back(*outputFile);
return false;
});
return hadError ? None : Optional<std::vector<std::string>>(outputFiles);
}
Optional<std::string>
OutputFilesComputer::computeOutputFile(StringRef outputArg,
const InputFile &input) const {
// Return an empty string to signify no output.
// The frontend does not currently produce a diagnostic
// if a -o argument is present for such an action
// for instance swiftc -frontend -o foo -interpret foo.swift
if (!FrontendOptions::doesActionProduceOutput(RequestedAction))
return std::string();
if (!OutputDirectoryArgument.empty())
return deriveOutputFileForDirectory(input);
if (!outputArg.empty())
return outputArg.str();
return deriveOutputFileFromInput(input);
}
Optional<std::string>
OutputFilesComputer::deriveOutputFileFromInput(const InputFile &input) const {
if (input.file() == "-" || HasTextualOutput)
return std::string("-");
std::string baseName = determineBaseNameOfOutput(input);
if (baseName.empty()) {
// Assuming FrontendOptions::doesActionProduceOutput(RequestedAction)
Diags.diagnose(SourceLoc(), diag::error_no_output_filename_specified);
return None;
}
return deriveOutputFileFromParts("", baseName);
}
Optional<std::string> OutputFilesComputer::deriveOutputFileForDirectory(
const InputFile &input) const {
std::string baseName = determineBaseNameOfOutput(input);
if (baseName.empty()) {
Diags.diagnose(SourceLoc(), diag::error_implicit_output_file_is_directory,
OutputDirectoryArgument);
return None;
}
return deriveOutputFileFromParts(OutputDirectoryArgument, baseName);
}
std::string
OutputFilesComputer::determineBaseNameOfOutput(const InputFile &input) const {
std::string nameToStem =
input.isPrimary()
? input.file()
: ModuleNameArg ? ModuleNameArg->getValue() : FirstInput;
return llvm::sys::path::stem(nameToStem).str();
}
std::string
OutputFilesComputer::deriveOutputFileFromParts(StringRef dir,
StringRef base) const {
assert(!base.empty());
llvm::SmallString<128> path(dir);
llvm::sys::path::append(path, base);
llvm::sys::path::replace_extension(path, Suffix);
return path.str();
}
SupplementaryOutputPathsComputer::SupplementaryOutputPathsComputer(
const ArgList &args, DiagnosticEngine &diags,
const FrontendInputsAndOutputs &inputsAndOutputs,
ArrayRef<std::string> outputFiles, StringRef moduleName)
: Args(args), Diags(diags), InputsAndOutputs(inputsAndOutputs),
OutputFiles(outputFiles), ModuleName(moduleName),
RequestedAction(
ArgsToFrontendOptionsConverter::determineRequestedAction(Args)) {}
Optional<std::vector<SupplementaryOutputPaths>>
SupplementaryOutputPathsComputer::computeOutputPaths() const {
Optional<std::vector<SupplementaryOutputPaths>> pathsFromUser =
getSupplementaryOutputPathsFromArguments();
if (!pathsFromUser)
return None;
if (InputsAndOutputs.hasPrimaryInputs())
assert(OutputFiles.size() == pathsFromUser->size());
else if (InputsAndOutputs.isSingleThreadedWMO())
assert(OutputFiles.size() == pathsFromUser->size() &&
pathsFromUser->size() == 1);
else {
// Multi-threaded WMO is the exception
assert(OutputFiles.size() == InputsAndOutputs.inputCount() &&
pathsFromUser->size() == InputsAndOutputs.hasInputs()
? 1
: 0);
}
std::vector<SupplementaryOutputPaths> outputPaths;
unsigned i = 0;
bool hadError = InputsAndOutputs.forEachInputProducingSupplementaryOutput(
[&](const InputFile &input) -> bool {
if (auto suppPaths = computeOutputPathsForOneInput(
OutputFiles[i], (*pathsFromUser)[i], input)) {
++i;
outputPaths.push_back(*suppPaths);
return false;
}
return true;
});
if (hadError)
return None;
return outputPaths;
}
Optional<std::vector<SupplementaryOutputPaths>>
SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments()
const {
auto objCHeaderOutput = getSupplementaryFilenamesFromArguments(
options::OPT_emit_objc_header_path);
auto moduleOutput =
getSupplementaryFilenamesFromArguments(options::OPT_emit_module_path);
auto moduleDocOutput =
getSupplementaryFilenamesFromArguments(options::OPT_emit_module_doc_path);
auto dependenciesFile = getSupplementaryFilenamesFromArguments(
options::OPT_emit_dependencies_path);
auto referenceDependenciesFile = getSupplementaryFilenamesFromArguments(
options::OPT_emit_reference_dependencies_path);
auto serializedDiagnostics = getSupplementaryFilenamesFromArguments(
options::OPT_serialize_diagnostics_path);
auto fixItsOutput = getSupplementaryFilenamesFromArguments(
options::OPT_emit_fixits_path);
auto loadedModuleTrace = getSupplementaryFilenamesFromArguments(
options::OPT_emit_loaded_module_trace_path);
auto TBD = getSupplementaryFilenamesFromArguments(options::OPT_emit_tbd_path);
if (!objCHeaderOutput || !moduleOutput || !moduleDocOutput ||
!dependenciesFile || !referenceDependenciesFile ||
!serializedDiagnostics || !fixItsOutput || !loadedModuleTrace || !TBD) {
return None;
}
std::vector<SupplementaryOutputPaths> result;
const unsigned N =
InputsAndOutputs.countOfFilesProducingSupplementaryOutput();
for (unsigned i = 0; i < N; ++i) {
SupplementaryOutputPaths sop;
sop.ObjCHeaderOutputPath = (*objCHeaderOutput)[i];
sop.ModuleOutputPath = (*moduleOutput)[i];
sop.ModuleDocOutputPath = (*moduleDocOutput)[i];
sop.DependenciesFilePath = (*dependenciesFile)[i];
sop.ReferenceDependenciesFilePath = (*referenceDependenciesFile)[i];
sop.SerializedDiagnosticsPath = (*serializedDiagnostics)[i];
sop.FixItsOutputPath = (*fixItsOutput)[i];
sop.LoadedModuleTracePath = (*loadedModuleTrace)[i];
sop.TBDPath = (*TBD)[i];
result.push_back(sop);
}
return result;
}
// Extend this routine for filelists if/when we have them.
Optional<std::vector<std::string>>
SupplementaryOutputPathsComputer::getSupplementaryFilenamesFromArguments(
options::ID pathID) const {
std::vector<std::string> paths = Args.getAllArgValues(pathID);
const unsigned N =
InputsAndOutputs.countOfFilesProducingSupplementaryOutput();
if (paths.size() == N)
return paths;
if (paths.empty())
return std::vector<std::string>(N, std::string());
Diags.diagnose(SourceLoc(), diag::error_wrong_number_of_arguments,
Args.getLastArg(pathID)->getOption().getPrefixedName(), N,
paths.size());
return None;
}
Optional<SupplementaryOutputPaths>
SupplementaryOutputPathsComputer::computeOutputPathsForOneInput(
StringRef outputFile, const SupplementaryOutputPaths &pathsFromArguments,
const InputFile &input) const {
StringRef defaultSupplementaryOutputPathExcludingExtension =
deriveDefaultSupplementaryOutputPathExcludingExtension(outputFile, input);
using namespace options;
auto dependenciesFilePath = determineSupplementaryOutputFilename(
OPT_emit_dependencies, pathsFromArguments.DependenciesFilePath, "d", "",
defaultSupplementaryOutputPathExcludingExtension);
auto referenceDependenciesFilePath = determineSupplementaryOutputFilename(
OPT_emit_reference_dependencies,
pathsFromArguments.ReferenceDependenciesFilePath, "swiftdeps", "",
defaultSupplementaryOutputPathExcludingExtension);
auto serializedDiagnosticsPath = determineSupplementaryOutputFilename(
OPT_serialize_diagnostics, pathsFromArguments.SerializedDiagnosticsPath,
"dia", "", defaultSupplementaryOutputPathExcludingExtension);
// There is no non-path form of -emit-fixits-path
auto fixItsOutputPath = pathsFromArguments.FixItsOutputPath;
auto objcHeaderOutputPath = determineSupplementaryOutputFilename(
OPT_emit_objc_header, pathsFromArguments.ObjCHeaderOutputPath, "h", "",
defaultSupplementaryOutputPathExcludingExtension);
auto loadedModuleTracePath = determineSupplementaryOutputFilename(
OPT_emit_loaded_module_trace, pathsFromArguments.LoadedModuleTracePath,
"trace.json", "", defaultSupplementaryOutputPathExcludingExtension);
auto tbdPath = determineSupplementaryOutputFilename(
OPT_emit_tbd, pathsFromArguments.TBDPath, "tbd", "",
defaultSupplementaryOutputPathExcludingExtension);
auto moduleDocOutputPath = determineSupplementaryOutputFilename(
OPT_emit_module_doc, pathsFromArguments.ModuleDocOutputPath,
SERIALIZED_MODULE_DOC_EXTENSION, "",
defaultSupplementaryOutputPathExcludingExtension);
ID emitModuleOption;
std::string moduleExtension;
std::string mainOutputIfUsableForModule;
deriveModulePathParameters(emitModuleOption, moduleExtension,
mainOutputIfUsableForModule);
auto moduleOutputPath = determineSupplementaryOutputFilename(
emitModuleOption, pathsFromArguments.ModuleOutputPath, moduleExtension,
mainOutputIfUsableForModule,
defaultSupplementaryOutputPathExcludingExtension);
SupplementaryOutputPaths sop;
sop.ObjCHeaderOutputPath = objcHeaderOutputPath;
sop.ModuleOutputPath = moduleOutputPath;
sop.ModuleDocOutputPath = moduleDocOutputPath;
sop.DependenciesFilePath = dependenciesFilePath;
sop.ReferenceDependenciesFilePath = referenceDependenciesFilePath;
sop.SerializedDiagnosticsPath = serializedDiagnosticsPath;
sop.FixItsOutputPath = fixItsOutputPath;
sop.LoadedModuleTracePath = loadedModuleTracePath;
sop.TBDPath = tbdPath;
return sop;
}
StringRef SupplementaryOutputPathsComputer::
deriveDefaultSupplementaryOutputPathExcludingExtension(
StringRef outputFilename, const InputFile &input) const {
// Put the supplementary output file next to the output file if possible.
if (!outputFilename.empty() && outputFilename != "-")
return outputFilename;
if (input.isPrimary() && input.file() != "-")
return llvm::sys::path::filename(input.file());
return ModuleName;
}
std::string
SupplementaryOutputPathsComputer::determineSupplementaryOutputFilename(
options::ID emitOpt, std::string pathFromArguments, StringRef extension,
StringRef mainOutputIfUsable,
StringRef defaultSupplementaryOutputPathExcludingExtension) const {
if (!pathFromArguments.empty())
return pathFromArguments;
if (!Args.hasArg(emitOpt))
return std::string();
if (!mainOutputIfUsable.empty()) {
return mainOutputIfUsable.str();
}
llvm::SmallString<128> path(defaultSupplementaryOutputPathExcludingExtension);
llvm::sys::path::replace_extension(path, extension);
return path.str().str();
};
void SupplementaryOutputPathsComputer::deriveModulePathParameters(
options::ID &emitOption, std::string &extension,
std::string &mainOutputIfUsable) const {
bool isSIB = RequestedAction == FrontendOptions::ActionType::EmitSIB ||
RequestedAction == FrontendOptions::ActionType::EmitSIBGen;
emitOption = !isSIB ? options::OPT_emit_module
: RequestedAction == FrontendOptions::ActionType::EmitSIB
? options::OPT_emit_sib
: options::OPT_emit_sibgen;
bool canUseMainOutputForModule =
RequestedAction == FrontendOptions::ActionType::MergeModules ||
RequestedAction == FrontendOptions::ActionType::EmitModuleOnly || isSIB;
extension = isSIB ? SIB_EXTENSION : SERIALIZED_MODULE_EXTENSION;
mainOutputIfUsable =
canUseMainOutputForModule && !OutputFiles.empty() ? OutputFiles[0] : "";
}