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 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "flang/Frontend/CompilerInstance.h" 14 #include "flang/Common/Fortran-features.h" 15 #include "flang/Frontend/CompilerInvocation.h" 16 #include "flang/Frontend/TextDiagnosticPrinter.h" 17 #include "flang/Parser/parsing.h" 18 #include "flang/Parser/provenance.h" 19 #include "flang/Semantics/semantics.h" 20 #include "llvm/Support/Errc.h" 21 #include "llvm/Support/Error.h" 22 #include "llvm/Support/FileSystem.h" 23 #include "llvm/Support/Path.h" 24 #include "llvm/Support/raw_ostream.h" 25 26 using namespace Fortran::frontend; 27 28 CompilerInstance::CompilerInstance() 29 : invocation(new CompilerInvocation()), 30 allSources(new Fortran::parser::AllSources()), 31 allCookedSources(new Fortran::parser::AllCookedSources(*allSources)), 32 parsing(new Fortran::parser::Parsing(*allCookedSources)) { 33 // TODO: This is a good default during development, but ultimately we should 34 // give the user the opportunity to specify this. 35 allSources->set_encoding(Fortran::parser::Encoding::UTF_8); 36 } 37 38 CompilerInstance::~CompilerInstance() { 39 assert(outputFiles.empty() && "Still output files in flight?"); 40 } 41 42 void CompilerInstance::setInvocation( 43 std::shared_ptr<CompilerInvocation> value) { 44 invocation = std::move(value); 45 } 46 47 void CompilerInstance::setSemaOutputStream(raw_ostream &value) { 48 ownedSemaOutputStream.release(); 49 semaOutputStream = &value; 50 } 51 52 void CompilerInstance::setSemaOutputStream(std::unique_ptr<raw_ostream> value) { 53 ownedSemaOutputStream.swap(value); 54 semaOutputStream = ownedSemaOutputStream.get(); 55 } 56 57 // Helper method to generate the path of the output file. The following logic 58 // applies: 59 // 1. If the user specifies the output file via `-o`, then use that (i.e. 60 // the outputFilename parameter). 61 // 2. If the user does not specify the name of the output file, derive it from 62 // the input file (i.e. inputFilename + extension) 63 // 3. If the output file is not specified and the input file is `-`, then set 64 // the output file to `-` as well. 65 static std::string getOutputFilePath(llvm::StringRef outputFilename, 66 llvm::StringRef inputFilename, 67 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(bool binary, llvm::StringRef baseName, 86 llvm::StringRef extension) { 87 88 // Get the path of the output file 89 std::string outputFilePath = 90 getOutputFilePath(getFrontendOpts().outputFile, baseName, extension); 91 92 // Create the output file 93 llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>> os = 94 createOutputFileImpl(outputFilePath, binary); 95 96 // If successful, add the file to the list of tracked output files and 97 // return. 98 if (os) { 99 outputFiles.emplace_back(OutputFile(outputFilePath)); 100 return std::move(*os); 101 } 102 103 // If unsuccessful, issue an error and return Null 104 unsigned diagID = getDiagnostics().getCustomDiagID( 105 clang::DiagnosticsEngine::Error, "unable to open output file '%0': '%1'"); 106 getDiagnostics().Report(diagID) 107 << outputFilePath << llvm::errorToErrorCode(os.takeError()).message(); 108 return nullptr; 109 } 110 111 llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>> 112 CompilerInstance::createOutputFileImpl(llvm::StringRef outputFilePath, 113 bool binary) { 114 115 // Creates the file descriptor for the output file 116 std::unique_ptr<llvm::raw_fd_ostream> os; 117 118 std::error_code error; 119 os.reset(new llvm::raw_fd_ostream(outputFilePath, error, 120 (binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_TextWithCRLF))); 121 if (error) { 122 return llvm::errorCodeToError(error); 123 } 124 125 // For seekable streams, just return the stream corresponding to the output 126 // file. 127 if (!binary || os->supportsSeeking()) 128 return std::move(os); 129 130 // For non-seekable streams, we need to wrap the output stream into something 131 // that supports 'pwrite' and takes care of the ownership for us. 132 return std::make_unique<llvm::buffer_unique_ostream>(std::move(os)); 133 } 134 135 void CompilerInstance::clearOutputFiles(bool eraseFiles) { 136 for (OutputFile &of : outputFiles) 137 if (!of.filename.empty() && eraseFiles) 138 llvm::sys::fs::remove(of.filename); 139 140 outputFiles.clear(); 141 } 142 143 bool CompilerInstance::executeAction(FrontendAction &act) { 144 auto &invoc = this->getInvocation(); 145 146 // Set some sane defaults for the frontend. 147 invoc.setDefaultFortranOpts(); 148 // Update the fortran options based on user-based input. 149 invoc.setFortranOpts(); 150 // Set the encoding to read all input files in based on user input. 151 allSources->set_encoding(invoc.getFortranOpts().encoding); 152 // Create the semantics context and set semantic options. 153 invoc.setSemanticsOpts(*this->allCookedSources); 154 155 // Run the frontend action `act` for every input file. 156 for (const FrontendInputFile &fif : getFrontendOpts().inputs) { 157 if (act.beginSourceFile(*this, fif)) { 158 if (llvm::Error err = act.execute()) { 159 consumeError(std::move(err)); 160 } 161 act.endSourceFile(); 162 } 163 } 164 return !getDiagnostics().getClient()->getNumErrors(); 165 } 166 167 void CompilerInstance::createDiagnostics(clang::DiagnosticConsumer *client, 168 bool shouldOwnClient) { 169 diagnostics = 170 createDiagnostics(&getDiagnosticOpts(), client, shouldOwnClient); 171 } 172 173 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> 174 CompilerInstance::createDiagnostics(clang::DiagnosticOptions *opts, 175 clang::DiagnosticConsumer *client, 176 bool shouldOwnClient) { 177 clang::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID( 178 new clang::DiagnosticIDs()); 179 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags( 180 new clang::DiagnosticsEngine(diagID, opts)); 181 182 // Create the diagnostic client for reporting errors or for 183 // implementing -verify. 184 if (client) { 185 diags->setClient(client, shouldOwnClient); 186 } else { 187 diags->setClient(new TextDiagnosticPrinter(llvm::errs(), opts)); 188 } 189 return diags; 190 } 191