xref: /llvm-project/flang/lib/Frontend/CompilerInstance.cpp (revision 3a1513c142f4a1ddb97d882a12c89f90cb2529ac)
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   auto &invoc = this->invocation();
142 
143   // Set some sane defaults for the frontend.
144   invoc.SetDefaultFortranOpts();
145   // Update the fortran options based on user-based input.
146   invoc.setFortranOpts();
147 
148   // Run the frontend action `act` for every input file.
149   for (const FrontendInputFile &fif : frontendOpts().inputs_) {
150     if (act.BeginSourceFile(*this, fif)) {
151       if (invoc.frontendOpts().fortranForm_ == FortranForm::Unknown) {
152         // Switch between fixed and free form format based on the input file
153         // extension. Ideally we should have all Fortran options set before
154         // entering this loop (i.e. processing any input files). However, we
155         // can't decide between fixed and free form based on the file extension
156         // earlier than this.
157         invoc.fortranOpts().isFixedForm = fif.IsFixedForm();
158       }
159       if (llvm::Error err = act.Execute()) {
160         consumeError(std::move(err));
161       }
162       act.EndSourceFile();
163     }
164   }
165   return !diagnostics().getClient()->getNumErrors();
166 }
167 
168 void CompilerInstance::CreateDiagnostics(
169     clang::DiagnosticConsumer *client, bool shouldOwnClient) {
170   diagnostics_ =
171       CreateDiagnostics(&GetDiagnosticOpts(), client, shouldOwnClient);
172 }
173 
174 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine>
175 CompilerInstance::CreateDiagnostics(clang::DiagnosticOptions *opts,
176     clang::DiagnosticConsumer *client, bool shouldOwnClient) {
177   clang::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID(
178       new clang::DiagnosticIDs());
179   clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags(
180       new clang::DiagnosticsEngine(diagID, opts));
181 
182   // Create the diagnostic client for reporting errors or for
183   // implementing -verify.
184   if (client) {
185     diags->setClient(client, shouldOwnClient);
186   } else {
187     diags->setClient(new TextDiagnosticPrinter(llvm::errs(), opts));
188   }
189   return diags;
190 }
191