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 121 // For seekable streams, just return the stream corresponding to the output 122 // file. 123 if (!binary || os->supportsSeeking()) 124 return std::move(os); 125 126 // For non-seekable streams, we need to wrap the output stream into something 127 // that supports 'pwrite' and takes care of the ownership for us. 128 return std::make_unique<llvm::buffer_unique_ostream>(std::move(os)); 129 } 130 131 void CompilerInstance::ClearOutputFiles(bool eraseFiles) { 132 for (OutputFile &of : outputFiles_) 133 if (!of.filename_.empty() && eraseFiles) 134 llvm::sys::fs::remove(of.filename_); 135 136 outputFiles_.clear(); 137 } 138 139 bool CompilerInstance::ExecuteAction(FrontendAction &act) { 140 auto &invoc = this->invocation(); 141 142 // Set some sane defaults for the frontend. 143 invoc.SetDefaultFortranOpts(); 144 // Update the fortran options based on user-based input. 145 invoc.setFortranOpts(); 146 // Set the encoding to read all input files in based on user input. 147 allSources_->set_encoding(invoc.fortranOpts().encoding); 148 // Create the semantics context and set semantic options. 149 invoc.setSemanticsOpts(*this->allCookedSources_); 150 151 // Run the frontend action `act` for every input file. 152 for (const FrontendInputFile &fif : frontendOpts().inputs) { 153 if (act.BeginSourceFile(*this, fif)) { 154 if (llvm::Error err = act.Execute()) { 155 consumeError(std::move(err)); 156 } 157 act.EndSourceFile(); 158 } 159 } 160 return !diagnostics().getClient()->getNumErrors(); 161 } 162 163 void CompilerInstance::CreateDiagnostics( 164 clang::DiagnosticConsumer *client, bool shouldOwnClient) { 165 diagnostics_ = 166 CreateDiagnostics(&GetDiagnosticOpts(), client, shouldOwnClient); 167 } 168 169 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> 170 CompilerInstance::CreateDiagnostics(clang::DiagnosticOptions *opts, 171 clang::DiagnosticConsumer *client, bool shouldOwnClient) { 172 clang::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID( 173 new clang::DiagnosticIDs()); 174 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags( 175 new clang::DiagnosticsEngine(diagID, opts)); 176 177 // Create the diagnostic client for reporting errors or for 178 // implementing -verify. 179 if (client) { 180 diags->setClient(client, shouldOwnClient); 181 } else { 182 diags->setClient(new TextDiagnosticPrinter(llvm::errs(), opts)); 183 } 184 return diags; 185 } 186