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