forked from OSchip/llvm-project
186 lines
6.5 KiB
C++
186 lines
6.5 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/Common/Fortran-features.h"
|
|
#include "flang/Frontend/CompilerInvocation.h"
|
|
#include "flang/Frontend/TextDiagnosticPrinter.h"
|
|
#include "flang/Parser/parsing.h"
|
|
#include "flang/Parser/provenance.h"
|
|
#include "flang/Semantics/semantics.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()),
|
|
allCookedSources_(new Fortran::parser::AllCookedSources(*allSources_)),
|
|
parsing_(new Fortran::parser::Parsing(*allCookedSources_)) {
|
|
// TODO: This is a good default during development, but ultimately we should
|
|
// give the user the opportunity to specify this.
|
|
allSources_->set_encoding(Fortran::parser::Encoding::UTF_8);
|
|
}
|
|
|
|
CompilerInstance::~CompilerInstance() {
|
|
assert(outputFiles_.empty() && "Still output files in flight?");
|
|
}
|
|
|
|
void CompilerInstance::set_invocation(
|
|
std::shared_ptr<CompilerInvocation> value) {
|
|
invocation_ = std::move(value);
|
|
}
|
|
|
|
void CompilerInstance::set_semaOutputStream(raw_ostream &Value) {
|
|
ownedSemaOutputStream_.release();
|
|
semaOutputStream_ = &Value;
|
|
}
|
|
|
|
void CompilerInstance::set_semaOutputStream(
|
|
std::unique_ptr<raw_ostream> Value) {
|
|
ownedSemaOutputStream_.swap(Value);
|
|
semaOutputStream_ = ownedSemaOutputStream_.get();
|
|
}
|
|
|
|
// 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) {
|
|
|
|
// Get the path of the output file
|
|
std::string outputFilePath =
|
|
GetOutputFilePath(frontendOpts().outputFile, baseName, extension);
|
|
|
|
// Create the output file
|
|
llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>> os =
|
|
CreateOutputFileImpl(outputFilePath, binary);
|
|
|
|
// If successful, add the file to the list of tracked output files and
|
|
// return.
|
|
if (os) {
|
|
outputFiles_.emplace_back(OutputFile(outputFilePath));
|
|
return std::move(*os);
|
|
}
|
|
|
|
// If unsuccessful, issue an error and return Null
|
|
unsigned DiagID = diagnostics().getCustomDiagID(
|
|
clang::DiagnosticsEngine::Error, "unable to open output file '%0': '%1'");
|
|
diagnostics().Report(DiagID)
|
|
<< outputFilePath << llvm::errorToErrorCode(os.takeError()).message();
|
|
return nullptr;
|
|
}
|
|
|
|
llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>>
|
|
CompilerInstance::CreateOutputFileImpl(
|
|
llvm::StringRef outputFilePath, bool binary) {
|
|
|
|
// Creates the file descriptor for the output file
|
|
std::unique_ptr<llvm::raw_fd_ostream> os;
|
|
|
|
std::error_code error;
|
|
os.reset(new llvm::raw_fd_ostream(outputFilePath, error,
|
|
(binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_TextWithCRLF)));
|
|
if (error) {
|
|
return llvm::errorCodeToError(error);
|
|
}
|
|
|
|
// For seekable streams, just return the stream corresponding to the output
|
|
// file.
|
|
if (!binary || os->supportsSeeking())
|
|
return std::move(os);
|
|
|
|
// For non-seekable streams, we need to wrap the output stream into something
|
|
// that supports 'pwrite' and takes care of the ownership for us.
|
|
return std::make_unique<llvm::buffer_unique_ostream>(std::move(os));
|
|
}
|
|
|
|
void CompilerInstance::ClearOutputFiles(bool eraseFiles) {
|
|
for (OutputFile &of : outputFiles_)
|
|
if (!of.filename_.empty() && eraseFiles)
|
|
llvm::sys::fs::remove(of.filename_);
|
|
|
|
outputFiles_.clear();
|
|
}
|
|
|
|
bool CompilerInstance::ExecuteAction(FrontendAction &act) {
|
|
auto &invoc = this->invocation();
|
|
|
|
// Set some sane defaults for the frontend.
|
|
invoc.SetDefaultFortranOpts();
|
|
// Update the fortran options based on user-based input.
|
|
invoc.setFortranOpts();
|
|
// Set the encoding to read all input files in based on user input.
|
|
allSources_->set_encoding(invoc.fortranOpts().encoding);
|
|
// Create the semantics context and set semantic options.
|
|
invoc.setSemanticsOpts(*this->allCookedSources_);
|
|
|
|
// Run the frontend action `act` for every input file.
|
|
for (const FrontendInputFile &fif : frontendOpts().inputs) {
|
|
if (act.BeginSourceFile(*this, fif)) {
|
|
if (llvm::Error err = act.Execute()) {
|
|
consumeError(std::move(err));
|
|
}
|
|
act.EndSourceFile();
|
|
}
|
|
}
|
|
return !diagnostics().getClient()->getNumErrors();
|
|
}
|
|
|
|
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;
|
|
}
|