This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG4c5906cffd04: [Flang][Driver] Add infrastructure for basic 
frontend actions and file I/O (authored by CarolineConcatto, committed by 
awarzynski).

Changed prior to commit:
  https://reviews.llvm.org/D87989?vs=299033&id=300485#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87989/new/

https://reviews.llvm.org/D87989

Files:
  clang/include/clang/Driver/Options.h
  clang/include/clang/Driver/Options.td
  clang/lib/Driver/Driver.cpp
  clang/lib/Driver/ToolChains/Flang.cpp
  clang/lib/Driver/Types.cpp
  clang/test/Driver/immediate-options.c
  flang/include/flang/Frontend/CompilerInstance.h
  flang/include/flang/Frontend/CompilerInvocation.h
  flang/include/flang/Frontend/FrontendAction.h
  flang/include/flang/Frontend/FrontendActions.h
  flang/include/flang/Frontend/FrontendOptions.h
  flang/include/flang/FrontendTool/Utils.h
  flang/lib/Frontend/CMakeLists.txt
  flang/lib/Frontend/CompilerInstance.cpp
  flang/lib/Frontend/CompilerInvocation.cpp
  flang/lib/Frontend/FrontendAction.cpp
  flang/lib/Frontend/FrontendActions.cpp
  flang/lib/Frontend/FrontendOptions.cpp
  flang/lib/FrontendTool/CMakeLists.txt
  flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
  flang/test/Flang-Driver/driver-help-hidden.f90
  flang/test/Flang-Driver/driver-help.f90
  flang/test/Flang-Driver/emit-obj.f90
  flang/test/Frontend/Inputs/hello-world.f90
  flang/test/Frontend/input-output-file.f90
  flang/test/Frontend/multiple-input-files.f90
  flang/test/lit.cfg.py
  flang/tools/flang-driver/fc1_main.cpp
  flang/unittests/Frontend/CMakeLists.txt
  flang/unittests/Frontend/CompilerInstanceTest.cpp
  flang/unittests/Frontend/InputOutputTest.cpp

Index: flang/unittests/Frontend/InputOutputTest.cpp
===================================================================
--- /dev/null
+++ flang/unittests/Frontend/InputOutputTest.cpp
@@ -0,0 +1,76 @@
+//===- 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
Index: flang/unittests/Frontend/CompilerInstanceTest.cpp
===================================================================
--- flang/unittests/Frontend/CompilerInstanceTest.cpp
+++ flang/unittests/Frontend/CompilerInstanceTest.cpp
@@ -9,6 +9,7 @@
 #include "flang/Frontend/CompilerInstance.h"
 #include "flang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Basic/DiagnosticOptions.h"
+#include "llvm/Support//FileSystem.h"
 
 #include "gtest/gtest.h"
 
@@ -17,6 +18,51 @@
 
 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;
Index: flang/unittests/Frontend/CMakeLists.txt
===================================================================
--- flang/unittests/Frontend/CMakeLists.txt
+++ flang/unittests/Frontend/CMakeLists.txt
@@ -1,5 +1,6 @@
 add_flang_unittest(FlangFrontendTests
   CompilerInstanceTest.cpp
+  InputOutputTest.cpp
 )
 
 target_link_libraries(FlangFrontendTests
@@ -7,4 +8,6 @@
   clangBasic
   clangFrontend
   flangFrontend
-  flangFrontendTool)
+  flangFrontendTool
+  FortranParser
+  )
Index: flang/tools/flang-driver/fc1_main.cpp
===================================================================
--- flang/tools/flang-driver/fc1_main.cpp
+++ flang/tools/flang-driver/fc1_main.cpp
@@ -56,5 +56,8 @@
   // Execute the frontend actions.
   success = ExecuteCompilerInvocation(flang.get());
 
+  // Delete output files to free Compiler Instance
+  flang->ClearOutputFiles(/*EraseFiles=*/false);
+
   return !success;
 }
Index: flang/test/lit.cfg.py
===================================================================
--- flang/test/lit.cfg.py
+++ flang/test/lit.cfg.py
@@ -43,7 +43,7 @@
 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__)
