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 "llvm/Support/Errc.h" 15 #include "llvm/Support/Error.h" 16 #include "llvm/Support/FileSystem.h" 17 #include "llvm/Support/Path.h" 18 #include "llvm/Support/raw_ostream.h" 19 20 using namespace Fortran::frontend; 21 22 CompilerInstance::CompilerInstance() 23 : invocation_(new CompilerInvocation()), 24 allSources_(new Fortran::parser::AllSources()), 25 allCookedSources_(new Fortran::parser::AllCookedSources(*allSources_)), 26 parsing_(new Fortran::parser::Parsing(*allCookedSources_)) { 27 28 // TODO: This is a good default during development, but ultimately we should 29 // give the user the opportunity to specify this. 30 allSources_->set_encoding(Fortran::parser::Encoding::UTF_8); 31 } 32 33 CompilerInstance::~CompilerInstance() { 34 assert(outputFiles_.empty() && "Still output files in flight?"); 35 } 36 37 void CompilerInstance::set_invocation( 38 std::shared_ptr<CompilerInvocation> value) { 39 invocation_ = std::move(value); 40 } 41 42 void CompilerInstance::AddOutputFile(OutputFile &&outFile) { 43 outputFiles_.push_back(std::move(outFile)); 44 } 45 46 // Helper method to generate the path of the output file. The following logic 47 // applies: 48 // 1. If the user specifies the output file via `-o`, then use that (i.e. 49 // the outputFilename parameter). 50 // 2. If the user does not specify the name of the output file, derive it from 51 // the input file (i.e. inputFilename + extension) 52 // 3. If the output file is not specified and the input file is `-`, then set 53 // the output file to `-` as well. 54 static std::string GetOutputFilePath(llvm::StringRef outputFilename, 55 llvm::StringRef inputFilename, llvm::StringRef extension) { 56 57 // Output filename _is_ specified. Just use that. 58 if (!outputFilename.empty()) 59 return std::string(outputFilename); 60 61 // Output filename _is not_ specified. Derive it from the input file name. 62 std::string outFile = "-"; 63 if (!extension.empty() && (inputFilename != "-")) { 64 llvm::SmallString<128> path(inputFilename); 65 llvm::sys::path::replace_extension(path, extension); 66 outFile = std::string(path.str()); 67 } 68 69 return outFile; 70 } 71 72 std::unique_ptr<llvm::raw_pwrite_stream> 73 CompilerInstance::CreateDefaultOutputFile( 74 bool binary, llvm::StringRef baseName, llvm::StringRef extension) { 75 std::string outputPathName; 76 std::error_code ec; 77 78 // Get the path of the output file 79 std::string outputFilePath = 80 GetOutputFilePath(frontendOpts().outputFile_, baseName, extension); 81 82 // Create the output file 83 std::unique_ptr<llvm::raw_pwrite_stream> os = 84 CreateOutputFile(outputFilePath, ec, binary); 85 86 // Add the file to the list of tracked output files (provided it was created 87 // successfully) 88 if (os) 89 AddOutputFile(OutputFile(outputPathName)); 90 91 return os; 92 } 93 94 std::unique_ptr<llvm::raw_pwrite_stream> CompilerInstance::CreateOutputFile( 95 llvm::StringRef outputFilePath, std::error_code &error, bool binary) { 96 97 // Creates the file descriptor for the output file 98 std::unique_ptr<llvm::raw_fd_ostream> os; 99 std::string osFile; 100 if (!os) { 101 osFile = outputFilePath; 102 os.reset(new llvm::raw_fd_ostream(osFile, error, 103 (binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_Text))); 104 if (error) 105 return nullptr; 106 } 107 108 // Return the stream corresponding to the output file. 109 // For non-seekable streams, wrap it in llvm::buffer_ostream first. 110 if (!binary || os->supportsSeeking()) 111 return std::move(os); 112 113 assert(!nonSeekStream_ && "The non-seek stream has already been set!"); 114 auto b = std::make_unique<llvm::buffer_ostream>(*os); 115 nonSeekStream_ = std::move(os); 116 return std::move(b); 117 } 118 119 void CompilerInstance::ClearOutputFiles(bool eraseFiles) { 120 for (OutputFile &of : outputFiles_) 121 if (!of.filename_.empty() && eraseFiles) 122 llvm::sys::fs::remove(of.filename_); 123 124 outputFiles_.clear(); 125 nonSeekStream_.reset(); 126 } 127 128 bool CompilerInstance::ExecuteAction(FrontendAction &act) { 129 // Set some sane defaults for the frontend. 130 // TODO: Instead of defaults we should be setting these options based on the 131 // user input. 132 this->invocation().SetDefaultFortranOpts(); 133 134 // Connect Input to a CompileInstance 135 for (const FrontendInputFile &fif : frontendOpts().inputs_) { 136 if (act.BeginSourceFile(*this, fif)) { 137 if (llvm::Error err = act.Execute()) { 138 consumeError(std::move(err)); 139 } 140 act.EndSourceFile(); 141 } 142 } 143 return true; 144 } 145 146 void CompilerInstance::CreateDiagnostics( 147 clang::DiagnosticConsumer *client, bool shouldOwnClient) { 148 diagnostics_ = 149 CreateDiagnostics(&GetDiagnosticOpts(), client, shouldOwnClient); 150 } 151 152 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> 153 CompilerInstance::CreateDiagnostics(clang::DiagnosticOptions *opts, 154 clang::DiagnosticConsumer *client, bool shouldOwnClient) { 155 clang::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID( 156 new clang::DiagnosticIDs()); 157 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags( 158 new clang::DiagnosticsEngine(diagID, opts)); 159 160 // Create the diagnostic client for reporting errors or for 161 // implementing -verify. 162 if (client) { 163 diags->setClient(client, shouldOwnClient); 164 } else { 165 diags->setClient(new TextDiagnosticPrinter(llvm::errs(), opts)); 166 } 167 return diags; 168 } 169