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 void CompilerInstance::AddOutputFile(OutputFile &&outFile) { 55 outputFiles_.push_back(std::move(outFile)); 56 } 57 58 // Helper method to generate the path of the output file. The following logic 59 // applies: 60 // 1. If the user specifies the output file via `-o`, then use that (i.e. 61 // the outputFilename parameter). 62 // 2. If the user does not specify the name of the output file, derive it from 63 // the input file (i.e. inputFilename + extension) 64 // 3. If the output file is not specified and the input file is `-`, then set 65 // the output file to `-` as well. 66 static std::string GetOutputFilePath(llvm::StringRef outputFilename, 67 llvm::StringRef inputFilename, 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( 86 bool binary, llvm::StringRef baseName, llvm::StringRef extension) { 87 std::string outputPathName; 88 std::error_code ec; 89 90 // Get the path of the output file 91 std::string outputFilePath = 92 GetOutputFilePath(frontendOpts().outputFile_, baseName, extension); 93 94 // Create the output file 95 std::unique_ptr<llvm::raw_pwrite_stream> os = 96 CreateOutputFile(outputFilePath, ec, binary); 97 98 // Add the file to the list of tracked output files (provided it was created 99 // successfully) 100 if (os) 101 AddOutputFile(OutputFile(outputPathName)); 102 103 return os; 104 } 105 106 std::unique_ptr<llvm::raw_pwrite_stream> CompilerInstance::CreateOutputFile( 107 llvm::StringRef outputFilePath, std::error_code &error, bool binary) { 108 109 // Creates the file descriptor for the output file 110 std::unique_ptr<llvm::raw_fd_ostream> os; 111 std::string osFile; 112 if (!os) { 113 osFile = outputFilePath; 114 os.reset(new llvm::raw_fd_ostream(osFile, error, 115 (binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_TextWithCRLF))); 116 if (error) 117 return nullptr; 118 } 119 120 // Return the stream corresponding to the output file. 121 // For non-seekable streams, wrap it in llvm::buffer_ostream first. 122 if (!binary || os->supportsSeeking()) 123 return std::move(os); 124 125 assert(!nonSeekStream_ && "The non-seek stream has already been set!"); 126 auto b = std::make_unique<llvm::buffer_ostream>(*os); 127 nonSeekStream_ = std::move(os); 128 return std::move(b); 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 nonSeekStream_.reset(); 138 } 139 140 bool CompilerInstance::ExecuteAction(FrontendAction &act) { 141 auto &invoc = this->invocation(); 142 143 // Set some sane defaults for the frontend. 144 invoc.SetDefaultFortranOpts(); 145 // Update the fortran options based on user-based input. 146 invoc.setFortranOpts(); 147 // Set the encoding to read all input files in based on user input. 148 allSources_->set_encoding(invoc.fortranOpts().encoding); 149 // Create the semantics context and set semantic options. 150 invoc.setSemanticsOpts(*this->allCookedSources_); 151 152 // Run the frontend action `act` for every input file. 153 for (const FrontendInputFile &fif : frontendOpts().inputs_) { 154 if (act.BeginSourceFile(*this, fif)) { 155 if (llvm::Error err = act.Execute()) { 156 consumeError(std::move(err)); 157 } 158 act.EndSourceFile(); 159 } 160 } 161 return !diagnostics().getClient()->getNumErrors(); 162 } 163 164 void CompilerInstance::CreateDiagnostics( 165 clang::DiagnosticConsumer *client, bool shouldOwnClient) { 166 diagnostics_ = 167 CreateDiagnostics(&GetDiagnosticOpts(), client, shouldOwnClient); 168 } 169 170 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> 171 CompilerInstance::CreateDiagnostics(clang::DiagnosticOptions *opts, 172 clang::DiagnosticConsumer *client, bool shouldOwnClient) { 173 clang::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID( 174 new clang::DiagnosticIDs()); 175 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags( 176 new clang::DiagnosticsEngine(diagID, opts)); 177 178 // Create the diagnostic client for reporting errors or for 179 // implementing -verify. 180 if (client) { 181 diags->setClient(client, shouldOwnClient); 182 } else { 183 diags->setClient(new TextDiagnosticPrinter(llvm::errs(), opts)); 184 } 185 return diags; 186 } 187