LinkOption = (1 << 13),
FlangOption = (1 << 14),
FC1Option = (1 << 15),
- Ignored = (1 << 16),
+ FlangOnlyOption = (1 << 16),
+ Ignored = (1 << 17),
};
enum ID {
// flang mode.
def FlangOption : OptionFlag;
+// FlangOnlyOption - This option should only be used by Flang (i.e. it is not
+// available for Clang)
+def FlangOnlyOption : OptionFlag;
+
// FC1Option - This option should be accepted by flang -fc1.
def FC1Option : OptionFlag;
Flags<[DriverOption]>,
HelpText<"Restore the default behavior of not embedding source text in DWARF debug sections">;
def headerpad__max__install__names : Joined<["-"], "headerpad_max_install_names">;
-def help : Flag<["-", "--"], "help">, Flags<[CC1Option,CC1AsOption, FC1Option, FlangOption]>,
- HelpText<"Display available options">;
+def help : Flag<["-", "--"], "help">, Flags<[CC1Option,CC1AsOption, FC1Option,
+ FlangOption]>, HelpText<"Display available options">;
def ibuiltininc : Flag<["-"], "ibuiltininc">,
HelpText<"Enable builtin #include directories even when -nostdinc is used "
"before or after -ibuiltininc. "
def nostdlib : Flag<["-"], "nostdlib">, Group<Link_Group>;
def nostdlibxx : Flag<["-"], "nostdlib++">;
def object : Flag<["-"], "object">;
-def o : JoinedOrSeparate<["-"], "o">, Flags<[DriverOption, RenderAsInput, CC1Option, CC1AsOption]>,
+def o : JoinedOrSeparate<["-"], "o">, Flags<[DriverOption, RenderAsInput,
+ CC1Option, CC1AsOption, FC1Option, FlangOption]>,
HelpText<"Write output to <file>">, MetaVarName<"<file>">;
def pagezero__size : JoinedOrSeparate<["-"], "pagezero_size">;
def pass_exit_codes : Flag<["-", "--"], "pass-exit-codes">, Flags<[Unsupported]>;
HelpText<"SYCL language standard to compile for.">, Values<"2017, 121, 1.2.1, sycl-1.2.1">;
//===----------------------------------------------------------------------===//
+// FlangOption and FC1 Options
+//===----------------------------------------------------------------------===//
+def test_io : Flag<["-"], "test-io">, Flags<[HelpHidden, FlangOption, FC1Option, FlangOnlyOption]>, Group<Action_Group>,
+ HelpText<"Run the InputOuputTest action. Use for development and testing only.">;
+
+//===----------------------------------------------------------------------===//
// CC1 Options
//===----------------------------------------------------------------------===//
std::tie(IncludedFlagsBitmask, ExcludedFlagsBitmask) =
getIncludeExcludeOptionFlagMasks(IsClCompatMode);
+ // Make sure that Flang-only options don't pollute the Clang output
+ // TODO: Make sure that Clang-only options don't pollute Flang output
+ if (!IsFlangMode())
+ ExcludedFlagsBitmask |= options::FlangOnlyOption;
+
unsigned MissingArgIndex, MissingArgCount;
InputArgList Args =
getOpts().ParseArgs(ArgStrings, MissingArgIndex, MissingArgCount,
if (IsFlangMode())
IncludedFlagsBitmask |= options::FlangOption;
+ else
+ ExcludedFlagsBitmask |= options::FlangOnlyOption;
std::string Usage = llvm::formatv("{0} [options] file...", Name).str();
getOpts().PrintHelp(llvm::outs(), Usage.c_str(), DriverTitle.c_str(),
unsigned int DisableFlags =
options::NoDriverOption | options::Unsupported | options::Ignored;
+ // Make sure that Flang-only options don't pollute the Clang output
+ // TODO: Make sure that Clang-only options don't pollute Flang output
+ if (!IsFlangMode())
+ DisableFlags |= options::FlangOnlyOption;
+
// Distinguish "--autocomplete=-someflag" and "--autocomplete=-someflag,"
// because the latter indicates that the user put space before pushing tab
// which should end up in a file completion.
CmdArgs.push_back("-fc1");
- CmdArgs.push_back("-triple");
- CmdArgs.push_back(Args.MakeArgString(TripleStr));
-
+ // TODO: Eventually all actions will require a triple (e.g. `-triple
+ // aarch64-unknown-linux-gnu`). However, `-triple` is currently not supported
+ // by `flang-new -fc1`, so we only add it selectively to actions that we
+ // don't support/execute just yet.
if (isa<PreprocessJobAction>(JA)) {
- CmdArgs.push_back("-E");
+ if (C.getArgs().hasArg(options::OPT_test_io))
+ CmdArgs.push_back("-test-io");
+ else
+ CmdArgs.push_back("-E");
} else if (isa<CompileJobAction>(JA) || isa<BackendJobAction>(JA)) {
+ CmdArgs.push_back("-triple");
+ CmdArgs.push_back(Args.MakeArgString(TripleStr));
if (JA.getType() == types::TY_Nothing) {
CmdArgs.push_back("-fsyntax-only");
} else if (JA.getType() == types::TY_AST) {
assert(false && "Unexpected output type!");
}
} else if (isa<AssembleJobAction>(JA)) {
+ CmdArgs.push_back("-triple");
+ CmdArgs.push_back(Args.MakeArgString(TripleStr));
CmdArgs.push_back("-emit-obj");
} else {
assert(false && "Unexpected action class for Flang tool.");
// Filter to compiler mode. When the compiler is run as a preprocessor then
// compilation is not an option.
// -S runs the compiler in Assembly listing mode.
+ // -test-io is used by Flang to run InputOutputTest action
if (Driver.CCCIsCPP() || DAL.getLastArg(options::OPT_E) ||
DAL.getLastArg(options::OPT__SLASH_EP) ||
DAL.getLastArg(options::OPT_M, options::OPT_MM) ||
- DAL.getLastArg(options::OPT__SLASH_P))
+ DAL.getLastArg(options::OPT__SLASH_P) ||
+ DAL.getLastArg(options::OPT_test_io))
LastPhase = phases::Preprocess;
// --precompile only runs up to precompilation.
// HELP-NOT: ast-dump
// HELP-NOT: driver-mode
+// Make sure that Flang-only options are not available in Clang
+// HELP-NOT: test-io
+
// RUN: %clang --help-hidden | FileCheck %s -check-prefix=HELP-HIDDEN
// HELP-HIDDEN: driver-mode
+// HELP-HIDDEN-NOT: test-io
// RUN: %clang -dumpversion | FileCheck %s -check-prefix=DUMPVERSION
// DUMPVERSION: {{[0-9]+\.[0-9.]+}}
#define LLVM_FLANG_FRONTEND_COMPILERINSTANCE_H
#include "flang/Frontend/CompilerInvocation.h"
+#include "flang/Frontend/FrontendAction.h"
+#include "flang/Parser/provenance.h"
+#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <memory>
/// The options used in this compiler instance.
std::shared_ptr<CompilerInvocation> invocation_;
+ /// Flang file manager.
+ std::shared_ptr<Fortran::parser::AllSources> allSources_;
+
/// The diagnostics engine instance.
llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnostics_;
+ /// Holds information about the output file.
+ struct OutputFile {
+ std::string filename_;
+ OutputFile(std::string inputFilename)
+ : filename_(std::move(inputFilename)) {}
+ };
+
+ /// Output stream that doesn't support seeking (e.g. terminal, pipe).
+ /// This stream is normally wrapped in buffer_ostream before being passed
+ /// to users (e.g. via CreateOutputFile).
+ std::unique_ptr<llvm::raw_fd_ostream> nonSeekStream_;
+
+ /// The list of active output files.
+ std::list<OutputFile> outputFiles_;
+
+ /// Holds the output stream provided by the user. Normally, users of
+ /// CompilerInstance will call CreateOutputFile to obtain/create an output
+ /// stream. If they want to provide their own output stream, this field will
+ /// facilitate this. It is optional and will normally be just a nullptr.
+ std::unique_ptr<llvm::raw_pwrite_stream> outputStream_;
+
public:
explicit CompilerInstance();
~CompilerInstance();
+
+ /// @name Compiler Invocation
+ /// {
+
CompilerInvocation &GetInvocation() {
assert(invocation_ && "Compiler instance has no invocation!");
return *invocation_;
};
+ /// Replace the current invocation.
+ void SetInvocation(std::shared_ptr<CompilerInvocation> value);
+
+ /// }
+ /// @name File manager
+ /// {
+
+ /// Return the current allSources.
+ Fortran::parser::AllSources &GetAllSources() const { return *allSources_; }
+
+ bool HasAllSources() const { return allSources_ != nullptr; }
+
+ /// }
+ /// @name High-Level Operations
+ /// {
+
+ /// Execute the provided action against the compiler's
+ /// CompilerInvocation object.
+ /// \param act - The action to execute.
+ /// \return - True on success.
+ bool ExecuteAction(FrontendAction &act);
+
/// }
/// @name Forwarding Methods
/// {
return *diagnostics_;
}
- /// SetDiagnostics - Replace the current diagnostics engine.
+ /// Replace the current diagnostics engine.
void SetDiagnostics(clang::DiagnosticsEngine *value);
clang::DiagnosticConsumer &GetDiagnosticClient() const {
return *diagnostics_;
}
+ /// {
+ /// @name Output Files
+ /// {
+
+ /// Add an output file onto the list of tracked output files.
+ ///
+ /// \param outFile - The output file info.
+ void AddOutputFile(OutputFile &&outFile);
+
+ /// Clear the output file list.
+ void ClearOutputFiles(bool eraseFiles);
+
+ /// Create the default output file (based on the invocation's options) and
+ /// add it to the list of tracked output files. If the name of the output
+ /// file is not provided, it is derived from the input file.
+ ///
+ /// \param binary The mode to open the file in.
+ /// \param baseInput If the invocation contains no output file name (i.e.
+ /// outputFile_ in FrontendOptions is empty), the input path
+ /// name to use for deriving the output path.
+ /// \param extension The extension to use for output names derived from
+ /// \p baseInput.
+ /// \return ostream for the output file or nullptr on error.
+ std::unique_ptr<llvm::raw_pwrite_stream> CreateDefaultOutputFile(
+ bool binary = true, llvm::StringRef baseInput = "",
+ llvm::StringRef extension = "");
+
+ /// Create a new output file
+ ///
+ /// \param outputPath The path to the output file.
+ /// \param error [out] On failure, the error.
+ /// \param binary The mode to open the file in.
+ /// \return ostream for the output file or nullptr on error.
+ std::unique_ptr<llvm::raw_pwrite_stream> CreateOutputFile(
+ llvm::StringRef outputPath, std::error_code &error, bool binary);
+
/// }
/// @name Construction Utility Methods
/// {
- /// Create a DiagnosticsEngine object with a the TextDiagnosticPrinter.
+ /// Create a DiagnosticsEngine object
///
- /// If no diagnostic client is provided, this creates a
- /// DiagnosticConsumer that is owned by the returned diagnostic
- /// object, if using directly the caller is responsible for
- /// releasing the returned DiagnosticsEngine's client eventually.
+ /// If no diagnostic client is provided, this method creates a
+ /// DiagnosticConsumer that is owned by the returned diagnostic object. If
+ /// using directly the caller is responsible for releasing the returned
+ /// DiagnosticsEngine's client eventually.
///
/// \param opts - The diagnostic options; note that the created text
/// diagnostic object contains a reference to these options.
///
- /// \param client If non-NULL, a diagnostic client that will be
- /// attached to (and, then, owned by) the returned DiagnosticsEngine
- /// object.
+ /// \param client - If non-NULL, a diagnostic client that will be attached to
+ /// (and optionally, depending on /p shouldOwnClient, owned by) the returned
+ /// DiagnosticsEngine object.
///
/// \return The new object on success, or null on failure.
static clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> CreateDiagnostics(
clang::DiagnosticConsumer *client = nullptr, bool shouldOwnClient = true);
void CreateDiagnostics(
clang::DiagnosticConsumer *client = nullptr, bool shouldOwnClient = true);
+
+ /// }
+ /// @name Output Stream Methods
+ /// {
+ void SetOutputStream(std::unique_ptr<llvm::raw_pwrite_stream> outStream) {
+ outputStream_ = std::move(outStream);
+ }
+
+ bool IsOutputStreamNull() { return (outputStream_ == nullptr); }
+
+ // Allow the frontend compiler to write in the output stream.
+ void WriteOutputStream(const std::string &message) {
+ *outputStream_ << message;
+ }
};
} // end namespace Fortran::frontend
class CompilerInvocationBase {
public:
- /// Options controlling the diagnostic engine.$
+ /// Options controlling the diagnostic engine.
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagnosticOpts_;
CompilerInvocationBase();
--- /dev/null
+//===- FrontendAction.h -----------------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Defines the flang::FrontendAction interface.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FLANG_FRONTEND_FRONTENDACTION_H
+#define LLVM_FLANG_FRONTEND_FRONTENDACTION_H
+
+#include "flang/Frontend/FrontendOptions.h"
+#include "llvm/Support/Error.h"
+
+namespace Fortran::frontend {
+class CompilerInstance;
+
+/// Abstract base class for actions which can be performed by the frontend.
+class FrontendAction {
+ FrontendInputFile currentInput_;
+ CompilerInstance *instance_;
+
+protected:
+ /// @name Implementation Action Interface
+ /// @{
+
+ /// Callback to run the program action, using the initialized
+ /// compiler instance.
+ virtual void ExecuteAction() = 0;
+
+ /// Callback at the end of processing a single input, to determine
+ /// if the output files should be erased or not.
+ ///
+ /// By default it returns true if a compiler error occurred.
+ virtual bool ShouldEraseOutputFiles();
+
+ /// @}
+
+public:
+ FrontendAction() : instance_(nullptr) {}
+ virtual ~FrontendAction() = default;
+
+ /// @name Compiler Instance Access
+ /// @{
+
+ CompilerInstance &GetCompilerInstance() const {
+ assert(instance_ && "Compiler instance not registered!");
+ return *instance_;
+ }
+
+ void SetCompilerInstance(CompilerInstance *value) { instance_ = value; }
+
+ /// @}
+ /// @name Current File Information
+ /// @{
+
+ const FrontendInputFile &GetCurrentInput() const { return currentInput_; }
+
+ llvm::StringRef GetCurrentFile() const {
+ assert(!currentInput_.IsEmpty() && "No current file!");
+ return currentInput_.GetFile();
+ }
+
+ llvm::StringRef GetCurrentFileOrBufferName() const {
+ assert(!currentInput_.IsEmpty() && "No current file!");
+ return currentInput_.IsFile()
+ ? currentInput_.GetFile()
+ : currentInput_.GetBuffer()->getBufferIdentifier();
+ }
+ void SetCurrentInput(const FrontendInputFile ¤tInput);
+
+ /// @}
+ /// @name Public Action Interface
+ /// @}
+
+ /// Prepare the action for processing the input file \p input.
+ ///
+ /// This is run after the options and frontend have been initialized,
+ /// but prior to executing any per-file processing.
+ /// \param ci - The compiler instance this action is being run from. The
+ /// action may store and use this object.
+ /// \param input - The input filename and kind.
+ /// \return True on success; on failure the compilation of this file should
+ bool BeginSourceFile(CompilerInstance &ci, const FrontendInputFile &input);
+
+ /// Run the action.
+ llvm::Error Execute();
+
+ /// Perform any per-file post processing, deallocate per-file
+ /// objects, and run statistics and output file cleanup code.
+ void EndSourceFile();
+};
+
+} // namespace Fortran::frontend
+
+#endif // LLVM_FLANG_FRONTEND_FRONTENDACTION_H
--- /dev/null
+//===- FrontendActions.h -----------------------------------------*- C++-*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FLANG_FRONTEND_FRONTENDACTIONS_H
+#define LLVM_FLANG_FRONTEND_FRONTENDACTIONS_H
+
+#include "flang/Frontend/FrontendAction.h"
+
+namespace Fortran::frontend {
+
+//===----------------------------------------------------------------------===//
+// Custom Consumer Actions
+//===----------------------------------------------------------------------===//
+
+class InputOutputTestAction : public FrontendAction {
+ void ExecuteAction() override;
+};
+
+} // namespace Fortran::frontend
+
+#endif // LLVM_FLANG_FRONTEND_FRONTENDACTIONS_H
#ifndef LLVM_FLANG_FRONTEND_FRONTENDOPTIONS_H
#define LLVM_FLANG_FRONTEND_FRONTENDOPTIONS_H
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/MemoryBuffer.h"
+
#include <cstdint>
#include <string>
+
namespace Fortran::frontend {
+enum ActionKind {
+ InvalidAction = 0,
+
+ /// -test-io mode
+ InputOutputTest,
+
+ // TODO: ADD flags as the Actions are implemented, e.g.
+ // RunPreprocessor, ParserSyntaxOnly, EmitLLVM, EmitLLVMOnly,
+ // EmitCodeGenOnly, EmitAssembly, (...)
+};
+
+inline const char *GetActionKindName(const ActionKind ak) {
+ switch (ak) {
+ case InputOutputTest:
+ return "InputOutputTest";
+ default:
+ return "<unknown ActionKind>";
+ // TODO:
+ // case RunPreprocessor:
+ // case ParserSyntaxOnly:
+ // case EmitLLVM:
+ // case EmitLLVMOnly:
+ // case EmitCodeGenOnly:
+ // (...)
+ }
+}
+
enum class Language : uint8_t {
Unknown,
/// and compile it to assembly or object code.
LLVM_IR,
- ///@{ Languages that the frontend can parse and compile.
+ /// @{ Languages that the frontend can parse and compile.
Fortran,
- ///@}
};
/// The kind of a file that we've been handed as an input.
bool IsUnknown() const { return lang_ == Language::Unknown; }
};
+/// An input file for the front end.
+class FrontendInputFile {
+ /// The file name, or "-" to read from standard input.
+ std::string file_;
+
+ /// The input, if it comes from a buffer rather than a file. This object
+ /// does not own the buffer, and the caller is responsible for ensuring
+ /// that it outlives any users.
+ const llvm::MemoryBuffer *buffer_ = nullptr;
+
+ /// The kind of input, atm it contains language
+ InputKind kind_;
+
+public:
+ FrontendInputFile() = default;
+ FrontendInputFile(llvm::StringRef file, InputKind kind)
+ : file_(file.str()), kind_(kind) {}
+ FrontendInputFile(const llvm::MemoryBuffer *buffer, InputKind kind)
+ : buffer_(buffer), kind_(kind) {}
+
+ InputKind GetKind() const { return kind_; }
+
+ bool IsEmpty() const { return file_.empty() && buffer_ == nullptr; }
+ bool IsFile() const { return !IsBuffer(); }
+ bool IsBuffer() const { return buffer_ != nullptr; }
+
+ llvm::StringRef GetFile() const {
+ assert(IsFile());
+ return file_;
+ }
+
+ const llvm::MemoryBuffer *GetBuffer() const {
+ assert(IsBuffer() && "Requested buffer_, but it is empty!");
+ return buffer_;
+ }
+};
+
/// FrontendOptions - Options for controlling the behavior of the frontend.
class FrontendOptions {
public:
/// Show the -version text.
unsigned showVersion_ : 1;
+ /// The input files and their types.
+ std::vector<FrontendInputFile> inputs_;
+
+ /// The output file, if any.
+ std::string outputFile_;
+
+ /// The frontend action to perform.
+ frontend::ActionKind programAction_;
+
public:
FrontendOptions() : showHelp_(false), showVersion_(false) {}
+
+ // Return the appropriate input kind for a file extension. For example,
+ /// "*.f" would return Language::Fortran.
+ ///
+ /// \return The input kind for the extension, or Language::Unknown if the
+ /// extension is not recognized.
+ static InputKind GetInputKindForExtension(llvm::StringRef extension);
};
} // namespace Fortran::frontend
namespace Fortran::frontend {
class CompilerInstance;
+class FrontendAction;
+
+/// Construct the FrontendAction of a compiler invocation based on the
+/// options specified for the compiler invocation.
+///
+/// \return - The created FrontendAction object
+std::unique_ptr<FrontendAction> CreateFrontendAction(CompilerInstance &ci);
/// ExecuteCompilerInvocation - Execute the given actions described by the
/// compiler invocation object in the given compiler instance.
add_flang_library(flangFrontend
CompilerInstance.cpp
CompilerInvocation.cpp
+ FrontendAction.cpp
+ FrontendActions.cpp
FrontendOptions.cpp
TextDiagnosticPrinter.cpp
TextDiagnosticBuffer.cpp
clangBasic
LINK_LIBS
+ FortranParser
clangBasic
clangDriver
#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()) {}
+CompilerInstance::CompilerInstance()
+ : invocation_(new CompilerInvocation()),
+ allSources_(new Fortran::parser::AllSources()) {}
-CompilerInstance::~CompilerInstance() = default;
+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) {
default: {
llvm_unreachable("Invalid option in group!");
}
+ case clang::driver::options::OPT_test_io:
+ opts.programAction_ = InputOutputTest;
+ break;
// TODO:
// case clang::driver::options::OPT_E:
// case clang::driver::options::OPT_emit_obj:
}
}
+ opts.outputFile_ = args.getLastArgValue(clang::driver::options::OPT_o);
opts.showHelp_ = args.hasArg(clang::driver::options::OPT_help);
opts.showVersion_ = args.hasArg(clang::driver::options::OPT_version);
<< a->getAsString(args) << a->getValue();
}
+ // Collect the input files and save them in our instance of FrontendOptions.
+ std::vector<std::string> inputs =
+ args.getAllArgValues(clang::driver::options::OPT_INPUT);
+ opts.inputs_.clear();
+ if (inputs.empty())
+ // '-' is the default input if none is given.
+ inputs.push_back("-");
+ for (unsigned i = 0, e = inputs.size(); i != e; ++i) {
+ InputKind ik = dashX;
+ if (ik.IsUnknown()) {
+ ik = FrontendOptions::GetInputKindForExtension(
+ llvm::StringRef(inputs[i]).rsplit('.').second);
+ if (ik.IsUnknown())
+ ik = Language::Unknown;
+ if (i == 0)
+ dashX = ik;
+ }
+
+ opts.inputs_.emplace_back(std::move(inputs[i]), ik);
+ }
return dashX;
}
--- /dev/null
+//===--- FrontendAction.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/FrontendAction.h"
+#include "flang/Frontend/CompilerInstance.h"
+#include "flang/Frontend/FrontendActions.h"
+#include "llvm/Support/Errc.h"
+
+using namespace Fortran::frontend;
+
+void FrontendAction::SetCurrentInput(const FrontendInputFile ¤tInput) {
+ this->currentInput_ = currentInput;
+}
+
+// Call this method if BeginSourceFile fails.
+// Deallocate compiler instance, input and output descriptors
+static void BeginSourceFileCleanUp(FrontendAction &fa, CompilerInstance &ci) {
+ ci.ClearOutputFiles(/*EraseFiles=*/true);
+ fa.SetCurrentInput(FrontendInputFile());
+ fa.SetCompilerInstance(nullptr);
+}
+
+bool FrontendAction::BeginSourceFile(
+ CompilerInstance &ci, const FrontendInputFile &realInput) {
+
+ FrontendInputFile input(realInput);
+ assert(!instance_ && "Already processing a source file!");
+ assert(!realInput.IsEmpty() && "Unexpected empty filename!");
+ SetCurrentInput(realInput);
+ SetCompilerInstance(&ci);
+ if (!ci.HasAllSources()) {
+ BeginSourceFileCleanUp(*this, ci);
+ return false;
+ }
+ return true;
+}
+
+bool FrontendAction::ShouldEraseOutputFiles() {
+ return GetCompilerInstance().getDiagnostics().hasErrorOccurred();
+}
+
+llvm::Error FrontendAction::Execute() {
+ ExecuteAction();
+ return llvm::Error::success();
+}
+
+void FrontendAction::EndSourceFile() {
+ CompilerInstance &ci = GetCompilerInstance();
+
+ // Cleanup the output streams, and erase the output files if instructed by the
+ // FrontendAction.
+ ci.ClearOutputFiles(/*EraseFiles=*/ShouldEraseOutputFiles());
+
+ SetCompilerInstance(nullptr);
+ SetCurrentInput(FrontendInputFile());
+}
--- /dev/null
+//===--- FrontendActions.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/FrontendActions.h"
+#include "flang/Common/Fortran-features.h"
+#include "flang/Common/default-kinds.h"
+#include "flang/Frontend/CompilerInstance.h"
+#include "flang/Parser/source.h"
+#include "clang/Serialization/PCHContainerOperations.h"
+
+using namespace Fortran::frontend;
+
+void InputOutputTestAction::ExecuteAction() {
+
+ // Get the name of the file from FrontendInputFile current.
+ std::string path{GetCurrentFileOrBufferName()};
+ std::string buf;
+ llvm::raw_string_ostream error_stream{buf};
+ bool binaryMode = true;
+
+ // Set/store input file info into CompilerInstance.
+ CompilerInstance &ci = GetCompilerInstance();
+ Fortran::parser::AllSources &allSources{ci.GetAllSources()};
+ const Fortran::parser::SourceFile *sf;
+ sf = allSources.Open(path, error_stream);
+ llvm::ArrayRef<char> fileContent = sf->content();
+
+ // Output file descriptor to receive the content of input file.
+ std::unique_ptr<llvm::raw_ostream> os;
+
+ // Do not write on the output file if using outputStream_.
+ if (ci.IsOutputStreamNull()) {
+ os = ci.CreateDefaultOutputFile(
+ binaryMode, GetCurrentFileOrBufferName(), "txt");
+ if (!os)
+ return;
+ (*os) << fileContent.data();
+ } else {
+ ci.WriteOutputStream(fileContent.data());
+ }
+}
//===----------------------------------------------------------------------===//
#include "flang/Frontend/FrontendOptions.h"
+#include "llvm/ADT/StringSwitch.h"
+
+using namespace Fortran::frontend;
+
+InputKind FrontendOptions::GetInputKindForExtension(llvm::StringRef extension) {
+ return llvm::StringSwitch<InputKind>(extension)
+ // TODO: Should match the list in flang/test/lit.cfg.py
+ // FIXME: Currently this API allows at most 9 items per case.
+ .Cases("f", "F", "f77", "f90", "F90", "f95", "F95", "ff95", "f18", "F18",
+ Language::Fortran)
+ .Default(Language::Unknown);
+}
clangBasic
LINK_LIBS
+ flangFrontend
clangBasic
clangDriver
//===----------------------------------------------------------------------===//
#include "flang/Frontend/CompilerInstance.h"
+#include "flang/Frontend/FrontendActions.h"
#include "clang/Driver/Options.h"
#include "llvm/Option/OptTable.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/BuryPointer.h"
#include "llvm/Support/CommandLine.h"
namespace Fortran::frontend {
+
+static std::unique_ptr<FrontendAction> CreateFrontendBaseAction(
+ CompilerInstance &ci) {
+
+ ActionKind ak = ci.GetFrontendOpts().programAction_;
+ switch (ak) {
+ case InputOutputTest:
+ return std::make_unique<InputOutputTestAction>();
+ break;
+ default:
+ break;
+ // TODO:
+ // case RunPreprocessor:
+ // case ParserSyntaxOnly:
+ // case EmitLLVM:
+ // case EmitLLVMOnly:
+ // case EmitCodeGenOnly:
+ // (...)
+ }
+ return 0;
+}
+
+std::unique_ptr<FrontendAction> CreateFrontendAction(CompilerInstance &ci) {
+ // Create the underlying action.
+ std::unique_ptr<FrontendAction> act = CreateFrontendBaseAction(ci);
+ if (!act)
+ return nullptr;
+
+ return act;
+}
bool ExecuteCompilerInvocation(CompilerInstance *flang) {
// Honor -help.
if (flang->GetFrontendOpts().showHelp_) {
clang::driver::getDriverOptTable().PrintHelp(llvm::outs(),
"flang-new -fc1 [options] file...", "LLVM 'Flang' Compiler",
/*Include=*/clang::driver::options::FC1Option,
- /*Exclude=*/0, /*ShowAllAliases=*/false);
+ /*Exclude=*/llvm::opt::DriverFlag::HelpHidden,
+ /*ShowAllAliases=*/false);
return true;
}
return true;
}
- return true;
+ // Create and execute the frontend action.
+ std::unique_ptr<FrontendAction> act(CreateFrontendAction(*flang));
+ if (!act)
+ return false;
+
+ bool success = flang->ExecuteAction(*act);
+ return success;
}
} // namespace Fortran::frontend
--- /dev/null
+! REQUIRES: new-flang-driver
+
+!--------------------------
+! FLANG DRIVER (flang-new)
+!--------------------------
+! RUN: %flang-new --help-hidden 2>&1 | FileCheck %s
+! RUN: not %flang-new -help-hidden 2>&1 | FileCheck %s --check-prefix=ERROR-FLANG
+
+!----------------------------------------
+! FLANG FRONTEND DRIVER (flang-new -fc1)
+!----------------------------------------
+! RUN: not %flang-new -fc1 --help-hidden 2>&1 | FileCheck %s --check-prefix=ERROR-FLANG-FC1
+! RUN: not %flang-new -fc1 -help-hidden 2>&1 | FileCheck %s --check-prefix=ERROR-FLANG-FC1
+
+!----------------------------------------------------
+! EXPECTED OUTPUT FOR FLANG DRIVER (flang-new)
+!----------------------------------------------------
+! CHECK:USAGE: flang-new
+! CHECK-EMPTY:
+! CHECK-NEXT:OPTIONS:
+! CHECK-NEXT: -fcolor-diagnostics Enable colors in diagnostics
+! CHECK-NEXT: -fno-color-diagnostics Disable colors in diagnostics
+! CHECK-NEXT: -help Display available options
+! CHECK-NEXT: -o <file> Write output to <file>
+! CHECK-NEXT: -test-io Run the InputOuputTest action. Use for development and testing only.
+! CHECK-NEXT: --version Print version information
+
+!-------------------------------------------------------------
+! EXPECTED OUTPUT FOR FLANG DRIVER (flang-new)
+!-------------------------------------------------------------
+! ERROR-FLANG: error: unknown argument '-help-hidden'; did you mean '--help-hidden'?
+
+!-------------------------------------------------------------
+! EXPECTED OUTPUT FOR FLANG FRONTEND DRIVER (flang-new -fc1)
+!-------------------------------------------------------------
+! Frontend driver -help-hidden is not supported
+! ERROR-FLANG-FC1: error: unknown argument: '{{.*}}'
+
+! REQUIRES: new-flang-driver
+
+!--------------------------
+! FLANG DRIVER (flang-new)
+!--------------------------
! RUN: %flang-new -help 2>&1 | FileCheck %s --check-prefix=HELP
-! RUN: %flang-new -fc1 -help 2>&1 | FileCheck %s --check-prefix=HELP-FC1
! RUN: not %flang-new -helps 2>&1 | FileCheck %s --check-prefix=ERROR
-! REQUIRES: new-flang-driver
+!----------------------------------------
+! FLANG FRONTEND DRIVER (flang-new -fc1)
+!----------------------------------------
+! RUN: %flang-new -fc1 -help 2>&1 | FileCheck %s --check-prefix=HELP-FC1
+! RUN: not %flang-new -fc1 -helps 2>&1 | FileCheck %s --check-prefix=ERROR
+!-----------------------------
+! EXPECTED OUTPUT (flang-new)
+!-----------------------------
! HELP:USAGE: flang-new
! HELP-EMPTY:
! HELP-NEXT:OPTIONS:
! HELP-NEXT: -fcolor-diagnostics Enable colors in diagnostics
! HELP-NEXT: -fno-color-diagnostics Disable colors in diagnostics
-! HELP-NEXT: -help Display available options
-! HELP-NEXT: --version Print version information
+! HELP-NEXT: -help Display available options
+! HELP-NEXT: -o <file> Write output to <file>
+! HELP-NEXT: --version Print version information
+!----------------------------------
+! EXPECTED OUTPUT (flang-new -fc1)
+!----------------------------------
! HELP-FC1:USAGE: flang-new
! HELP-FC1-EMPTY:
! HELP-FC1-NEXT:OPTIONS:
! HELP-FC1-NEXT: -help Display available options
+! HELP-FC1-NEXT: -o <file> Write output to <file>
! HELP-FC1-NEXT: --version Print version information
-! ERROR: flang-new: error: unknown argument '-helps'; did you mean '-help'
+!---------------
+! EXPECTED ERROR
+!---------------
+! ERROR: error: unknown argument '-helps'; did you mean '-help'
-! RUN: not %flang-new %s 2>&1 | FileCheck %s --check-prefix=ERROR-IMPLICIT
-! RUN: not %flang-new -emit-obj %s 2>&1 | FileCheck %s --check-prefix=ERROR-EXPLICIT
+! RUN: not %flang-new %s 2>&1 | FileCheck %s --check-prefix=ERROR
+! RUN: not %flang-new -emit-obj %s 2>&1 | FileCheck %s --check-prefix=ERROR
! RUN: not %flang-new -fc1 -emit-obj %s 2>&1 | FileCheck %s --check-prefix=ERROR-FC1
! REQUIRES: new-flang-driver
! creates a job that corresponds to `-emit-obj`. This option/action is
! not yet supported. Verify that this is correctly reported as error.
-! ERROR-IMPLICIT: error: unknown argument: '-triple'
-! ERROR-IMPLICIT: error: unknown argument: '-emit-obj'
-! ERROR-IMPLICIT: error: unknown argument: '-o'
-
-! ERROR-EXPLICIT: error: unknown argument: '-o'
+! ERROR: error: unknown argument: '-triple'
+! ERROR: error: unknown argument: '-emit-obj'
! ERROR-FC1: error: unknown argument: '-emit-obj'
--- /dev/null
+!This is a test file with a hello world in Fortran
+program hello
+ implicit none
+ write(*,*) 'Hello world!'
+end program hello
--- /dev/null
+! RUN: rm -rf %S/input-output-file.txt
+
+! REQUIRES: new-flang-driver
+
+!--------------------------
+! FLANG DRIVER (flang-new)
+!--------------------------
+! TEST 1: Print to stdout (implicit)
+! RUN: %flang-new -test-io %s 2>&1 | FileCheck %s
+! TEST 2: Print to stdout (explicit)
+! RUN: %flang-new -test-io -o - %s 2>&1 | FileCheck %s
+! TEST 3: Print to a file
+! RUN: %flang-new -test-io -o %t %s 2>&1 && FileCheck %s --input-file=%t
+
+!----------------------------------------
+! FLANG FRONTEND DRIVER (flang-new -fc1)
+!----------------------------------------
+! TEST 4: Write to a file (implicit)
+! RUN: %flang-new -fc1 -test-io %s 2>&1 && FileCheck %s --input-file=%S/input-output-file.txt
+! TEST 5: Write to a file (explicit)
+! RUN: %flang-new -fc1 -test-io -o %t %s 2>&1 && FileCheck %s --input-file=%t
+
+
+!-----------------------
+! EXPECTED OUTPUT
+!-----------------------
+! CHECK-LABEL: Program arithmetic
+! CHECK-NEXT: Integer :: i, j
+! CHECK-NEXT: i = 2; j = 3; i= i * j;
+! CHECK-NEXT: End Program arithmetic
+
+Program arithmetic
+ Integer :: i, j
+ i = 2; j = 3; i= i * j;
+End Program arithmetic
\ No newline at end of file
--- /dev/null
+! RUN: rm -rf %S/multiple-input-files.txt %S/Inputs/hello-world.txt
+
+! REQUIRES: new-flang-driver
+
+!--------------------------
+! FLANG DRIVER (flang-new)
+!--------------------------
+! TEST 1: Both input files are processed (output is printed to stdout)
+! RUN: %flang-new -test-io %s %S/Inputs/hello-world.f90 | FileCheck %s -check-prefix=flang-new
+
+! TEST 2: None of the files is processed (not possible to specify the output file when multiple input files are present)
+! RUN: not %flang-new -test-io -o - %S/Inputs/hello-world.f90 %s 2>&1 | FileCheck %s -check-prefix=ERROR
+! RUN: not %flang-new -test-io -o %t %S/Inputs/hello-world.f90 %s 2>&1 | FileCheck %s -check-prefix=ERROR
+
+!----------------------------------------
+! FLANG FRONTEND DRIVER (flang-new -fc1)
+!----------------------------------------
+! TEST 3: Both input files are processed
+! RUN: %flang-new -fc1 -test-io %S/Inputs/hello-world.f90 %s 2>&1 \
+! RUN: && FileCheck %s --input-file=%S/multiple-input-files.txt -check-prefix=flang-new-FC1-OUTPUT1
+
+! TEST 4: Only the last input file is processed
+! RUN: %flang-new -fc1 -test-io %S/Inputs/hello-world.f90 %s -o %t 2>&1 \
+! RUN: && FileCheck %s --input-file=%t -check-prefix=flang-new-FC1-OUTPUT1
+
+!-----------------------
+! EXPECTED OUTPUT
+!-----------------------
+! TEST 1: By default, `flang-new` prints the output from all input files to
+! stdout
+! flang-new-LABEL: Program arithmetic
+! flang-new-NEXT: Integer :: i, j
+! flang-new-NEXT: i = 2; j = 3; i= i * j;
+! flang-new-NEXT: End Program arithmetic
+! flang-new-NEXT: !This is a test file with a hello world in Fortran
+! flang-new-NEXT:program hello
+! flang-new-NEXT: implicit none
+! flang-new-NEXT: write(*,*) 'Hello world!'
+! flang-new-NEXT:end program hello
+
+
+! TEST 2: `-o` does not work for `flang-new` when multiple input files are present
+! ERROR:error: cannot specify -o when generating multiple output files
+
+
+! TEST 3 & TEST 4: Unless the output file is specified, `flang-new -fc1` generates one output file for every input file. If an
+! output file is specified (with `-o`), then only the last input file is processed.
+! flang-new-FC1-OUTPUT1-LABEL: Program arithmetic
+! flang-new-FC1-OUTPUT1-NEXT: Integer :: i, j
+! flang-new-FC1-OUTPUT1-NEXT: i = 2; j = 3; i= i * j;
+! flang-new-FC1-OUTPUT1-NEXT: End Program arithmetic
+! flang-new-FC1-OUTPUT1-NEXT: !This is a test file with a hello world in Fortran
+! flang-new-FC1-OUTPUT1-NEXT:program hello
+! flang-new-FC1-OUTPUT1-NEXT: implicit none
+! flang-new-FC1-OUTPUT1-NEXT: write(*,*) 'Hello world!'
+! flang-new-FC1-OUTPUT1-NEXT:end program hello
+
+
+Program arithmetic
+ Integer :: i, j
+ i = 2; j = 3; i= i * j;
+End Program arithmetic
if config.include_flang_new_driver_test:
config.available_features.add('new-flang-driver')
else:
- config.excludes.append('Flang-Driver')
+ config.excludes.append('Flang-Driver','Frontend')
# test_source_root: The root path where tests are located.
config.test_source_root = os.path.dirname(__file__)
// Execute the frontend actions.
success = ExecuteCompilerInvocation(flang.get());
+ // Delete output files to free Compiler Instance
+ flang->ClearOutputFiles(/*EraseFiles=*/false);
+
return !success;
}
add_flang_unittest(FlangFrontendTests
CompilerInstanceTest.cpp
+ InputOutputTest.cpp
)
target_link_libraries(FlangFrontendTests
clangBasic
clangFrontend
flangFrontend
- flangFrontendTool)
+ flangFrontendTool
+ FortranParser
+ )
#include "flang/Frontend/CompilerInstance.h"
#include "flang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Basic/DiagnosticOptions.h"
+#include "llvm/Support//FileSystem.h"
#include "gtest/gtest.h"
namespace {
+TEST(CompilerInstance, SanityCheckForFileManager) {
+ const char *inputSource = "InputSourceFile";
+ std::string inputFile = "buffer-file-test.f";
+ std::error_code ec;
+
+ // 1. Create the input file for the file manager
+ // AllSources (which is used to manage files inside every compiler instance),
+ // works with paths. This means that it requires a physical file. Create one.
+ std::unique_ptr<llvm::raw_fd_ostream> os{
+ new llvm::raw_fd_ostream(inputFile, ec, llvm::sys::fs::OF_None)};
+ if (ec)
+ FAIL() << "Failed to create the input file";
+
+ // Populate the input file with the pre-defined input and flush it.
+ *(os) << inputSource;
+ os.reset();
+
+ // Get the path of the input file
+ llvm::SmallString<64> cwd;
+ if (std::error_code ec = llvm::sys::fs::current_path(cwd))
+ FAIL() << "Failed to obtain the current working directory";
+ std::string testFilePath(cwd.c_str());
+ testFilePath += "/" + inputFile;
+
+ // 2. Set up CompilerInstance (i.e. specify the input file)
+ std::string buf;
+ llvm::raw_string_ostream error_stream{buf};
+ CompilerInstance compInst;
+ const Fortran::parser::SourceFile *sf =
+ compInst.GetAllSources().Open(testFilePath, error_stream);
+
+ // 3. Verify the content of the input file
+ // This is just a sanity check to make sure that CompilerInstance is capable
+ // of reading input files.
+ llvm::ArrayRef<char> fileContent = sf->content();
+ EXPECT_FALSE(fileContent.size() == 0);
+ EXPECT_TRUE(
+ llvm::StringRef(fileContent.data()).startswith("InputSourceFile"));
+
+ // 4. Delete the test file
+ ec = llvm::sys::fs::remove(inputFile);
+ if (ec)
+ FAIL() << "Failed to delete the test file";
+}
+
TEST(CompilerInstance, AllowDiagnosticLogWithUnownedDiagnosticConsumer) {
// 1. Set-up a basic DiagnosticConsumer
std::string diagnosticOutput;
--- /dev/null
+//===- unittests/Frontend/OutputStreamTest.cpp --- FrontendAction tests --===//
+//
+// 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 "gtest/gtest.h"
+#include "flang/Frontend/CompilerInstance.h"
+#include "flang/Frontend/CompilerInvocation.h"
+#include "flang/Frontend/FrontendOptions.h"
+#include "flang/FrontendTool/Utils.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace Fortran::frontend;
+
+namespace {
+
+TEST(FrontendAction, TestInputOutputTestAction) {
+ std::string inputFile = "io-file-test.f";
+ std::error_code ec;
+
+ // 1. Create the input file for the file manager
+ // AllSources (which is used to manage files inside every compiler instance),
+ // works with paths. This means that it requires a physical file. Create one.
+ std::unique_ptr<llvm::raw_fd_ostream> os{
+ new llvm::raw_fd_ostream(inputFile, ec, llvm::sys::fs::OF_None)};
+ if (ec)
+ FAIL() << "Failed to create the input file";
+
+ // Populate the input file with the pre-defined input and flush it.
+ *(os) << "End Program arithmetic";
+ os.reset();
+
+ // Get the path of the input file
+ llvm::SmallString<64> cwd;
+ if (std::error_code ec = llvm::sys::fs::current_path(cwd))
+ FAIL() << "Failed to obtain the current working directory";
+ std::string testFilePath(cwd.c_str());
+ testFilePath += "/" + inputFile;
+
+ // 2. Prepare the compiler (CompilerInvocation + CompilerInstance)
+ CompilerInstance compInst;
+ compInst.CreateDiagnostics();
+ auto invocation = std::make_shared<CompilerInvocation>();
+ invocation->GetFrontendOpts().programAction_ = InputOutputTest;
+ compInst.SetInvocation(std::move(invocation));
+ compInst.GetFrontendOpts().inputs_.push_back(
+ FrontendInputFile(/*File=*/testFilePath, Language::Fortran));
+
+ // 3. Set-up the output stream. Using output buffer wrapped as an output
+ // stream, as opposed to an actual file (or a file descriptor).
+ llvm::SmallVector<char, 256> outputFileBuffer;
+ std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream(
+ new llvm::raw_svector_ostream(outputFileBuffer));
+ compInst.SetOutputStream(std::move(outputFileStream));
+
+ // 4. Run the earlier defined FrontendAction
+ bool success = ExecuteCompilerInvocation(&compInst);
+
+ EXPECT_TRUE(success);
+ EXPECT_TRUE(!outputFileBuffer.empty());
+ EXPECT_TRUE(llvm::StringRef(outputFileBuffer.data())
+ .startswith("End Program arithmetic"));
+
+ // 5. Clear the input and the output files. Since we used an output buffer,
+ // there are no physical output files to delete.
+ ec = llvm::sys::fs::remove(inputFile);
+ if (ec)
+ FAIL() << "Failed to delete the test file";
+
+ compInst.ClearOutputFiles(/*EraseFiles=*/false);
+}
+} // namespace