xref: /llvm-project/flang/lib/Frontend/CompilerInstance.cpp (revision b83a4450c216879d76d78c21a2a0b864fdc2eb3d)
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/Common/Fortran-features.h"
11 #include "flang/Frontend/CompilerInvocation.h"
12 #include "flang/Frontend/TextDiagnosticPrinter.h"
13 #include "flang/Parser/parsing.h"
14 #include "flang/Parser/provenance.h"
15 #include "flang/Semantics/semantics.h"
16 #include "llvm/Support/Errc.h"
17 #include "llvm/Support/Error.h"
18 #include "llvm/Support/FileSystem.h"
19 #include "llvm/Support/Path.h"
20 #include "llvm/Support/raw_ostream.h"
21 
22 using namespace Fortran::frontend;
23 
24 CompilerInstance::CompilerInstance()
25     : invocation_(new CompilerInvocation()),
26       allSources_(new Fortran::parser::AllSources()),
27       allCookedSources_(new Fortran::parser::AllCookedSources(*allSources_)),
28       parsing_(new Fortran::parser::Parsing(*allCookedSources_)) {
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_TextWithCRLF)));
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   // Set the encoding to read all input files in based on user input.
148   allSources_->set_encoding(invoc.fortranOpts().encoding);
149   // Create the semantics context and set semantic options.
150   invoc.setSemanticsOpts(*this->allCookedSources_);
151 
152   // Run the frontend action `act` for every input file.
153   for (const FrontendInputFile &fif : frontendOpts().inputs_) {
154     if (act.BeginSourceFile(*this, fif)) {
155       if (llvm::Error err = act.Execute()) {
156         consumeError(std::move(err));
157       }
158       act.EndSourceFile();
159     }
160   }
161   return !diagnostics().getClient()->getNumErrors();
162 }
163 
164 void CompilerInstance::CreateDiagnostics(
165     clang::DiagnosticConsumer *client, bool shouldOwnClient) {
166   diagnostics_ =
167       CreateDiagnostics(&GetDiagnosticOpts(), client, shouldOwnClient);
168 }
169 
170 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine>
171 CompilerInstance::CreateDiagnostics(clang::DiagnosticOptions *opts,
172     clang::DiagnosticConsumer *client, bool shouldOwnClient) {
173   clang::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID(
174       new clang::DiagnosticIDs());
175   clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags(
176       new clang::DiagnosticsEngine(diagID, opts));
177 
178   // Create the diagnostic client for reporting errors or for
179   // implementing -verify.
180   if (client) {
181     diags->setClient(client, shouldOwnClient);
182   } else {
183     diags->setClient(new TextDiagnosticPrinter(llvm::errs(), opts));
184   }
185   return diags;
186 }
187