[cfe-commits] [PATCH] Make ASTConsumers take file handles instead of file names

Eli Friedman eli.friedman at gmail.com
Sun May 17 19:46:33 PDT 2009


Patch attached.  There's one issue that I haven't worked out how to
fix: this causes a failure in
clang/test/CodeGen/2008-07-17-no-emit-on-error.c.

Does this look like a good direction?  Does anyone have suggestions
for how to make sure the file gets deleted on failure?  Or actually,
do we even care, considering that the clang driver does the right
thing anyway?

-Eli
-------------- next part --------------
Index: tools/clang-cc/clang-cc.cpp
===================================================================
--- tools/clang-cc/clang-cc.cpp	(revision 71992)
+++ tools/clang-cc/clang-cc.cpp	(working copy)
@@ -59,10 +59,12 @@
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/PluginLoader.h"
 #include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Streams.h"
 #include "llvm/Support/Timer.h"
 #include "llvm/System/Host.h"
 #include "llvm/System/Path.h"
 #include "llvm/System/Process.h"
+#include "llvm/System/Program.h"
 #include "llvm/System/Signals.h"
 #include <cstdlib>
 #if HAVE_SYS_TYPES_H
@@ -1571,6 +1573,42 @@
 // Main driver
 //===----------------------------------------------------------------------===//
 
+static llvm::raw_ostream* ComputeOutFile(const std::string& InFile,
+                                         const char* Extension,
+                                         bool Binary) {
+  llvm::raw_ostream* Ret;
+  bool UseStdout = false;
+  std::string OutFile;
+  if (OutputFile == "-" || (OutputFile.empty() && InFile == "-")) {
+    UseStdout = true;
+  } else if (!OutputFile.empty()) {
+    OutFile = OutputFile;
+  } else if (Extension) {
+    llvm::sys::Path Path(InFile);
+    Path.eraseSuffix();
+    Path.appendSuffix(Extension);
+    OutFile = Path.toString();
+  } else {
+    UseStdout = true;
+  }
+
+  if (UseStdout) {
+    Ret = new llvm::raw_stdout_ostream();
+    if (Binary)
+      llvm::sys::Program::ChangeStdoutToBinary();
+  } else {
+    std::string Error;
+    Ret = new llvm::raw_fd_ostream(OutFile.c_str(), Binary, Error);
+    if (!Error.empty()) {
+      // FIXME: Don't fail this way.
+      llvm::cerr << "ERROR: " << Error << "\n";
+      ::exit(1);
+    }
+  }
+
+  return Ret;
+}
+
 /// CreateASTConsumer - Create the ASTConsumer for the corresponding program
 /// action.  These consumers can operate on both ASTs that are freshly
 /// parsed from source files as well as those deserialized from Bitcode.
