blob: 980d50070886963468e47dc98b9985cd16d18253 [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;
Optional<std::vector<std::string>> ArgsToFrontendOutputsConverter::convert() {
const auto requestedAction =
ArgsToFrontendOptionsConverter::determineRequestedAction(Args);
if (!FrontendOptions::doesActionProduceOutput(requestedAction))
return std::vector<std::string>();
if (auto ofc = OutputFilesComputer::create(Args, Diags, InputsAndOutputs))
return ofc->computeOutputFiles();
return None;
}
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()
? 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;
bool hadError = false;
unsigned i = 0;
InputsAndOutputs.forEachInputProducingAMainOutputFile(
[&](const InputFile &input) -> void {
StringRef outputArg = OutputFileArguments.empty()
? StringRef()
: StringRef(OutputFileArguments[i++]);
Optional<std::string> outputFile = computeOutputFile(outputArg, input);
if (!outputFile) {
hadError = true;
return;
}
outputFiles.push_back(*outputFile);
});
return hadError ? None : Optional<std::vector<std::string>>(outputFiles);
}
Optional<std::string>
OutputFilesComputer::computeOutputFile(StringRef outputArg,
const InputFile &input) const {
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 {
StringRef 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();
}