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