@@ -1581,12 +1619,15 @@
                                       const llvm::StringMap<bool>& Features,
                                       Preprocessor *PP,
                                       PreprocessorFactory *PPF) {
+  // FIXME: This file handle leaks; figure out a way to handle the ownership
+  llvm::raw_ostream* OS = 0;
   switch (ProgAction) {
   default:
     return NULL;
     
   case ASTPrint:
-    return CreateASTPrinter();
+    OS = ComputeOutFile(InFile, 0, false);
+    return CreateASTPrinter(OS);
     
   case ASTDump:
     return CreateASTDumper(false);
@@ -1599,41 +1640,48 @@
 
   case PrintDeclContext:
     return CreateDeclContextPrinter();
-    
+
   case EmitHTML:
-    return CreateHTMLPrinter(OutputFile, Diag, PP, PPF);
+    OS = ComputeOutFile(InFile, 0, true);
+    return CreateHTMLPrinter(OS, Diag, PP, PPF);
 
   case InheritanceView:
     return CreateInheritanceViewer(InheritanceViewCls);
-    
+
   case EmitAssembly:
   case EmitLLVM:
   case EmitBC: 
   case EmitLLVMOnly: {
     BackendAction Act;
-    if (ProgAction == EmitAssembly)
+    if (ProgAction == EmitAssembly) {
       Act = Backend_EmitAssembly;
-    else if (ProgAction == EmitLLVM)
+      OS = ComputeOutFile(InFile, "s", true);
+    } else if (ProgAction == EmitLLVM) {
       Act = Backend_EmitLL;
-    else if (ProgAction == EmitLLVMOnly)
+      OS = ComputeOutFile(InFile, "ll", true);
+    } else if (ProgAction == EmitLLVMOnly) {
       Act = Backend_EmitNothing;
-    else
+      OS = 0;
+    } else {
       Act = Backend_EmitBC;
-    
+      OS = ComputeOutFile(InFile, "bc", true);
+    }
+
     CompileOptions Opts;
     InitializeCompileOptions(Opts, LangOpts, Features);
-    return CreateBackendConsumer(Act, Diag, LangOpts, Opts, 
-                                 InFile, OutputFile);
+    return CreateBackendConsumer(Act, Diag, LangOpts, Opts, InFile, OS);
   }
 
   case GeneratePCH:
-    return CreatePCHGenerator(*PP, OutputFile);    
+    OS = ComputeOutFile(InFile, 0, true);
+    return CreatePCHGenerator(*PP, OS);
 
   case RewriteObjC:
-    return CreateCodeRewriterTest(InFile, OutputFile, Diag, LangOpts);
+    return CreateCodeRewriterTest(InFile, ComputeOutFile(InFile, "cpp", true),
+                                  Diag, LangOpts);
 
   case RewriteBlocks:
-    return CreateBlockRewriter(InFile, OutputFile, Diag, LangOpts);
+    return CreateBlockRewriter(InFile, Diag, LangOpts);
     
   case RunAnalysis:
     return CreateAnalysisConsumer(Diag, PP, PPF, LangOpts, OutputFile);
Index: tools/clang-cc/RewriteObjC.cpp
===================================================================
--- tools/clang-cc/RewriteObjC.cpp	(revision 71992)
+++ tools/clang-cc/RewriteObjC.cpp	(working copy)
@@ -93,7 +93,7 @@
     bool IsHeader;
     
     std::string InFileName;
-    std::string OutFileName;
+    llvm::raw_ostream* OutFile;
      
     std::string Preamble;
 
@@ -136,7 +136,7 @@
     }
     void HandleTopLevelSingleDecl(Decl *D);
     void HandleDeclInMainFile(Decl *D);
-    RewriteObjC(std::string inFile, std::string outFile,
+    RewriteObjC(std::string inFile, llvm::raw_ostream *OS,
                 Diagnostic &D, const LangOptions &LOpts);
 
     ~RewriteObjC() {}
@@ -416,12 +416,10 @@
   return Ext == "h" || Ext == "hh" || Ext == "H";
 }    
 
-RewriteObjC::RewriteObjC(std::string inFile, std::string outFile,
+RewriteObjC::RewriteObjC(std::string inFile, llvm::raw_ostream* OS,
                          Diagnostic &D, const LangOptions &LOpts)
-      : Diags(D), LangOpts(LOpts) {
+      : Diags(D), LangOpts(LOpts), InFileName(inFile), OutFile(OS) {
   IsHeader = IsHeaderFile(inFile);
-  InFileName = inFile;
-  OutFileName = outFile;
   RewriteFailedDiag = Diags.getCustomDiagID(Diagnostic::Warning, 
                "rewriting sub-expression within a macro (may not be correct)");
   TryFinallyContainsReturnDiag = Diags.getCustomDiagID(Diagnostic::Warning, 
@@ -430,10 +428,10 @@
 }
 
 ASTConsumer *clang::CreateCodeRewriterTest(const std::string& InFile,
-                                           const std::string& OutFile,
+                                           llvm::raw_ostream* OS,
                                            Diagnostic &Diags, 
                                            const LangOptions &LOpts) {
-  return new RewriteObjC(InFile, OutFile, Diags, LOpts);
+  return new RewriteObjC(InFile, OS, Diags, LOpts);
 }
 
 void RewriteObjC::Initialize(ASTContext &context) {
@@ -4654,34 +4652,7 @@
   
   if (Diags.hasErrorOccurred())
     return;
-
-  // Create the output file.
   
-  llvm::OwningPtr<llvm::raw_ostream> OwnedStream;
-  llvm::raw_ostream *OutFile;
-  if (OutFileName == "-") {
-    OutFile = &llvm::outs();
-  } else if (!OutFileName.empty()) {
-    std::string Err;
-    OutFile = new llvm::raw_fd_ostream(OutFileName.c_str(), 
-                                       // set binary mode (critical for Windoze)
-                                       true, 
-                                       Err);
-    OwnedStream.reset(OutFile);
-  } else if (InFileName == "-") {
-    OutFile = &llvm::outs();
-  } else {
-    llvm::sys::Path Path(InFileName);
-    Path.eraseSuffix();
-    Path.appendSuffix("cpp");
-    std::string Err;
-    OutFile = new llvm::raw_fd_ostream(Path.toString().c_str(), 
-                                       // set binary mode (critical for Windoze)
-                                       true, 
-                                       Err);
-    OwnedStream.reset(OutFile);
-  }
-  
   RewriteInclude();
   
   // Here's a great place to add any extra declarations that may be needed.
Index: tools/clang-cc/RewriteBlocks.cpp
===================================================================
--- tools/clang-cc/RewriteBlocks.cpp	(revision 71992)
+++ tools/clang-cc/RewriteBlocks.cpp	(working copy)
@@ -57,12 +57,10 @@
   ObjCMethodDecl *CurMethodDef;
   
   bool IsHeader;
-  std::string InFileName;
-  std::string OutFileName;
   
   std::string Preamble;
 public:
-  RewriteBlocks(std::string inFile, std::string outFile, Diagnostic &D, 
+  RewriteBlocks(std::string inFile, Diagnostic &D, 
                 const LangOptions &LOpts);
   ~RewriteBlocks() {
     // Get the buffer corresponding to MainFileID.  
@@ -169,12 +167,10 @@
   return Ext == "h" || Ext == "hh" || Ext == "H";
 }    
 
-RewriteBlocks::RewriteBlocks(std::string inFile, std::string outFile, 
+RewriteBlocks::RewriteBlocks(std::string inFile,
                              Diagnostic &D, const LangOptions &LOpts) : 
   Diags(D), LangOpts(LOpts) {
   IsHeader = IsHeaderFile(inFile);
-  InFileName = inFile;
-  OutFileName = outFile;
   CurFunctionDef = 0;
   CurMethodDef = 0;
   RewriteFailedDiag = Diags.getCustomDiagID(Diagnostic::Warning, 
@@ -182,10 +178,9 @@
 }
 
 ASTConsumer *clang::CreateBlockRewriter(const std::string& InFile,
-                                        const std::string& OutFile,
                                         Diagnostic &Diags,
                                         const LangOptions &LangOpts) {
-  return new RewriteBlocks(InFile, OutFile, Diags, LangOpts);
+  return new RewriteBlocks(InFile, Diags, LangOpts);
 }
 
 void RewriteBlocks::Initialize(ASTContext &context) {
Index: tools/clang-cc/HTMLPrint.cpp
===================================================================
--- tools/clang-cc/HTMLPrint.cpp	(revision 71992)
+++ tools/clang-cc/HTMLPrint.cpp	(working copy)
@@ -30,25 +30,25 @@
 namespace {
   class HTMLPrinter : public ASTConsumer {
     Rewriter R;
-    std::string OutFilename;
+    llvm::raw_ostream *Out;
     Diagnostic &Diags;
     Preprocessor *PP;
     PreprocessorFactory *PPF;
   public:
-    HTMLPrinter(const std::string &OutFile, Diagnostic &D, Preprocessor *pp,
+    HTMLPrinter(llvm::raw_ostream *OS, Diagnostic &D, Preprocessor *pp,
                 PreprocessorFactory* ppf)
-      : OutFilename(OutFile), Diags(D), PP(pp), PPF(ppf) {}
+      : Out(OS), Diags(D), PP(pp), PPF(ppf) {}
     virtual ~HTMLPrinter();
     
     void Initialize(ASTContext &context);
   };
 }
 
-ASTConsumer* clang::CreateHTMLPrinter(const std::string &OutFile, 
+ASTConsumer* clang::CreateHTMLPrinter(llvm::raw_ostream *OS,
                                       Diagnostic &D, Preprocessor *PP,
                                       PreprocessorFactory* PPF) {
   
-  return new HTMLPrinter(OutFile, D, PP, PPF);
+  return new HTMLPrinter(OS, D, PP, PPF);
 }
 
 void HTMLPrinter::Initialize(ASTContext &context) {
@@ -73,25 +73,11 @@
   if (PP) html::SyntaxHighlight(R, FID, *PP);
   if (PPF) html::HighlightMacros(R, FID, *PP);
   html::EscapeText(R, FID, false, true);
-  
-  // Open the output.
-  FILE *OutputFILE;
-  if (OutFilename.empty() || OutFilename == "-")
-    OutputFILE = stdout;
-  else {
-    OutputFILE = fopen(OutFilename.c_str(), "w+");
-    if (OutputFILE == 0) {
-      fprintf(stderr, "Error opening output file '%s'.\n", OutFilename.c_str());
-      exit(1);
-    }
-  }
-  
+
   // Emit the HTML.
   const RewriteBuffer &RewriteBuf = R.getEditBuffer(FID);
   char *Buffer = (char*)malloc(RewriteBuf.size());
   std::copy(RewriteBuf.begin(), RewriteBuf.end(), Buffer);
-  fwrite(Buffer, 1, RewriteBuf.size(), OutputFILE);
+  Out->write(Buffer, RewriteBuf.size());
   free(Buffer);
-  
-  if (OutputFILE != stdout) fclose(OutputFILE);
 }
Index: tools/clang-cc/GeneratePCH.cpp
===================================================================
--- tools/clang-cc/GeneratePCH.cpp	(revision 71992)
+++ tools/clang-cc/GeneratePCH.cpp	(working copy)
@@ -32,19 +32,19 @@
 namespace {
   class VISIBILITY_HIDDEN PCHGenerator : public SemaConsumer {
     const Preprocessor &PP;
-    std::string OutFile;
+    llvm::raw_ostream *Out;
     Sema *SemaPtr;
     MemorizeStatCalls *StatCalls; // owned by the FileManager
 
   public:
-    explicit PCHGenerator(const Preprocessor &PP, const std::string &OutFile);
+    explicit PCHGenerator(const Preprocessor &PP, llvm::raw_ostream *Out);
     virtual void InitializeSema(Sema &S) { SemaPtr = &S; }
     virtual void HandleTranslationUnit(ASTContext &Ctx);
   };
 }
 
-PCHGenerator::PCHGenerator(const Preprocessor &PP, const std::string &OutFile)
-  : PP(PP), OutFile(OutFile), SemaPtr(0), StatCalls(0) { 
+PCHGenerator::PCHGenerator(const Preprocessor &PP, llvm::raw_ostream *OS)
+  : PP(PP), Out(OS), SemaPtr(0), StatCalls(0) { 
 
   // Install a stat() listener to keep track of all of the stat()
   // calls.
@@ -65,23 +65,14 @@
   assert(SemaPtr && "No Sema?");
   Writer.WritePCH(*SemaPtr, StatCalls);
 
-  // Open up the PCH file.
-  std::string ErrMsg;
-  llvm::raw_fd_ostream Out(OutFile.c_str(), true, ErrMsg);
-  
-  if (!ErrMsg.empty()) {
-    llvm::errs() << "PCH error: " << ErrMsg << "\n";
-    return;
-  }
-
   // Write the generated bitstream to "Out".
-  Out.write((char *)&Buffer.front(), Buffer.size());
+  Out->write((char *)&Buffer.front(), Buffer.size());
 
   // Make sure it hits disk now.
-  Out.flush();
+  Out->flush();
 }
 
 ASTConsumer *clang::CreatePCHGenerator(const Preprocessor &PP,
-                                       const std::string &OutFile) {
-  return new PCHGenerator(PP, OutFile);
+                                       llvm::raw_ostream *OS) {
+  return new PCHGenerator(PP, OS);
 }
Index: tools/clang-cc/Backend.cpp
===================================================================
--- tools/clang-cc/Backend.cpp	(revision 71992)
+++ tools/clang-cc/Backend.cpp	(working copy)
@@ -42,8 +42,7 @@
   class VISIBILITY_HIDDEN BackendConsumer : public ASTConsumer {
     BackendAction Action;
     CompileOptions CompileOpts;
-    const std::string &InputFile;
-    std::string OutputFile;
+    llvm::raw_ostream *AsmOutStream;
     ASTContext *Context;
 
     Timer LLVMIRGeneration;
@@ -53,7 +52,6 @@
     
     llvm::Module *TheModule;
     llvm::TargetData *TheTargetData;
-    llvm::raw_ostream *AsmOutStream;
 
     mutable llvm::ModuleProvider *ModuleProvider;
     mutable FunctionPassManager *CodeGenPasses;
@@ -78,15 +76,14 @@
   public:  
     BackendConsumer(BackendAction action, Diagnostic &Diags, 
                     const LangOptions &langopts, const CompileOptions &compopts,
-                    const std::string &infile, const std::string &outfile) :
+                    const std::string &infile, llvm::raw_ostream* OS) :
       Action(action), 
       CompileOpts(compopts),
-      InputFile(infile), 
-      OutputFile(outfile), 
+      AsmOutStream(OS), 
       LLVMIRGeneration("LLVM IR Generation Time"),
       CodeGenerationTime("Code Generation Time"),
-      Gen(CreateLLVMCodeGen(Diags, InputFile, compopts)),
-      TheModule(0), TheTargetData(0), AsmOutStream(0), ModuleProvider(0),
+      Gen(CreateLLVMCodeGen(Diags, infile, compopts)),
+      TheModule(0), TheTargetData(0), ModuleProvider(0),
       CodeGenPasses(0), PerModulePasses(0), PerFunctionPasses(0) {
       
       // Enable -time-passes if -ftime-report is enabled.
@@ -196,28 +193,6 @@
   if (Action == Backend_EmitNothing)
     return true;
 
-  if (OutputFile == "-" || (InputFile == "-" && OutputFile.empty())) {
-    AsmOutStream = new raw_stdout_ostream();
-    sys::Program::ChangeStdoutToBinary();
-  } else {
-    if (OutputFile.empty()) {
-      llvm::sys::Path Path(InputFile);
-      Path.eraseSuffix();
-      if (Action == Backend_EmitBC) {
-        Path.appendSuffix("bc");
-      } else if (Action == Backend_EmitLL) {
-        Path.appendSuffix("ll");
-      } else {
-        Path.appendSuffix("s");
-      }
-      OutputFile = Path.toString();
-    }
-
-    AsmOutStream = new raw_fd_ostream(OutputFile.c_str(), true, Error);
-    if (!Error.empty())
-      return false;
-  }
-
   if (Action == Backend_EmitBC) {
     getPerModulePasses()->add(createBitcodeWriterPass(*AsmOutStream));
   } else if (Action == Backend_EmitLL) {
@@ -435,7 +410,6 @@
                                           const LangOptions &LangOpts,
                                           const CompileOptions &CompileOpts,
                                           const std::string& InFile,
-                                          const std::string& OutFile) {
-  return new BackendConsumer(Action, Diags, LangOpts, CompileOpts,
-                             InFile, OutFile);  
+                                          llvm::raw_ostream* OS) {
+  return new BackendConsumer(Action, Diags, LangOpts, CompileOpts, InFile, OS);
 }
Index: tools/clang-cc/ASTConsumers.h
===================================================================
--- tools/clang-cc/ASTConsumers.h	(revision 71992)
+++ tools/clang-cc/ASTConsumers.h	(working copy)
@@ -36,7 +36,7 @@
 // original C code.  The output is intended to be in a format such that
 // clang could re-parse the output back into the same AST, but the
 // implementation is still incomplete.
-ASTConsumer *CreateASTPrinter(llvm::raw_ostream* OS = NULL);
+ASTConsumer *CreateASTPrinter(llvm::raw_ostream* OS);
 
 // AST dumper: dumps the raw AST in human-readable form to stderr; this is
 // intended for debugging. A normal dump is done with FullDump = false;
@@ -56,7 +56,7 @@
 // ObjC rewriter: attempts tp rewrite ObjC constructs into pure C code.
 // This is considered experimental, and only works with Apple's ObjC runtime.
 ASTConsumer *CreateCodeRewriterTest(const std::string& InFile,
-                                    const std::string& OutFile,
+                                    llvm::raw_ostream* OS,
                                     Diagnostic &Diags,
                                     const LangOptions &LOpts);
 
@@ -74,23 +74,22 @@
                                    const LangOptions &Features,
                                    const CompileOptions &CompileOpts,
                                    const std::string &InFile,
-                                   const std::string &OutFile);
+                                   llvm::raw_ostream *OS);
 
 // HTML printer: uses the rewriter to convert source code to HTML with
 // syntax highlighting suitable for viewing in a web-browser.
-ASTConsumer* CreateHTMLPrinter(const std::string &OutFile, Diagnostic &D,
+ASTConsumer* CreateHTMLPrinter(llvm::raw_ostream *OS, Diagnostic &D,
                                Preprocessor *PP, PreprocessorFactory *PPF);
 
 // PCH generator: generates a precompiled header file; this file can be
 // used later with the PCHReader (clang-cc option -include-pch)
 // to speed up compile times.
 ASTConsumer *CreatePCHGenerator(const Preprocessor &PP,
-                                const std::string &OutFile);
+                                llvm::raw_ostream *OS);
 
 // Block rewriter: rewrites code using the Apple blocks extension to pure
-// C code.
+// C code.  Output is always sent to stdout.
 ASTConsumer *CreateBlockRewriter(const std::string &InFile,
-                                 const std::string &OutFile,
                                  Diagnostic &Diags,
                                  const LangOptions &LangOpts);
 


More information about the cfe-commits mailing list