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       semanticsContext_(new Fortran::semantics::SemanticsContext(
29           *(new Fortran::common::IntrinsicTypeDefaultKinds()),
30           *(new common::LanguageFeatureControl()), *allCookedSources_)) {
31   // TODO: This is a good default during development, but ultimately we should
32   // give the user the opportunity to specify this.
33   allSources_->set_encoding(Fortran::parser::Encoding::UTF_8);
34 }
35 
36 CompilerInstance::~CompilerInstance() {
37   assert(outputFiles_.empty() && "Still output files in flight?");
38 }
39 
40 void CompilerInstance::set_invocation(
41     std::shared_ptr<CompilerInvocation> value) {
42   invocation_ = std::move(value);
43 }
44 
45 void CompilerInstance::set_semaOutputStream(raw_ostream &Value) {
46   ownedSemaOutputStream_.release();
47   semaOutputStream_ = &Value;
48 }
49 
50 void CompilerInstance::set_semaOutputStream(
51     std::unique_ptr<raw_ostream> Value) {
52   ownedSemaOutputStream_.swap(Value);
53   semaOutputStream_ = ownedSemaOutputStream_.get();
54 }
55 
56 void CompilerInstance::AddOutputFile(OutputFile &&outFile) {
57   outputFiles_.push_back(std::move(outFile));
58 }
59 
60 // Helper method to generate the path of the output file. The following logic
61 // applies:
62 // 1. If the user specifies the output file via `-o`, then use that (i.e.
63 //    the outputFilename parameter).
64 // 2. If the user does not specify the name of the output file, derive it from
65 //    the input file (i.e. inputFilename + extension)
66 // 3. If the output file is not specified and the input file is `-`, then set
67 //    the output file to `-` as well.
68 static std::string GetOutputFilePath(llvm::StringRef outputFilename,
69     llvm::StringRef inputFilename, llvm::StringRef extension) {
70 
71   // Output filename _is_ specified. Just use that.
72   if (!outputFilename.empty())
73     return std::string(outputFilename);
74 
75   // Output filename _is not_ specified. Derive it from the input file name.
76   std::string outFile = "-";
77   if (!extension.empty() && (inputFilename != "-")) {
78     llvm::SmallString<128> path(inputFilename);
79     llvm::sys::path::replace_extension(path, extension);
80     outFile = std::string(path.str());
81   }
82 
83   return outFile;
84 }
85 
86 std::unique_ptr<llvm::raw_pwrite_stream>
87 CompilerInstance::CreateDefaultOutputFile(
88     bool binary, llvm::StringRef baseName, llvm::StringRef extension) {
89   std::string outputPathName;
90   std::error_code ec;
91 
92   // Get the path of the output file
93   std::string outputFilePath =
94       GetOutputFilePath(frontendOpts().outputFile_, baseName, extension);
95 
96   // Create the output file
97   std::unique_ptr<llvm::raw_pwrite_stream> os =
98       CreateOutputFile(outputFilePath, ec, binary);
99 
100   // Add the file to the list of tracked output files (provided it was created
101   // successfully)
102   if (os)
103     AddOutputFile(OutputFile(outputPathName));
104 
105   return os;
106 }
107 
108 std::unique_ptr<llvm::raw_pwrite_stream> CompilerInstance::CreateOutputFile(
109     llvm::StringRef outputFilePath, std::error_code &error, bool binary) {
110 
111   // Creates the file descriptor for the output file
112   std::unique_ptr<llvm::raw_fd_ostream> os;
113   std::string osFile;
114   if (!os) {
115     osFile = outputFilePath;
116     os.reset(new llvm::raw_fd_ostream(osFile, error,
117         (binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_Text)));
118     if (error)
119       return nullptr;
120   }
121 
122   // Return the stream corresponding to the output file.
123   // For non-seekable streams, wrap it in llvm::buffer_ostream first.
124   if (!binary || os->supportsSeeking())
125     return std::move(os);
126 
127   assert(!nonSeekStream_ && "The non-seek stream has already been set!");
128   auto b = std::make_unique<llvm::buffer_ostream>(*os);
129   nonSeekStream_ = std::move(os);
130   return std::move(b);
131 }
132 
133 void CompilerInstance::ClearOutputFiles(bool eraseFiles) {
134   for (OutputFile &of : outputFiles_)
135     if (!of.filename_.empty() && eraseFiles)
136       llvm::sys::fs::remove(of.filename_);
137 
138   outputFiles_.clear();
139   nonSeekStream_.reset();
140 }
141 
142 bool CompilerInstance::ExecuteAction(FrontendAction &act) {
143   auto &invoc = this->invocation();
144 
145   // Set some sane defaults for the frontend.
146   invoc.SetDefaultFortranOpts();
147   // Update the fortran options based on user-based input.
148   invoc.setFortranOpts();
149   // Set semantic options
150   invoc.setSemanticsOpts(this->semanticsContext());
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