1257b2971SCaroline Concatto //===--- CompilerInstance.cpp ---------------------------------------------===//
2257b2971SCaroline Concatto //
3257b2971SCaroline Concatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4257b2971SCaroline Concatto // See https://llvm.org/LICENSE.txt for license information.
5257b2971SCaroline Concatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6257b2971SCaroline Concatto //
7257b2971SCaroline Concatto //===----------------------------------------------------------------------===//
8*1e462fafSAndrzej Warzynski //
9*1e462fafSAndrzej Warzynski // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10*1e462fafSAndrzej Warzynski //
11*1e462fafSAndrzej Warzynski //===----------------------------------------------------------------------===//
12257b2971SCaroline Concatto 
13257b2971SCaroline Concatto #include "flang/Frontend/CompilerInstance.h"
146d48a1a5SFaris Rehman #include "flang/Common/Fortran-features.h"
15257b2971SCaroline Concatto #include "flang/Frontend/CompilerInvocation.h"
168d51d37eSAndrzej Warzynski #include "flang/Frontend/TextDiagnosticPrinter.h"
17d28de0d7SCaroline Concatto #include "flang/Parser/parsing.h"
184c5906cfSCaroline Concatto #include "flang/Parser/provenance.h"
197d246cb1SAndrzej Warzynski #include "flang/Semantics/semantics.h"
204c5906cfSCaroline Concatto #include "llvm/Support/Errc.h"
214c5906cfSCaroline Concatto #include "llvm/Support/Error.h"
224c5906cfSCaroline Concatto #include "llvm/Support/FileSystem.h"
234c5906cfSCaroline Concatto #include "llvm/Support/Path.h"
24257b2971SCaroline Concatto #include "llvm/Support/raw_ostream.h"
25257b2971SCaroline Concatto 
26257b2971SCaroline Concatto using namespace Fortran::frontend;
27257b2971SCaroline Concatto 
CompilerInstance()284c5906cfSCaroline Concatto CompilerInstance::CompilerInstance()
29*1e462fafSAndrzej Warzynski     : invocation(new CompilerInvocation()),
30*1e462fafSAndrzej Warzynski       allSources(new Fortran::parser::AllSources()),
31*1e462fafSAndrzej Warzynski       allCookedSources(new Fortran::parser::AllCookedSources(*allSources)),
32*1e462fafSAndrzej Warzynski       parsing(new Fortran::parser::Parsing(*allCookedSources)) {
33d28de0d7SCaroline Concatto   // TODO: This is a good default during development, but ultimately we should
34d28de0d7SCaroline Concatto   // give the user the opportunity to specify this.
35*1e462fafSAndrzej Warzynski   allSources->set_encoding(Fortran::parser::Encoding::UTF_8);
36d28de0d7SCaroline Concatto }
37257b2971SCaroline Concatto 
~CompilerInstance()384c5906cfSCaroline Concatto CompilerInstance::~CompilerInstance() {
39*1e462fafSAndrzej Warzynski   assert(outputFiles.empty() && "Still output files in flight?");
404c5906cfSCaroline Concatto }
414c5906cfSCaroline Concatto 
setInvocation(std::shared_ptr<CompilerInvocation> value)42*1e462fafSAndrzej Warzynski void CompilerInstance::setInvocation(
434c5906cfSCaroline Concatto     std::shared_ptr<CompilerInvocation> value) {
44*1e462fafSAndrzej Warzynski   invocation = std::move(value);
454c5906cfSCaroline Concatto }
464c5906cfSCaroline Concatto 
setSemaOutputStream(raw_ostream & value)47*1e462fafSAndrzej Warzynski void CompilerInstance::setSemaOutputStream(raw_ostream &value) {
48*1e462fafSAndrzej Warzynski   ownedSemaOutputStream.release();
49*1e462fafSAndrzej Warzynski   semaOutputStream = &value;
507d246cb1SAndrzej Warzynski }
517d246cb1SAndrzej Warzynski 
setSemaOutputStream(std::unique_ptr<raw_ostream> value)52*1e462fafSAndrzej Warzynski void CompilerInstance::setSemaOutputStream(std::unique_ptr<raw_ostream> value) {
53*1e462fafSAndrzej Warzynski   ownedSemaOutputStream.swap(value);
54*1e462fafSAndrzej Warzynski   semaOutputStream = ownedSemaOutputStream.get();
557d246cb1SAndrzej Warzynski }
567d246cb1SAndrzej Warzynski 
574c5906cfSCaroline Concatto // Helper method to generate the path of the output file. The following logic
584c5906cfSCaroline Concatto // applies:
594c5906cfSCaroline Concatto // 1. If the user specifies the output file via `-o`, then use that (i.e.
604c5906cfSCaroline Concatto //    the outputFilename parameter).
614c5906cfSCaroline Concatto // 2. If the user does not specify the name of the output file, derive it from
624c5906cfSCaroline Concatto //    the input file (i.e. inputFilename + extension)
634c5906cfSCaroline Concatto // 3. If the output file is not specified and the input file is `-`, then set
644c5906cfSCaroline Concatto //    the output file to `-` as well.
getOutputFilePath(llvm::StringRef outputFilename,llvm::StringRef inputFilename,llvm::StringRef extension)65*1e462fafSAndrzej Warzynski static std::string getOutputFilePath(llvm::StringRef outputFilename,
66*1e462fafSAndrzej Warzynski                                      llvm::StringRef inputFilename,
67*1e462fafSAndrzej Warzynski                                      llvm::StringRef extension) {
684c5906cfSCaroline Concatto 
694c5906cfSCaroline Concatto   // Output filename _is_ specified. Just use that.
704c5906cfSCaroline Concatto   if (!outputFilename.empty())
714c5906cfSCaroline Concatto     return std::string(outputFilename);
724c5906cfSCaroline Concatto 
734c5906cfSCaroline Concatto   // Output filename _is not_ specified. Derive it from the input file name.
744c5906cfSCaroline Concatto   std::string outFile = "-";
754c5906cfSCaroline Concatto   if (!extension.empty() && (inputFilename != "-")) {
764c5906cfSCaroline Concatto     llvm::SmallString<128> path(inputFilename);
774c5906cfSCaroline Concatto     llvm::sys::path::replace_extension(path, extension);
784c5906cfSCaroline Concatto     outFile = std::string(path.str());
794c5906cfSCaroline Concatto   }
804c5906cfSCaroline Concatto 
814c5906cfSCaroline Concatto   return outFile;
824c5906cfSCaroline Concatto }
834c5906cfSCaroline Concatto 
844c5906cfSCaroline Concatto std::unique_ptr<llvm::raw_pwrite_stream>
createDefaultOutputFile(bool binary,llvm::StringRef baseName,llvm::StringRef extension)85*1e462fafSAndrzej Warzynski CompilerInstance::createDefaultOutputFile(bool binary, llvm::StringRef baseName,
86*1e462fafSAndrzej Warzynski                                           llvm::StringRef extension) {
874c5906cfSCaroline Concatto 
884c5906cfSCaroline Concatto   // Get the path of the output file
894c5906cfSCaroline Concatto   std::string outputFilePath =
90*1e462fafSAndrzej Warzynski       getOutputFilePath(getFrontendOpts().outputFile, baseName, extension);
914c5906cfSCaroline Concatto 
924c5906cfSCaroline Concatto   // Create the output file
93787c443aSAndrzej Warzynski   llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>> os =
94*1e462fafSAndrzej Warzynski       createOutputFileImpl(outputFilePath, binary);
954c5906cfSCaroline Concatto 
96787c443aSAndrzej Warzynski   // If successful, add the file to the list of tracked output files and
97787c443aSAndrzej Warzynski   // return.
98787c443aSAndrzej Warzynski   if (os) {
99*1e462fafSAndrzej Warzynski     outputFiles.emplace_back(OutputFile(outputFilePath));
100787c443aSAndrzej Warzynski     return std::move(*os);
1014c5906cfSCaroline Concatto   }
1024c5906cfSCaroline Concatto 
103787c443aSAndrzej Warzynski   // If unsuccessful, issue an error and return Null
104*1e462fafSAndrzej Warzynski   unsigned diagID = getDiagnostics().getCustomDiagID(
105787c443aSAndrzej Warzynski       clang::DiagnosticsEngine::Error, "unable to open output file '%0': '%1'");
106*1e462fafSAndrzej Warzynski   getDiagnostics().Report(diagID)
107787c443aSAndrzej Warzynski       << outputFilePath << llvm::errorToErrorCode(os.takeError()).message();
108316be03fSAndrzej Warzynski   return nullptr;
109316be03fSAndrzej Warzynski }
110fd21d1e1SAndrzej Warzynski 
111787c443aSAndrzej Warzynski llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>>
createOutputFileImpl(llvm::StringRef outputFilePath,bool binary)112*1e462fafSAndrzej Warzynski CompilerInstance::createOutputFileImpl(llvm::StringRef outputFilePath,
113*1e462fafSAndrzej Warzynski                                        bool binary) {
114787c443aSAndrzej Warzynski 
115787c443aSAndrzej Warzynski   // Creates the file descriptor for the output file
116787c443aSAndrzej Warzynski   std::unique_ptr<llvm::raw_fd_ostream> os;
117787c443aSAndrzej Warzynski 
118787c443aSAndrzej Warzynski   std::error_code error;
119787c443aSAndrzej Warzynski   os.reset(new llvm::raw_fd_ostream(outputFilePath, error,
120787c443aSAndrzej Warzynski       (binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_TextWithCRLF)));
121787c443aSAndrzej Warzynski   if (error) {
122787c443aSAndrzej Warzynski     return llvm::errorCodeToError(error);
123787c443aSAndrzej Warzynski   }
124787c443aSAndrzej Warzynski 
125787c443aSAndrzej Warzynski   // For seekable streams, just return the stream corresponding to the output
126787c443aSAndrzej Warzynski   // file.
1274c5906cfSCaroline Concatto   if (!binary || os->supportsSeeking())
1284c5906cfSCaroline Concatto     return std::move(os);
1294c5906cfSCaroline Concatto 
130787c443aSAndrzej Warzynski   // For non-seekable streams, we need to wrap the output stream into something
131787c443aSAndrzej Warzynski   // that supports 'pwrite' and takes care of the ownership for us.
132787c443aSAndrzej Warzynski   return std::make_unique<llvm::buffer_unique_ostream>(std::move(os));
1334c5906cfSCaroline Concatto }
1344c5906cfSCaroline Concatto 
clearOutputFiles(bool eraseFiles)135*1e462fafSAndrzej Warzynski void CompilerInstance::clearOutputFiles(bool eraseFiles) {
136*1e462fafSAndrzej Warzynski   for (OutputFile &of : outputFiles)
137*1e462fafSAndrzej Warzynski     if (!of.filename.empty() && eraseFiles)
138*1e462fafSAndrzej Warzynski       llvm::sys::fs::remove(of.filename);
1394c5906cfSCaroline Concatto 
140*1e462fafSAndrzej Warzynski   outputFiles.clear();
1414c5906cfSCaroline Concatto }
1424c5906cfSCaroline Concatto 
executeAction(FrontendAction & act)143*1e462fafSAndrzej Warzynski bool CompilerInstance::executeAction(FrontendAction &act) {
144*1e462fafSAndrzej Warzynski   auto &invoc = this->getInvocation();
1454c5906cfSCaroline Concatto 
146aba24c15SAndrzej Warzynski   // Set some sane defaults for the frontend.
147*1e462fafSAndrzej Warzynski   invoc.setDefaultFortranOpts();
148aba24c15SAndrzej Warzynski   // Update the fortran options based on user-based input.
149*1e462fafSAndrzej Warzynski   invoc.setFortranOpts();
15010826ea7SFaris Rehman   // Set the encoding to read all input files in based on user input.
151*1e462fafSAndrzej Warzynski   allSources->set_encoding(invoc.getFortranOpts().encoding);
1526d48a1a5SFaris Rehman   // Create the semantics context and set semantic options.
153*1e462fafSAndrzej Warzynski   invoc.setSemanticsOpts(*this->allCookedSources);
154aba24c15SAndrzej Warzynski 
155aba24c15SAndrzej Warzynski   // Run the frontend action `act` for every input file.
156*1e462fafSAndrzej Warzynski   for (const FrontendInputFile &fif : getFrontendOpts().inputs) {
157*1e462fafSAndrzej Warzynski     if (act.beginSourceFile(*this, fif)) {
158*1e462fafSAndrzej Warzynski       if (llvm::Error err = act.execute()) {
1594c5906cfSCaroline Concatto         consumeError(std::move(err));
1604c5906cfSCaroline Concatto       }
161*1e462fafSAndrzej Warzynski       act.endSourceFile();
1624c5906cfSCaroline Concatto     }
1634c5906cfSCaroline Concatto   }
164*1e462fafSAndrzej Warzynski   return !getDiagnostics().getClient()->getNumErrors();
1654c5906cfSCaroline Concatto }
166257b2971SCaroline Concatto 
createDiagnostics(clang::DiagnosticConsumer * client,bool shouldOwnClient)167*1e462fafSAndrzej Warzynski void CompilerInstance::createDiagnostics(clang::DiagnosticConsumer *client,
168*1e462fafSAndrzej Warzynski                                          bool shouldOwnClient) {
169*1e462fafSAndrzej Warzynski   diagnostics =
170*1e462fafSAndrzej Warzynski       createDiagnostics(&getDiagnosticOpts(), client, shouldOwnClient);
171257b2971SCaroline Concatto }
172257b2971SCaroline Concatto 
173257b2971SCaroline Concatto clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine>
createDiagnostics(clang::DiagnosticOptions * opts,clang::DiagnosticConsumer * client,bool shouldOwnClient)174*1e462fafSAndrzej Warzynski CompilerInstance::createDiagnostics(clang::DiagnosticOptions *opts,
175*1e462fafSAndrzej Warzynski                                     clang::DiagnosticConsumer *client,
176*1e462fafSAndrzej Warzynski                                     bool shouldOwnClient) {
177257b2971SCaroline Concatto   clang::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID(
178257b2971SCaroline Concatto       new clang::DiagnosticIDs());
179257b2971SCaroline Concatto   clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags(
180257b2971SCaroline Concatto       new clang::DiagnosticsEngine(diagID, opts));
181257b2971SCaroline Concatto 
182257b2971SCaroline Concatto   // Create the diagnostic client for reporting errors or for
183257b2971SCaroline Concatto   // implementing -verify.
184257b2971SCaroline Concatto   if (client) {
185257b2971SCaroline Concatto     diags->setClient(client, shouldOwnClient);
186257b2971SCaroline Concatto   } else {
1878d51d37eSAndrzej Warzynski     diags->setClient(new TextDiagnosticPrinter(llvm::errs(), opts));
188257b2971SCaroline Concatto   }
189257b2971SCaroline Concatto   return diags;
190257b2971SCaroline Concatto }
191