Index: flang/test/Frontend/multiple-input-files.f90
===================================================================
--- /dev/null
+++ flang/test/Frontend/multiple-input-files.f90
@@ -0,0 +1,62 @@
+! 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
Index: flang/test/Frontend/input-output-file.f90
===================================================================
--- /dev/null
+++ flang/test/Frontend/input-output-file.f90
@@ -0,0 +1,35 @@
+! 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
Index: flang/test/Frontend/Inputs/hello-world.f90
===================================================================
--- /dev/null
+++ flang/test/Frontend/Inputs/hello-world.f90
@@ -0,0 +1,5 @@
+!This is a test file with a hello world in Fortran
+program hello
+  implicit none
+  write(*,*) 'Hello world!'
+end program hello
Index: flang/test/Flang-Driver/emit-obj.f90
===================================================================
--- flang/test/Flang-Driver/emit-obj.f90
+++ flang/test/Flang-Driver/emit-obj.f90
@@ -1,5 +1,5 @@
-! 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
@@ -8,10 +8,7 @@
 ! 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'
Index: flang/test/Flang-Driver/driver-help.f90
===================================================================
--- flang/test/Flang-Driver/driver-help.f90
+++ flang/test/Flang-Driver/driver-help.f90
@@ -1,21 +1,40 @@
+! 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'
Index: flang/test/Flang-Driver/driver-help-hidden.f90
===================================================================
--- /dev/null
+++ flang/test/Flang-Driver/driver-help-hidden.f90
@@ -0,0 +1,38 @@
+! 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: '{{.*}}'
+
Index: flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
===================================================================
--- flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -12,18 +12,52 @@
 //===----------------------------------------------------------------------===//
 
 #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;
   }
 
@@ -33,7 +67,13 @@
     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
Index: flang/lib/FrontendTool/CMakeLists.txt
===================================================================
--- flang/lib/FrontendTool/CMakeLists.txt
+++ flang/lib/FrontendTool/CMakeLists.txt
@@ -5,6 +5,7 @@
   clangBasic
 
   LINK_LIBS
+  flangFrontend
   clangBasic
   clangDriver
 
Index: flang/lib/Frontend/FrontendOptions.cpp
===================================================================
--- flang/lib/Frontend/FrontendOptions.cpp
+++ flang/lib/Frontend/FrontendOptions.cpp
@@ -7,3 +7,15 @@
 //===----------------------------------------------------------------------===//
 
 #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);
+}
Index: flang/lib/Frontend/FrontendActions.cpp
===================================================================
--- /dev/null
+++ flang/lib/Frontend/FrontendActions.cpp
@@ -0,0 +1,45 @@
+//===--- 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());
+  }
+}
Index: flang/lib/Frontend/FrontendAction.cpp
===================================================================
--- /dev/null
+++ flang/lib/Frontend/FrontendAction.cpp
@@ -0,0 +1,61 @@
+//===--- 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 &currentInput) {
+  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());
+}
Index: flang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- flang/lib/Frontend/CompilerInvocation.cpp
+++ flang/lib/Frontend/CompilerInvocation.cpp
@@ -87,6 +87,9 @@
     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:
@@ -98,6 +101,7 @@
     }
   }
 
+  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);
 
@@ -122,6 +126,26 @@
           << 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;
 }
 
Index: flang/lib/Frontend/CompilerInstance.cpp
===================================================================
--- flang/lib/Frontend/CompilerInstance.cpp
+++ flang/lib/Frontend/CompilerInstance.cpp
@@ -9,13 +9,127 @@
 #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) {
