CarolineConcatto updated this revision to Diff 296066.
CarolineConcatto edited the summary of this revision.
CarolineConcatto added a comment.

address reviews comment


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
  llvm/include/llvm/Option/OptTable.h

Index: llvm/include/llvm/Option/OptTable.h
===================================================================
--- llvm/include/llvm/Option/OptTable.h
+++ llvm/include/llvm/Option/OptTable.h
@@ -243,7 +243,8 @@
   /// \param Usage - USAGE: Usage
   /// \param Title - OVERVIEW: Title
   /// \param FlagsToInclude - If non-zero, only include options with any
-  ///                         of these flags set.
+  ///                         of these flags set. Takes precedence over
+  ///                         FlagsToExclude.
   /// \param FlagsToExclude - Exclude options with any of these flags set.
   /// \param ShowAllAliases - If true, display all options including aliases
   ///                         that don't have help texts. By default, we display
Index: flang/unittests/Frontend/InputOutputTest.cpp
===================================================================
--- /dev/null
+++ flang/unittests/Frontend/InputOutputTest.cpp
@@ -0,0 +1,72 @@
+//===- 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 "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"
+#include "gtest/gtest.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 output files. Since we used an output buffer, there are no
+  // physical files to delete.
+  compInst.ClearOutputFiles(/*EraseFiles=*/false);
+}
+} // namespace
Index: flang/unittests/Frontend/CompilerInstanceTest.cpp
===================================================================
--- flang/unittests/Frontend/CompilerInstanceTest.cpp
+++ flang/unittests/Frontend/CompilerInstanceTest.cpp
@@ -9,7 +9,8 @@
 #include "flang/Frontend/CompilerInstance.h"
 #include "clang/Basic/DiagnosticOptions.h"
 #include "clang/Frontend/TextDiagnosticPrinter.h"
-
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gtest/gtest.h"
 
 using namespace llvm;
@@ -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
Index: flang/tools/flang-driver/fc1_main.cpp
===================================================================
--- flang/tools/flang-driver/fc1_main.cpp
+++ flang/tools/flang-driver/fc1_main.cpp
@@ -52,5 +52,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,13 +1,25 @@
-! RUN: %flang-new -help 2>&1 | FileCheck %s
-! RUN: %flang-new -fc1 -help 2>&1 | FileCheck %s
+! REQUIRES: new-flang-driver
+
+!--------------------------
+! FLANG DRIVER (flang-new)
+!--------------------------
+! RUN: %flang-new -help 2>&1 | FileCheck %s --check-prefix=CHECK
 ! 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=CHECK
 
-! CHECK:USAGE: flang-new
+!------------------
+! EXPECTED OUTPUT
+!------------------
+! CHECK: USAGE: flang-new
 ! CHECK-EMPTY:
 ! CHECK-NEXT:OPTIONS:
 ! CHECK-NEXT: -help     Display available options
+! CHECK-NEXT: -o <file> Write output to <file>
 ! CHECK-NEXT: --version Print version information
 
 ! 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,36 @@
+! 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: -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,53 @@
 //===----------------------------------------------------------------------===//
 
 #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",
+    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 +68,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 compiler instace
+  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
@@ -44,6 +44,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:
@@ -55,6 +58,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);
 
@@ -79,6 +83,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
@@ -8,14 +8,122 @@
 
 #include "flang/Frontend/CompilerInstance.h"
 #include "flang/Frontend/CompilerInvocation.h"
+#include "flang/Parser/provenance.h"
 #include "clang/Frontend/TextDiagnosticPrinter.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));
