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