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/Frontend/CompilerInvocation.h" 11 #include "flang/Frontend/TextDiagnosticPrinter.h" 12 #include "flang/Parser/parsing.h" 13 #include "flang/Parser/provenance.h" 14 #include "flang/Semantics/semantics.h" 15 #include "llvm/Support/Errc.h" 16 #include "llvm/Support/Error.h" 17 #include "llvm/Support/FileSystem.h" 18 #include "llvm/Support/Path.h" 19 #include "llvm/Support/raw_ostream.h" 20 21 using namespace Fortran::frontend; 22 23 CompilerInstance::CompilerInstance() 24 : invocation_(new CompilerInvocation()), 25 allSources_(new Fortran::parser::AllSources()), 26 allCookedSources_(new Fortran::parser::AllCookedSources(*allSources_)), 27 parsing_(new Fortran::parser::Parsing(*allCookedSources_)), 28 semanticsContext_(new Fortran::semantics::SemanticsContext( 29 *(new Fortran::common::IntrinsicTypeDefaultKinds()), 30 *(new common::LanguageFeatureControl()), *allCookedSources_)) { 31 // TODO: This is a good default during development, but ultimately we should 32 // give the user the opportunity to specify this. 33 allSources_->set_encoding(Fortran::parser::Encoding::UTF_8); 34 } 35 36 CompilerInstance::~CompilerInstance() { 37 assert(outputFiles_.empty() && "Still output files in flight?"); 38 } 39 40 void CompilerInstance::set_invocation( 41 std::shared_ptr<CompilerInvocation> value) { 42 invocation_ = std::move(value); 43 } 44 45 void CompilerInstance::set_semaOutputStream(raw_ostream &Value) { 46 ownedSemaOutputStream_.release(); 47 semaOutputStream_ = &Value; 48 } 49 50 void CompilerInstance::set_semaOutputStream( 51 std::unique_ptr<raw_ostream> Value) { 52 ownedSemaOutputStream_.swap(Value); 53 semaOutputStream_ = ownedSemaOutputStream_.get(); 54 } 55 56 void CompilerInstance::AddOutputFile(OutputFile &&outFile) { 57 outputFiles_.push_back(std::move(outFile)); 58 } 59 60 // Helper method to generate the path of the output file. The following logic 61 // applies: 62 // 1. If the user specifies the output file via `-o`, then use that (i.e. 63 // the outputFilename parameter). 64 // 2. If the user does not specify the name of the output file, derive it from 65 // the input file (i.e. inputFilename + extension) 66 // 3. If the output file is not specified and the input file is `-`, then set 67 // the output file to `-` as well. 68 static std::string GetOutputFilePath(llvm::StringRef outputFilename, 69 llvm::StringRef inputFilename, llvm::StringRef extension) { 70 71 // Output filename _is_ specified. Just use that. 72 if (!outputFilename.empty()) 73 return std::string(outputFilename); 74 75 // Output filename _is not_ specified. Derive it from the input file name. 76 std::string outFile = "-"; 77 if (!extension.empty() && (inputFilename != "-")) { 78 llvm::SmallString<128> path(inputFilename); 79 llvm::sys::path::replace_extension(path, extension); 80 outFile = std::string(path.str()); 81 } 82 83 return outFile; 84 } 85 86 std::unique_ptr<llvm::raw_pwrite_stream> 87 CompilerInstance::CreateDefaultOutputFile( 88 bool binary, llvm::StringRef baseName, llvm::StringRef extension) { 89 std::string outputPathName; 90 std::error_code ec; 91 92 // Get the path of the output file 93 std::string outputFilePath = 94 GetOutputFilePath(frontendOpts().outputFile_, baseName, extension); 95 96 // Create the output file 97 std::unique_ptr<llvm::raw_pwrite_stream> os = 98 CreateOutputFile(outputFilePath, ec, binary); 99 100 // Add the file to the list of tracked output files (provided it was created 101 // successfully) 102 if (os) 103 AddOutputFile(OutputFile(outputPathName)); 104 105 return os; 106 } 107 108 std::unique_ptr<llvm::raw_pwrite_stream> CompilerInstance::CreateOutputFile( 109 llvm::StringRef outputFilePath, std::error_code &error, bool binary) { 110 111 // Creates the file descriptor for the output file 112 std::unique_ptr<llvm::raw_fd_ostream> os; 113 std::string osFile; 114 if (!os) { 115 osFile = outputFilePath; 116 os.reset(new llvm::raw_fd_ostream(osFile, error, 117 (binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_Text))); 118 if (error) 119 return nullptr; 120 } 121 122 // Return the stream corresponding to the output file. 123 // For non-seekable streams, wrap it in llvm::buffer_ostream first. 124 if (!binary || os->supportsSeeking()) 125 return std::move(os); 126 127 assert(!nonSeekStream_ && "The non-seek stream has already been set!"); 128 auto b = std::make_unique<llvm::buffer_ostream>(*os); 129 nonSeekStream_ = std::move(os); 130 return std::move(b); 131 } 132 133 void CompilerInstance::ClearOutputFiles(bool eraseFiles) { 134 for (OutputFile &of : outputFiles_) 135 if (!of.filename_.empty() && eraseFiles) 136 llvm::sys::fs::remove(of.filename_); 137 138 outputFiles_.clear(); 139 nonSeekStream_.reset(); 140 } 141 142 bool CompilerInstance::ExecuteAction(FrontendAction &act) { 143 auto &invoc = this->invocation(); 144 145 // Set some sane defaults for the frontend. 146 invoc.SetDefaultFortranOpts(); 147 // Update the fortran options based on user-based input. 148 invoc.setFortranOpts(); 149 // Set semantic options 150 invoc.setSemanticsOpts(this->semanticsContext()); 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