Index: flang/lib/Frontend/CMakeLists.txt
===================================================================
--- flang/lib/Frontend/CMakeLists.txt
+++ flang/lib/Frontend/CMakeLists.txt
@@ -1,6 +1,8 @@
 add_flang_library(flangFrontend
   CompilerInstance.cpp
   CompilerInvocation.cpp
+  FrontendAction.cpp
+  FrontendActions.cpp
   FrontendOptions.cpp
   TextDiagnosticPrinter.cpp
   TextDiagnosticBuffer.cpp
@@ -10,6 +12,7 @@
   clangBasic
 
   LINK_LIBS
+  FortranParser
   clangBasic
   clangDriver
 
Index: flang/include/flang/FrontendTool/Utils.h
===================================================================
--- flang/include/flang/FrontendTool/Utils.h
+++ flang/include/flang/FrontendTool/Utils.h
@@ -17,6 +17,13 @@
 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.
Index: flang/include/flang/Frontend/FrontendOptions.h
===================================================================
--- flang/include/flang/Frontend/FrontendOptions.h
+++ flang/include/flang/Frontend/FrontendOptions.h
@@ -8,10 +8,41 @@
 #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,
 
@@ -19,9 +50,8 @@
   /// 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.
@@ -41,6 +71,43 @@
   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:
@@ -50,8 +117,24 @@
   /// 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
 
Index: flang/include/flang/Frontend/FrontendActions.h
===================================================================
--- /dev/null
+++ flang/include/flang/Frontend/FrontendActions.h
@@ -0,0 +1,26 @@
+//===- 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
Index: flang/include/flang/Frontend/FrontendAction.h
===================================================================
--- /dev/null
+++ flang/include/flang/Frontend/FrontendAction.h
@@ -0,0 +1,101 @@
+//===- 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 &currentInput);
+
+  /// @}
+  /// @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
Index: flang/include/flang/Frontend/CompilerInvocation.h
===================================================================
--- flang/include/flang/Frontend/CompilerInvocation.h
+++ flang/include/flang/Frontend/CompilerInvocation.h
@@ -24,7 +24,7 @@
 
 class CompilerInvocationBase {
 public:
-  /// Options controlling the diagnostic engine.$
+  /// Options controlling the diagnostic engine.
   llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagnosticOpts_;
 
   CompilerInvocationBase();
Index: flang/include/flang/Frontend/CompilerInstance.h
===================================================================
--- flang/include/flang/Frontend/CompilerInstance.h
+++ flang/include/flang/Frontend/CompilerInstance.h
@@ -9,6 +9,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>
@@ -20,18 +23,68 @@
   /// 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
   /// {
@@ -60,7 +113,7 @@
     return *diagnostics_;
   }
 
-  /// SetDiagnostics - Replace the current diagnostics engine.
+  /// Replace the current diagnostics engine.
   void SetDiagnostics(clang::DiagnosticsEngine *value);
 
   clang::DiagnosticConsumer &GetDiagnosticClient() const {
@@ -75,23 +128,59 @@
     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(
@@ -99,6 +188,20 @@
       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
Index: clang/test/Driver/immediate-options.c
===================================================================
--- clang/test/Driver/immediate-options.c
+++ clang/test/Driver/immediate-options.c
@@ -3,8 +3,12 @@
 // 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.]+}}
Index: clang/lib/Driver/Types.cpp
===================================================================
--- clang/lib/Driver/Types.cpp
+++ clang/lib/Driver/Types.cpp
@@ -325,10 +325,12 @@
   // 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.
Index: clang/lib/Driver/ToolChains/Flang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Flang.cpp
+++ clang/lib/Driver/ToolChains/Flang.cpp
@@ -30,12 +30,18 @@
 
   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) {
@@ -52,6 +58,8 @@
       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.");
Index: clang/lib/Driver/Driver.cpp
===================================================================
--- clang/lib/Driver/Driver.cpp
+++ clang/lib/Driver/Driver.cpp
@@ -211,6 +211,11 @@
   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,
@@ -1573,6 +1578,8 @@
 
   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(),
@@ -1628,6 +1635,11 @@
   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.
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -60,6 +60,10 @@
 // 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;
 
@@ -2130,8 +2134,8 @@
     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. "
@@ -2811,7 +2815,8 @@
 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]>;
@@ -3546,6 +3551,12 @@
 def sycl_std_EQ : Joined<["-"], "sycl-std=">, Group<sycl_Group>, Flags<[CC1Option, NoArgumentUnused, CoreOption]>,
   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
 //===----------------------------------------------------------------------===//
Index: clang/include/clang/Driver/Options.h
===================================================================
--- clang/include/clang/Driver/Options.h
+++ clang/include/clang/Driver/Options.h
@@ -36,7 +36,8 @@
   LinkOption = (1 << 13),
   FlangOption = (1 << 14),
   FC1Option = (1 << 15),
-  Ignored = (1 << 16),
+  FlangOnlyOption = (1 << 16),
+  Ignored = (1 << 17),
 };
 
 enum ID {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to