+}
+
+std::unique_ptr<llvm::raw_pwrite_stream>
+CompilerInstance::CreateDefaultOutputFile(bool binary, llvm::StringRef baseName,
+                                          llvm::StringRef extension) {
+  return CreateOutputFile(GetFrontendOpts().outputFile_, binary, baseName,
+                          extension);
+}
+
+std::unique_ptr<llvm::raw_pwrite_stream>
+CompilerInstance::CreateOutputFile(llvm::StringRef outputPath, bool binary,
+                                   llvm::StringRef baseName,
+                                   llvm::StringRef extension) {
+  std::string outputPathName;
+  std::error_code ec;
+
+  std::unique_ptr<llvm::raw_pwrite_stream> os = CreateOutputFile(
+      outputPath, ec, binary, baseName, extension, &outputPathName);
+
+  AddOutputFile(OutputFile(outputPathName));
+  return os;
+}
+
+std::unique_ptr<llvm::raw_pwrite_stream> CompilerInstance::CreateOutputFile(
+    llvm::StringRef outputPath, std::error_code &error, bool binary,
+    llvm::StringRef baseName, llvm::StringRef extension,
+    std::string *resultPathName) {
+
+  std::string outFile;
+
+  // Create the name of the output file
+  if (!outputPath.empty()) {
+    outFile = std::string(outputPath);
+  } else if (baseName == "-") {
+    outFile = "-";
+  } else if (!extension.empty()) {
+    llvm::SmallString<128> path(baseName);
+    llvm::sys::path::replace_extension(path, extension);
+    outFile = std::string(path.str());
+  } else {
+    outFile = "-";
+  }
+
+  if (resultPathName)
+    *resultPathName = outFile;
+
+  // Creates the file descriptor for the output file
+  std::unique_ptr<llvm::raw_fd_ostream> os;
+  std::string osFile;
+  if (!os) {
+    osFile = outFile;
+    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,12 +1,15 @@
 add_flang_library(flangFrontend
   CompilerInstance.cpp
   CompilerInvocation.cpp
+  FrontendAction.cpp
+  FrontendActions.cpp
   FrontendOptions.cpp
 
   DEPENDS
   clangBasic
 
   LINK_LIBS
+  FortranParser
   clangBasic
   clangDriver
   # TODO: Added to re-use clang's TextDiagnosticBuffer & TextDiagnosticPrinter.
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
@@ -15,7 +15,7 @@
 namespace Fortran::frontend {
 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,88 @@
     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_ 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 Null on error
+  std::unique_ptr<llvm::raw_pwrite_stream>
+  CreateDefaultOutputFile(bool binary = true, llvm::StringRef baseInput = "",
+                          llvm::StringRef extension = "");
+
+  /// Create a new output file and add it to the list of tracked output files.
+  //
+  /// If \p outputPath is empty, then createOutputFile will derive an output
+  /// path location as \p baseInput, with any suffix removed, and \p extension
+  /// appended.
+  ///
+  /// \param outputPath - If given, the path to the output file.
+  /// \param binary - The mode to open the file in.
+  /// \param baseInput - If the invocation contains no output file name (i.e.
+  /// outputFile_ 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 Null on error
+  std::unique_ptr<llvm::raw_pwrite_stream>
+  CreateOutputFile(llvm::StringRef outputPath, bool binary,
+                   llvm::StringRef baseInput, llvm::StringRef extension);
+
+  /// Create a new output file, optionally deriving the output path name.
+  ///
+  /// If \p outputPath is empty, then createOutputFile will derive an output
+  /// path location as \p baseInput, with any suffix removed, and \p extension
+  /// appended.
+  ///
+  /// \param outputPath - If given, the path to the output file.
+  /// \param error [out] - On failure, the error.
+  /// \param binary - The mode to open the file in.
+  /// \param baseName - The input file's name. Could be used to generate the
+  /// output file name.
+  /// \param extension - The extension to use for derived
+  /// output names.
+  /// \param resultPathName [out] - If given, the result path name
+  /// will be stored here on success.
+  std::unique_ptr<llvm::raw_pwrite_stream>
+  CreateOutputFile(llvm::StringRef outputPath, std::error_code &error,
+                   bool binary, llvm::StringRef baseName,
+                   llvm::StringRef extension, std::string *resultPathName);
+
   /// }
   /// @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 +217,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::NoClangOption;
+
   unsigned MissingArgIndex, MissingArgCount;
   InputArgList Args =
       getOpts().ParseArgs(ArgStrings, MissingArgIndex, MissingArgCount,
@@ -1571,8 +1576,11 @@
   if (!ShowHidden)
     ExcludedFlagsBitmask |= HelpHidden;
 
-  if (IsFlangMode())
+  if (IsFlangMode()) {
     IncludedFlagsBitmask |= options::FlangOption;
+  } else {
+    ExcludedFlagsBitmask |= options::NoClangOption;
+  }
 
   std::string Usage = llvm::formatv("{0} [options] file...", Name).str();
   getOpts().PrintHelp(llvm::outs(), Usage.c_str(), DriverTitle.c_str(),
@@ -1628,6 +1636,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::NoClangOption;
+
   // 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,9 @@
 // flang mode.
 def FlangOption : OptionFlag;
 
+// ClangOption - This option should not be accepted by Clang.
+def NoClangOption : OptionFlag;
+
 // FC1Option - This option should be accepted by flang -fc1.
 def FC1Option : OptionFlag;
 
@@ -2124,8 +2127,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. "
@@ -2795,7 +2798,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]>;
@@ -3526,6 +3530,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, NoClangOption]>, 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),
+  NoClangOption = (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