1 //===--- CompilerInstance.cpp ---------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "flang/Frontend/CompilerInstance.h"
10 #include "flang/Frontend/CompilerInvocation.h"
11 #include "flang/Frontend/TextDiagnosticPrinter.h"
12 #include "flang/Parser/parsing.h"
13 #include "flang/Parser/provenance.h"
14 #include "llvm/Support/Errc.h"
15 #include "llvm/Support/Error.h"
16 #include "llvm/Support/FileSystem.h"
17 #include "llvm/Support/Path.h"
18 #include "llvm/Support/raw_ostream.h"
19 
20 using namespace Fortran::frontend;
21 
CompilerInstance()22 CompilerInstance::CompilerInstance()
23     : invocation_(new CompilerInvocation()),
24       allSources_(new Fortran::parser::AllSources()),
25       allCookedSources_(new Fortran::parser::AllCookedSources(*allSources_)),
26       parsing_(new Fortran::parser::Parsing(*allCookedSources_)) {
27 
28   // TODO: This is a good default during development, but ultimately we should
29   // give the user the opportunity to specify this.
30   allSources_->set_encoding(Fortran::parser::Encoding::UTF_8);
31 }
32 
~CompilerInstance()33 CompilerInstance::~CompilerInstance() {
34   assert(outputFiles_.empty() && "Still output files in flight?");
35 }
36 
set_invocation(std::shared_ptr<CompilerInvocation> value)37 void CompilerInstance::set_invocation(
38     std::shared_ptr<CompilerInvocation> value) {
39   invocation_ = std::move(value);
40 }
41 
AddOutputFile(OutputFile && outFile)42 void CompilerInstance::AddOutputFile(OutputFile &&outFile) {
43   outputFiles_.push_back(std::move(outFile));
44 }
45 
46 // Helper method to generate the path of the output file. The following logic
47 // applies:
48 // 1. If the user specifies the output file via `-o`, then use that (i.e.
49 //    the outputFilename parameter).
50 // 2. If the user does not specify the name of the output file, derive it from
51 //    the input file (i.e. inputFilename + extension)
52 // 3. If the output file is not specified and the input file is `-`, then set
53 //    the output file to `-` as well.
GetOutputFilePath(llvm::StringRef outputFilename,llvm::StringRef inputFilename,llvm::StringRef extension)54 static std::string GetOutputFilePath(llvm::StringRef outputFilename,
55     llvm::StringRef inputFilename, llvm::StringRef extension) {
56 
57   // Output filename _is_ specified. Just use that.
58   if (!outputFilename.empty())
59     return std::string(outputFilename);
60 
61   // Output filename _is not_ specified. Derive it from the input file name.
62   std::string outFile = "-";
63   if (!extension.empty() && (inputFilename != "-")) {
64     llvm::SmallString<128> path(inputFilename);
65     llvm::sys::path::replace_extension(path, extension);
66     outFile = std::string(path.str());
67   }
68 
69   return outFile;
70 }
71 
72 std::unique_ptr<llvm::raw_pwrite_stream>
CreateDefaultOutputFile(bool binary,llvm::StringRef baseName,llvm::StringRef extension)73 CompilerInstance::CreateDefaultOutputFile(
74     bool binary, llvm::StringRef baseName, llvm::StringRef extension) {
75   std::string outputPathName;
76   std::error_code ec;
77 
78   // Get the path of the output file
79   std::string outputFilePath =
80       GetOutputFilePath(frontendOpts().outputFile_, baseName, extension);
81 
82   // Create the output file
83   std::unique_ptr<llvm::raw_pwrite_stream> os =
84       CreateOutputFile(outputFilePath, ec, binary);
85 
86   // Add the file to the list of tracked output files (provided it was created
87   // successfully)
88   if (os)
89     AddOutputFile(OutputFile(outputPathName));
90 
91   return os;
92 }
93 
CreateOutputFile(llvm::StringRef outputFilePath,std::error_code & error,bool binary)94 std::unique_ptr<llvm::raw_pwrite_stream> CompilerInstance::CreateOutputFile(
95     llvm::StringRef outputFilePath, std::error_code &error, bool binary) {
96 
97   // Creates the file descriptor for the output file
98   std::unique_ptr<llvm::raw_fd_ostream> os;
99   std::string osFile;
100   if (!os) {
101     osFile = outputFilePath;
102     os.reset(new llvm::raw_fd_ostream(osFile, error,
103         (binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_Text)));
104     if (error)
105       return nullptr;
106   }
107 
108   // Return the stream corresponding to the output file.
109   // For non-seekable streams, wrap it in llvm::buffer_ostream first.
110   if (!binary || os->supportsSeeking())
111     return std::move(os);
112 
113   assert(!nonSeekStream_ && "The non-seek stream has already been set!");
114   auto b = std::make_unique<llvm::buffer_ostream>(*os);
115   nonSeekStream_ = std::move(os);
116   return std::move(b);
117 }
118 
ClearOutputFiles(bool eraseFiles)119 void CompilerInstance::ClearOutputFiles(bool eraseFiles) {
120   for (OutputFile &of : outputFiles_)
121     if (!of.filename_.empty() && eraseFiles)
122       llvm::sys::fs::remove(of.filename_);
123 
124   outputFiles_.clear();
125   nonSeekStream_.reset();
126 }
127 
ExecuteAction(FrontendAction & act)128 bool CompilerInstance::ExecuteAction(FrontendAction &act) {
129   // Set some sane defaults for the frontend.
130   // TODO: Instead of defaults we should be setting these options based on the
131   // user input.
132   this->invocation().SetDefaultFortranOpts();
133 
134   // Connect Input to a CompileInstance
135   for (const FrontendInputFile &fif : frontendOpts().inputs_) {
136     if (act.BeginSourceFile(*this, fif)) {
137       if (llvm::Error err = act.Execute()) {
138         consumeError(std::move(err));
139       }
140       act.EndSourceFile();
141     }
142   }
143   return true;
144 }
145 
CreateDiagnostics(clang::DiagnosticConsumer * client,bool shouldOwnClient)146 void CompilerInstance::CreateDiagnostics(
147     clang::DiagnosticConsumer *client, bool shouldOwnClient) {
148   diagnostics_ =
149       CreateDiagnostics(&GetDiagnosticOpts(), client, shouldOwnClient);
150 }
151 
152 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine>
CreateDiagnostics(clang::DiagnosticOptions * opts,clang::DiagnosticConsumer * client,bool shouldOwnClient)153 CompilerInstance::CreateDiagnostics(clang::DiagnosticOptions *opts,
154     clang::DiagnosticConsumer *client, bool shouldOwnClient) {
155   clang::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID(
156       new clang::DiagnosticIDs());
157   clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags(
158       new clang::DiagnosticsEngine(diagID, opts));
159 
160   // Create the diagnostic client for reporting errors or for
161   // implementing -verify.
162   if (client) {
163     diags->setClient(client, shouldOwnClient);
164   } else {
165     diags->setClient(new TextDiagnosticPrinter(llvm::errs(), opts));
166   }
167   return diags;
168 }
169