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