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