mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
428 lines
16 KiB
C++
428 lines
16 KiB
C++
//===--- 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,
|
|
SupplementaryOutputPaths &supplementaryOutputs) {
|
|
const auto requestedAction =
|
|
ArgsToFrontendOptionsConverter::determineRequestedAction(Args);
|
|
if (!FrontendOptions::doesActionProduceOutput(requestedAction))
|
|
return false;
|
|
|
|
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<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()
|
|
? 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();
|
|
}
|
|
|
|
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<SupplementaryOutputPaths>
|
|
SupplementaryOutputPathsComputer::computeOutputPaths() const {
|
|
Optional<std::vector<SupplementaryOutputPaths>> pathsFromUser =
|
|
getSupplementaryOutputPathsFromArguments();
|
|
if (!pathsFromUser)
|
|
return None;
|
|
|
|
return computeOutputPathsForOneInput(
|
|
OutputFiles[0], (*pathsFromUser)[0],
|
|
InputsAndOutputs.firstInputProducingOutput());
|
|
}
|
|
|
|
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 loadedModuleTrace = getSupplementaryFilenamesFromArguments(
|
|
options::OPT_emit_loaded_module_trace_path);
|
|
auto TBD = getSupplementaryFilenamesFromArguments(options::OPT_emit_tbd_path);
|
|
|
|
if (!objCHeaderOutput || !moduleOutput || !moduleDocOutput ||
|
|
!dependenciesFile || !referenceDependenciesFile ||
|
|
!serializedDiagnostics || !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.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.size() == 0)
|
|
return std::vector<std::string>(N, std::string());
|
|
|
|
Diags.diagnose(SourceLoc(), diag::error_wrong_number_of_arguments,
|
|
Args.getLastArg(pathID)->getOption().getName(), 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);
|
|
|
|
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.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 {
|
|
using namespace options;
|
|
|
|
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] : "";
|
|
}
|