mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
...since they don't need to be accessed from other libraries. No intended functionality change.
190 lines
6.5 KiB
C++
190 lines
6.5 KiB
C++
//===--- ArgsToFrontendInputsConverter.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 "ArgsToFrontendInputsConverter.h"
|
|
|
|
#include "ArgsToFrontendOutputsConverter.h"
|
|
#include "swift/AST/DiagnosticsFrontend.h"
|
|
#include "swift/Basic/Defer.h"
|
|
#include "swift/Frontend/FrontendOptions.h"
|
|
#include "swift/Option/Options.h"
|
|
#include "swift/Parse/Lexer.h"
|
|
#include "swift/Strings.h"
|
|
#include "llvm/Option/Arg.h"
|
|
#include "llvm/Option/ArgList.h"
|
|
#include "llvm/Option/Option.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/LineIterator.h"
|
|
#include "llvm/Support/Path.h"
|
|
|
|
using namespace swift;
|
|
using namespace llvm::opt;
|
|
|
|
ArgsToFrontendInputsConverter::ArgsToFrontendInputsConverter(
|
|
DiagnosticEngine &diags, const ArgList &args)
|
|
: Diags(diags), Args(args),
|
|
FilelistPathArg(args.getLastArg(options::OPT_filelist)),
|
|
PrimaryFilelistPathArg(args.getLastArg(options::OPT_primary_filelist)) {}
|
|
|
|
Optional<FrontendInputsAndOutputs> ArgsToFrontendInputsConverter::convert(
|
|
SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> *buffers) {
|
|
SWIFT_DEFER {
|
|
if (buffers) {
|
|
std::move(ConfigurationFileBuffers.begin(),
|
|
ConfigurationFileBuffers.end(),
|
|
std::back_inserter(*buffers));
|
|
// Clearing the original list of buffers isn't strictly necessary, but
|
|
// makes the behavior more sensible if we were to call convert() again.
|
|
ConfigurationFileBuffers.clear();
|
|
}
|
|
};
|
|
|
|
if (enforceFilelistExclusion())
|
|
return None;
|
|
|
|
if (FilelistPathArg ? readInputFilesFromFilelist()
|
|
: readInputFilesFromCommandLine())
|
|
return None;
|
|
Optional<std::set<StringRef>> primaryFiles = readPrimaryFiles();
|
|
if (!primaryFiles)
|
|
return None;
|
|
|
|
FrontendInputsAndOutputs result;
|
|
std::set<StringRef> unusedPrimaryFiles;
|
|
std::tie(result, unusedPrimaryFiles) =
|
|
createInputFilesConsumingPrimaries(*primaryFiles);
|
|
|
|
if (diagnoseUnusedPrimaryFiles(unusedPrimaryFiles))
|
|
return None;
|
|
|
|
// Must be set before iterating over inputs needing outputs.
|
|
result.setBypassBatchModeChecks(
|
|
Args.hasArg(options::OPT_bypass_batch_mode_checks));
|
|
|
|
return std::move(result);
|
|
}
|
|
|
|
bool ArgsToFrontendInputsConverter::enforceFilelistExclusion() {
|
|
if (Args.hasArg(options::OPT_INPUT) && FilelistPathArg) {
|
|
Diags.diagnose(SourceLoc(),
|
|
diag::error_cannot_have_input_files_with_file_list);
|
|
return true;
|
|
}
|
|
// The following is not strictly necessary, but the restriction makes
|
|
// it easier to understand a given command line:
|
|
if (Args.hasArg(options::OPT_primary_file) && PrimaryFilelistPathArg) {
|
|
Diags.diagnose(
|
|
SourceLoc(),
|
|
diag::error_cannot_have_primary_files_with_primary_file_list);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ArgsToFrontendInputsConverter::readInputFilesFromCommandLine() {
|
|
bool hadDuplicates = false;
|
|
for (const Arg *A :
|
|
Args.filtered(options::OPT_INPUT, options::OPT_primary_file)) {
|
|
hadDuplicates = addFile(A->getValue()) || hadDuplicates;
|
|
}
|
|
return false; // FIXME: Don't bail out for duplicates, too many tests depend
|
|
// on it.
|
|
}
|
|
|
|
bool ArgsToFrontendInputsConverter::readInputFilesFromFilelist() {
|
|
bool hadDuplicates = false;
|
|
bool hadError =
|
|
forAllFilesInFilelist(FilelistPathArg, [&](StringRef file) -> void {
|
|
hadDuplicates = addFile(file) || hadDuplicates;
|
|
});
|
|
if (hadError)
|
|
return true;
|
|
return false; // FIXME: Don't bail out for duplicates, too many tests depend
|
|
// on it.
|
|
}
|
|
|
|
bool ArgsToFrontendInputsConverter::forAllFilesInFilelist(
|
|
Arg const *const pathArg, llvm::function_ref<void(StringRef)> fn) {
|
|
if (!pathArg)
|
|
return false;
|
|
StringRef path = pathArg->getValue();
|
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> filelistBufferOrError =
|
|
llvm::MemoryBuffer::getFile(path);
|
|
if (!filelistBufferOrError) {
|
|
Diags.diagnose(SourceLoc(), diag::cannot_open_file, path,
|
|
filelistBufferOrError.getError().message());
|
|
return true;
|
|
}
|
|
for (auto file :
|
|
llvm::make_range(llvm::line_iterator(*filelistBufferOrError->get()),
|
|
llvm::line_iterator()))
|
|
fn(file);
|
|
ConfigurationFileBuffers.push_back(std::move(*filelistBufferOrError));
|
|
return false;
|
|
}
|
|
|
|
bool ArgsToFrontendInputsConverter::addFile(StringRef file) {
|
|
if (Files.insert(file))
|
|
return false;
|
|
Diags.diagnose(SourceLoc(), diag::error_duplicate_input_file, file);
|
|
return true;
|
|
}
|
|
|
|
Optional<std::set<StringRef>>
|
|
ArgsToFrontendInputsConverter::readPrimaryFiles() {
|
|
std::set<StringRef> primaryFiles;
|
|
for (const Arg *A : Args.filtered(options::OPT_primary_file))
|
|
primaryFiles.insert(A->getValue());
|
|
if (forAllFilesInFilelist(
|
|
PrimaryFilelistPathArg,
|
|
[&](StringRef file) -> void { primaryFiles.insert(file); }))
|
|
return None;
|
|
return primaryFiles;
|
|
}
|
|
|
|
std::pair<FrontendInputsAndOutputs, std::set<StringRef>>
|
|
ArgsToFrontendInputsConverter::createInputFilesConsumingPrimaries(
|
|
std::set<StringRef> primaryFiles) {
|
|
bool hasAnyPrimaryFiles = !primaryFiles.empty();
|
|
|
|
FrontendInputsAndOutputs result;
|
|
for (auto &file : Files) {
|
|
bool isPrimary = primaryFiles.count(file) > 0;
|
|
result.addInput(InputFile(file, isPrimary));
|
|
if (isPrimary)
|
|
primaryFiles.erase(file);
|
|
}
|
|
|
|
if (!Files.empty() && !hasAnyPrimaryFiles) {
|
|
Optional<std::vector<std::string>> userSuppliedNamesOrErr =
|
|
OutputFilesComputer::getOutputFilenamesFromCommandLineOrFilelist(Args,
|
|
Diags);
|
|
if (userSuppliedNamesOrErr && userSuppliedNamesOrErr->size() == 1)
|
|
result.setIsSingleThreadedWMO(true);
|
|
}
|
|
|
|
return {std::move(result), std::move(primaryFiles)};
|
|
}
|
|
|
|
bool ArgsToFrontendInputsConverter::diagnoseUnusedPrimaryFiles(
|
|
std::set<StringRef> primaryFiles) {
|
|
for (auto &file : primaryFiles) {
|
|
// Catch "swiftc -frontend -c -filelist foo -primary-file
|
|
// some-file-not-in-foo".
|
|
assert(FilelistPathArg && "Unused primary with no filelist");
|
|
Diags.diagnose(SourceLoc(), diag::error_primary_file_not_found, file,
|
|
FilelistPathArg->getValue());
|
|
}
|
|
return !primaryFiles.empty();
|
|
}
|