Files
clang-p2996/flang/lib/Frontend/CompilerInstance.cpp
Caroline Concatto 4c5906cffd [Flang][Driver] Add infrastructure for basic frontend actions and file I/O
This patch introduces the dependencies required to read and manage input files
provided by the command line option. It also adds the infrastructure to create
and write to output files. The output is sent to either stdout or a file
(specified with the `-o` flag).

Separately, in order to be able to test the code for file I/O, it adds
infrastructure to create frontend actions. As a basic testable example, it adds
the `InputOutputTest` FrontendAction. The sole purpose of this action is to
read a file from the command line and print it either to stdout or the output
file.  This action is run by using the `-test-io` flag also introduced in this
patch (available for `flang-new` and `flang-new -fc1`). With this patch:
```
flang-new -test-io input-file.f90
```
will read input-file.f90 and print it in the output file.

The `InputOutputTest` frontend action has been introduced primarily to
facilitate testing. It is hidden from users (i.e. it's only displayed with
`--help-hidden`). Currently Clang doesn’t have an equivalent action.

`-test-io` is used to trigger the InputOutputTest action in the Flang frontend
driver. This patch makes sure that “flang-new” forwards it to “flang-new -fc1"
by creating a preprocessor job. However, in Flang.cpp, `-test-io` is passed to
“flang-new -fc1” without `-E`. This way we make sure that the preprocessor is
_not_ run in the frontend driver. This is the desired behaviour: `-test-io`
should only read the input file and print it to the output stream.

co-authored-by: Andrzej Warzynski <andrzej.warzynski@arm.com>

Differential Revision: https://reviews.llvm.org/D87989
2020-10-24 14:58:32 +01:00

157 lines
5.1 KiB
C++

//===--- CompilerInstance.cpp ---------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "flang/Frontend/CompilerInstance.h"
#include "flang/Frontend/CompilerInvocation.h"
#include "flang/Frontend/TextDiagnosticPrinter.h"
#include "flang/Parser/provenance.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
using namespace Fortran::frontend;
CompilerInstance::CompilerInstance()
: invocation_(new CompilerInvocation()),
allSources_(new Fortran::parser::AllSources()) {}
CompilerInstance::~CompilerInstance() {
assert(outputFiles_.empty() && "Still output files in flight?");
}
void CompilerInstance::SetInvocation(
std::shared_ptr<CompilerInvocation> value) {
invocation_ = std::move(value);
}
void CompilerInstance::AddOutputFile(OutputFile &&outFile) {
outputFiles_.push_back(std::move(outFile));
}
// Helper method to generate the path of the output file. The following logic
// applies:
// 1. If the user specifies the output file via `-o`, then use that (i.e.
// the outputFilename parameter).
// 2. If the user does not specify the name of the output file, derive it from
// the input file (i.e. inputFilename + extension)
// 3. If the output file is not specified and the input file is `-`, then set
// the output file to `-` as well.
static std::string GetOutputFilePath(llvm::StringRef outputFilename,
llvm::StringRef inputFilename, llvm::StringRef extension) {
// Output filename _is_ specified. Just use that.
if (!outputFilename.empty())
return std::string(outputFilename);
// Output filename _is not_ specified. Derive it from the input file name.
std::string outFile = "-";
if (!extension.empty() && (inputFilename != "-")) {
llvm::SmallString<128> path(inputFilename);
llvm::sys::path::replace_extension(path, extension);
outFile = std::string(path.str());
}
return outFile;
}
std::unique_ptr<llvm::raw_pwrite_stream>
CompilerInstance::CreateDefaultOutputFile(
bool binary, llvm::StringRef baseName, llvm::StringRef extension) {
std::string outputPathName;
std::error_code ec;
// Get the path of the output file
std::string outputFilePath =
GetOutputFilePath(GetFrontendOpts().outputFile_, baseName, extension);
// Create the output file
std::unique_ptr<llvm::raw_pwrite_stream> os =
CreateOutputFile(outputFilePath, ec, binary);
// Add the file to the list of tracked output files (provided it was created
// successfully)
if (os)
AddOutputFile(OutputFile(outputPathName));
return os;
}
std::unique_ptr<llvm::raw_pwrite_stream> CompilerInstance::CreateOutputFile(
llvm::StringRef outputFilePath, std::error_code &error, bool binary) {
// Creates the file descriptor for the output file
std::unique_ptr<llvm::raw_fd_ostream> os;
std::string osFile;
if (!os) {
osFile = outputFilePath;
os.reset(new llvm::raw_fd_ostream(osFile, error,
(binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_Text)));
if (error)
return nullptr;
}
// Return the stream corresponding to the output file.
// For non-seekable streams, wrap it in llvm::buffer_ostream first.
if (!binary || os->supportsSeeking())
return std::move(os);
assert(!nonSeekStream_ && "The non-seek stream has already been set!");
auto b = std::make_unique<llvm::buffer_ostream>(*os);
nonSeekStream_ = std::move(os);
return std::move(b);
}
void CompilerInstance::ClearOutputFiles(bool eraseFiles) {
for (OutputFile &of : outputFiles_)
if (!of.filename_.empty() && eraseFiles)
llvm::sys::fs::remove(of.filename_);
outputFiles_.clear();
nonSeekStream_.reset();
}
bool CompilerInstance::ExecuteAction(FrontendAction &act) {
// Connect Input to a CompileInstance
for (const FrontendInputFile &fif : GetFrontendOpts().inputs_) {
if (act.BeginSourceFile(*this, fif)) {
if (llvm::Error err = act.Execute()) {
consumeError(std::move(err));
}
act.EndSourceFile();
}
}
return true;
}
void CompilerInstance::CreateDiagnostics(
clang::DiagnosticConsumer *client, bool shouldOwnClient) {
diagnostics_ =
CreateDiagnostics(&GetDiagnosticOpts(), client, shouldOwnClient);
}
clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine>
CompilerInstance::CreateDiagnostics(clang::DiagnosticOptions *opts,
clang::DiagnosticConsumer *client, bool shouldOwnClient) {
clang::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID(
new clang::DiagnosticIDs());
clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags(
new clang::DiagnosticsEngine(diagID, opts));
// Create the diagnostic client for reporting errors or for
// implementing -verify.
if (client) {
diags->setClient(client, shouldOwnClient);
} else {
diags->setClient(new TextDiagnosticPrinter(llvm::errs(), opts));
}
return diags;
}