diff options
author | ed <ed@FreeBSD.org> | 2009-06-02 17:58:47 +0000 |
---|---|---|
committer | ed <ed@FreeBSD.org> | 2009-06-02 17:58:47 +0000 |
commit | f27e5a09a0d815b8a4814152954ff87dadfdefc0 (patch) | |
tree | ce7d964cbb5e39695b71481698f10cb099c23d4a /tools | |
download | FreeBSD-src-f27e5a09a0d815b8a4814152954ff87dadfdefc0.zip FreeBSD-src-f27e5a09a0d815b8a4814152954ff87dadfdefc0.tar.gz |
Import Clang, at r72732.
Diffstat (limited to 'tools')
-rw-r--r-- | tools/CMakeLists.txt | 2 | ||||
-rw-r--r-- | tools/Makefile | 13 | ||||
-rw-r--r-- | tools/clang-cc/CMakeLists.txt | 26 | ||||
-rw-r--r-- | tools/clang-cc/Makefile | 32 | ||||
-rw-r--r-- | tools/clang-cc/clang-cc.cpp | 2293 | ||||
-rw-r--r-- | tools/driver/CMakeLists.txt | 12 | ||||
-rw-r--r-- | tools/driver/Makefile | 23 | ||||
-rw-r--r-- | tools/driver/driver.cpp | 228 | ||||
-rw-r--r-- | tools/scan-view/Reporter.py | 248 | ||||
-rw-r--r-- | tools/scan-view/Resources/FileRadar.scpt | bin | 0 -> 18418 bytes | |||
-rw-r--r-- | tools/scan-view/Resources/GetRadarVersion.scpt | 0 | ||||
-rw-r--r-- | tools/scan-view/Resources/bugcatcher.ico | bin | 0 -> 318 bytes | |||
-rw-r--r-- | tools/scan-view/ScanView.py | 770 | ||||
-rwxr-xr-x | tools/scan-view/scan-view | 131 | ||||
-rw-r--r-- | tools/scan-view/startfile.py | 203 |
15 files changed, 3981 insertions, 0 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..6c66dea --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(clang-cc) +add_subdirectory(driver) diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 0000000..43124ba --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,13 @@ +##===- tools/Makefile --------------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL := ../../.. +DIRS := clang-cc driver + +include $(LEVEL)/Makefile.common diff --git a/tools/clang-cc/CMakeLists.txt b/tools/clang-cc/CMakeLists.txt new file mode 100644 index 0000000..ec2ea3d --- /dev/null +++ b/tools/clang-cc/CMakeLists.txt @@ -0,0 +1,26 @@ +set(LLVM_NO_RTTI 1) + +set( LLVM_USED_LIBS + clangFrontend + clangCodeGen + clangAnalysis + clangRewrite + clangSema + clangAST + clangParse + clangLex + clangBasic + ) + +set( LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + bitreader + bitwriter + codegen + ipo + selectiondag + ) + +add_clang_executable(clang-cc + clang-cc.cpp + ) diff --git a/tools/clang-cc/Makefile b/tools/clang-cc/Makefile new file mode 100644 index 0000000..874a42f --- /dev/null +++ b/tools/clang-cc/Makefile @@ -0,0 +1,32 @@ +##===- tools/clang-cc/Makefile -----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +LEVEL = ../../../.. + +TOOLNAME = clang-cc +CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../include +CXXFLAGS = -fno-rtti + +# Clang has no plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +# Include this here so we can get the configuration of the targets +# that have been configured for construction. We have to do this +# early so we can set up LINK_COMPONENTS before including Makefile.rules +include $(LEVEL)/Makefile.config + +LINK_COMPONENTS := $(TARGETS_TO_BUILD) bitreader bitwriter codegen ipo selectiondag +USEDLIBS = clangFrontend.a clangCodeGen.a clangAnalysis.a \ + clangRewrite.a clangSema.a clangAST.a clangParse.a \ + clangLex.a clangBasic.a + +# clang-cc lives in a special location; we can get away with this +# because nothing else gets installed from here. +PROJ_bindir := $(DESTDIR)$(PROJ_prefix)/libexec + +include $(LLVM_SRC_ROOT)/Makefile.rules diff --git a/tools/clang-cc/clang-cc.cpp b/tools/clang-cc/clang-cc.cpp new file mode 100644 index 0000000..a0ccafa --- /dev/null +++ b/tools/clang-cc/clang-cc.cpp @@ -0,0 +1,2293 @@ +//===--- clang.cpp - C-Language Front-end ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This utility may be invoked in the following manner: +// clang --help - Output help info. +// clang [options] - Read from stdin. +// clang [options] file - Read from "file". +// clang [options] file1 file2 - Read these files. +// +//===----------------------------------------------------------------------===// +// +// TODO: Options to support: +// +// -Wfatal-errors +// -ftabstop=width +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/AnalysisConsumer.h" +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/CompileOptions.h" +#include "clang/Frontend/FixItRewriter.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/InitHeaderSearch.h" +#include "clang/Frontend/InitPreprocessor.h" +#include "clang/Frontend/PathDiagnosticClients.h" +#include "clang/Frontend/PCHReader.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/Utils.h" +#include "clang/Analysis/PathDiagnostic.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Sema/ParseAST.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclGroup.h" +#include "clang/Parse/Parser.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/LexDiagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Version.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Config/config.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#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 +# include <sys/types.h> +#endif + +using namespace clang; + +//===----------------------------------------------------------------------===// +// Source Location Parser +//===----------------------------------------------------------------------===// + +/// \brief A source location that has been parsed on the command line. +struct ParsedSourceLocation { + std::string FileName; + unsigned Line; + unsigned Column; + + /// \brief Try to resolve the file name of a parsed source location. + /// + /// \returns true if there was an error, false otherwise. + bool ResolveLocation(FileManager &FileMgr, RequestedSourceLocation &Result); +}; + +bool +ParsedSourceLocation::ResolveLocation(FileManager &FileMgr, + RequestedSourceLocation &Result) { + const FileEntry *File = FileMgr.getFile(FileName); + if (!File) + return true; + + Result.File = File; + Result.Line = Line; + Result.Column = Column; + return false; +} + +namespace llvm { + namespace cl { + /// \brief Command-line option parser that parses source locations. + /// + /// Source locations are of the form filename:line:column. + template<> + class parser<ParsedSourceLocation> + : public basic_parser<ParsedSourceLocation> { + public: + bool parse(Option &O, const char *ArgName, + const std::string &ArgValue, + ParsedSourceLocation &Val); + }; + + bool + parser<ParsedSourceLocation>:: + parse(Option &O, const char *ArgName, const std::string &ArgValue, + ParsedSourceLocation &Val) { + using namespace clang; + + const char *ExpectedFormat + = "source location must be of the form filename:line:column"; + std::string::size_type SecondColon = ArgValue.rfind(':'); + if (SecondColon == std::string::npos) { + std::fprintf(stderr, "%s\n", ExpectedFormat); + return true; + } + char *EndPtr; + long Column + = std::strtol(ArgValue.c_str() + SecondColon + 1, &EndPtr, 10); + if (EndPtr != ArgValue.c_str() + ArgValue.size()) { + std::fprintf(stderr, "%s\n", ExpectedFormat); + return true; + } + + std::string::size_type FirstColon = ArgValue.rfind(':', SecondColon-1); + if (SecondColon == std::string::npos) { + std::fprintf(stderr, "%s\n", ExpectedFormat); + return true; + } + long Line = std::strtol(ArgValue.c_str() + FirstColon + 1, &EndPtr, 10); + if (EndPtr != ArgValue.c_str() + SecondColon) { + std::fprintf(stderr, "%s\n", ExpectedFormat); + return true; + } + + Val.FileName = ArgValue.substr(0, FirstColon); + Val.Line = Line; + Val.Column = Column; + return false; + } + } +} + +//===----------------------------------------------------------------------===// +// Global options. +//===----------------------------------------------------------------------===// + +/// ClangFrontendTimer - The front-end activities should charge time to it with +/// TimeRegion. The -ftime-report option controls whether this will do +/// anything. +llvm::Timer *ClangFrontendTimer = 0; + +static bool HadErrors = false; + +static llvm::cl::opt<bool> +Verbose("v", llvm::cl::desc("Enable verbose output")); +static llvm::cl::opt<bool> +Stats("print-stats", + llvm::cl::desc("Print performance metrics and statistics")); +static llvm::cl::opt<bool> +DisableFree("disable-free", + llvm::cl::desc("Disable freeing of memory on exit"), + llvm::cl::init(false)); +static llvm::cl::opt<bool> +EmptyInputOnly("empty-input-only", + llvm::cl::desc("Force running on an empty input file")); + +enum ProgActions { + RewriteObjC, // ObjC->C Rewriter. + RewriteBlocks, // ObjC->C Rewriter for Blocks. + RewriteMacros, // Expand macros but not #includes. + RewriteTest, // Rewriter playground + FixIt, // Fix-It Rewriter + HTMLTest, // HTML displayer testing stuff. + EmitAssembly, // Emit a .s file. + EmitLLVM, // Emit a .ll file. + EmitBC, // Emit a .bc file. + EmitLLVMOnly, // Generate LLVM IR, but do not + EmitHTML, // Translate input source into HTML. + ASTPrint, // Parse ASTs and print them. + ASTPrintXML, // Parse ASTs and print them in XML. + ASTDump, // Parse ASTs and dump them. + ASTView, // Parse ASTs and view them in Graphviz. + PrintDeclContext, // Print DeclContext and their Decls. + ParsePrintCallbacks, // Parse and print each callback. + ParseSyntaxOnly, // Parse and perform semantic analysis. + ParseNoop, // Parse with noop callbacks. + RunPreprocessorOnly, // Just lex, no output. + PrintPreprocessedInput, // -E mode. + DumpTokens, // Dump out preprocessed tokens. + DumpRawTokens, // Dump out raw tokens. + RunAnalysis, // Run one or more source code analyses. + GeneratePTH, // Generate pre-tokenized header. + GeneratePCH, // Generate pre-compiled header. + InheritanceView // View C++ inheritance for a specified class. +}; + +static llvm::cl::opt<ProgActions> +ProgAction(llvm::cl::desc("Choose output type:"), llvm::cl::ZeroOrMore, + llvm::cl::init(ParseSyntaxOnly), + llvm::cl::values( + clEnumValN(RunPreprocessorOnly, "Eonly", + "Just run preprocessor, no output (for timings)"), + clEnumValN(PrintPreprocessedInput, "E", + "Run preprocessor, emit preprocessed file"), + clEnumValN(DumpRawTokens, "dump-raw-tokens", + "Lex file in raw mode and dump raw tokens"), + clEnumValN(RunAnalysis, "analyze", + "Run static analysis engine"), + clEnumValN(DumpTokens, "dump-tokens", + "Run preprocessor, dump internal rep of tokens"), + clEnumValN(ParseNoop, "parse-noop", + "Run parser with noop callbacks (for timings)"), + clEnumValN(ParseSyntaxOnly, "fsyntax-only", + "Run parser and perform semantic analysis"), + clEnumValN(ParsePrintCallbacks, "parse-print-callbacks", + "Run parser and print each callback invoked"), + clEnumValN(EmitHTML, "emit-html", + "Output input source as HTML"), + clEnumValN(ASTPrint, "ast-print", + "Build ASTs and then pretty-print them"), + clEnumValN(ASTPrintXML, "ast-print-xml", + "Build ASTs and then print them in XML format"), + clEnumValN(ASTDump, "ast-dump", + "Build ASTs and then debug dump them"), + clEnumValN(ASTView, "ast-view", + "Build ASTs and view them with GraphViz"), + clEnumValN(PrintDeclContext, "print-decl-contexts", + "Print DeclContexts and their Decls"), + clEnumValN(GeneratePTH, "emit-pth", + "Generate pre-tokenized header file"), + clEnumValN(GeneratePCH, "emit-pch", + "Generate pre-compiled header file"), + clEnumValN(EmitAssembly, "S", + "Emit native assembly code"), + clEnumValN(EmitLLVM, "emit-llvm", + "Build ASTs then convert to LLVM, emit .ll file"), + clEnumValN(EmitBC, "emit-llvm-bc", + "Build ASTs then convert to LLVM, emit .bc file"), + clEnumValN(EmitLLVMOnly, "emit-llvm-only", + "Build ASTs and convert to LLVM, discarding output"), + clEnumValN(RewriteTest, "rewrite-test", + "Rewriter playground"), + clEnumValN(RewriteObjC, "rewrite-objc", + "Rewrite ObjC into C (code rewriter example)"), + clEnumValN(RewriteMacros, "rewrite-macros", + "Expand macros without full preprocessing"), + clEnumValN(RewriteBlocks, "rewrite-blocks", + "Rewrite Blocks to C"), + clEnumValN(FixIt, "fixit", + "Apply fix-it advice to the input source"), + clEnumValEnd)); + + +static llvm::cl::opt<std::string> +OutputFile("o", + llvm::cl::value_desc("path"), + llvm::cl::desc("Specify output file")); + + +//===----------------------------------------------------------------------===// +// PTH. +//===----------------------------------------------------------------------===// + +static llvm::cl::opt<std::string> +TokenCache("token-cache", llvm::cl::value_desc("path"), + llvm::cl::desc("Use specified token cache file")); + +//===----------------------------------------------------------------------===// +// Diagnostic Options +//===----------------------------------------------------------------------===// + +static llvm::cl::opt<bool> +VerifyDiagnostics("verify", + llvm::cl::desc("Verify emitted diagnostics and warnings")); + +static llvm::cl::opt<std::string> +HTMLDiag("html-diags", + llvm::cl::desc("Generate HTML to report diagnostics"), + llvm::cl::value_desc("HTML directory")); + +static llvm::cl::opt<bool> +NoShowColumn("fno-show-column", + llvm::cl::desc("Do not include column number on diagnostics")); + +static llvm::cl::opt<bool> +NoShowLocation("fno-show-source-location", + llvm::cl::desc("Do not include source location information with" + " diagnostics")); + +static llvm::cl::opt<bool> +NoCaretDiagnostics("fno-caret-diagnostics", + llvm::cl::desc("Do not include source line and caret with" + " diagnostics")); + +static llvm::cl::opt<bool> +NoDiagnosticsFixIt("fno-diagnostics-fixit-info", + llvm::cl::desc("Do not include fixit information in" + " diagnostics")); + +static llvm::cl::opt<bool> +PrintSourceRangeInfo("fdiagnostics-print-source-range-info", + llvm::cl::desc("Print source range spans in numeric form")); + +static llvm::cl::opt<bool> +PrintDiagnosticOption("fdiagnostics-show-option", + llvm::cl::desc("Print diagnostic name with mappable diagnostics")); + +static llvm::cl::opt<unsigned> +MessageLength("fmessage-length", + llvm::cl::desc("Format message diagnostics so that they fit " + "within N columns or fewer, when possible."), + llvm::cl::value_desc("N")); + +//===----------------------------------------------------------------------===// +// C++ Visualization. +//===----------------------------------------------------------------------===// + +static llvm::cl::opt<std::string> +InheritanceViewCls("cxx-inheritance-view", + llvm::cl::value_desc("class name"), + llvm::cl::desc("View C++ inheritance for a specified class")); + +//===----------------------------------------------------------------------===// +// Builtin Options +//===----------------------------------------------------------------------===// + +static llvm::cl::opt<bool> +TimeReport("ftime-report", + llvm::cl::desc("Print the amount of time each " + "phase of compilation takes")); + +static llvm::cl::opt<bool> +Freestanding("ffreestanding", + llvm::cl::desc("Assert that the compilation takes place in a " + "freestanding environment")); + +static llvm::cl::opt<bool> +AllowBuiltins("fbuiltin", llvm::cl::init(true), + llvm::cl::desc("Disable implicit builtin knowledge of functions")); + + +static llvm::cl::opt<bool> +MathErrno("fmath-errno", llvm::cl::init(true), + llvm::cl::desc("Require math functions to respect errno")); + +//===----------------------------------------------------------------------===// +// Language Options +//===----------------------------------------------------------------------===// + +enum LangKind { + langkind_unspecified, + langkind_c, + langkind_c_cpp, + langkind_asm_cpp, + langkind_cxx, + langkind_cxx_cpp, + langkind_objc, + langkind_objc_cpp, + langkind_objcxx, + langkind_objcxx_cpp +}; + +static llvm::cl::opt<LangKind> +BaseLang("x", llvm::cl::desc("Base language to compile"), + llvm::cl::init(langkind_unspecified), + llvm::cl::values(clEnumValN(langkind_c, "c", "C"), + clEnumValN(langkind_cxx, "c++", "C++"), + clEnumValN(langkind_objc, "objective-c", "Objective C"), + clEnumValN(langkind_objcxx,"objective-c++","Objective C++"), + clEnumValN(langkind_c_cpp, "cpp-output", + "Preprocessed C"), + clEnumValN(langkind_asm_cpp, "assembler-with-cpp", + "Preprocessed asm"), + clEnumValN(langkind_cxx_cpp, "c++-cpp-output", + "Preprocessed C++"), + clEnumValN(langkind_objc_cpp, "objective-c-cpp-output", + "Preprocessed Objective C"), + clEnumValN(langkind_objcxx_cpp, "objective-c++-cpp-output", + "Preprocessed Objective C++"), + clEnumValN(langkind_c, "c-header", + "C header"), + clEnumValN(langkind_objc, "objective-c-header", + "Objective-C header"), + clEnumValN(langkind_cxx, "c++-header", + "C++ header"), + clEnumValN(langkind_objcxx, "objective-c++-header", + "Objective-C++ header"), + clEnumValEnd)); + +static llvm::cl::opt<bool> +LangObjC("ObjC", llvm::cl::desc("Set base language to Objective-C"), + llvm::cl::Hidden); +static llvm::cl::opt<bool> +LangObjCXX("ObjC++", llvm::cl::desc("Set base language to Objective-C++"), + llvm::cl::Hidden); + +static llvm::cl::opt<bool> +ObjCExclusiveGC("fobjc-gc-only", + llvm::cl::desc("Use GC exclusively for Objective-C related " + "memory management")); + +static llvm::cl::opt<bool> +ObjCEnableGC("fobjc-gc", + llvm::cl::desc("Enable Objective-C garbage collection")); + +static llvm::cl::opt<bool> +ObjCEnableGCBitmapPrint("print-ivar-layout", + llvm::cl::desc("Enable Objective-C Ivar layout bitmap print trace")); + +static llvm::cl::opt<LangOptions::VisibilityMode> +SymbolVisibility("fvisibility", + llvm::cl::desc("Set the default symbol visibility:"), + llvm::cl::init(LangOptions::Default), + llvm::cl::values(clEnumValN(LangOptions::Default, "default", + "Use default symbol visibility"), + clEnumValN(LangOptions::Hidden, "hidden", + "Use hidden symbol visibility"), + clEnumValN(LangOptions::Protected,"protected", + "Use protected symbol visibility"), + clEnumValEnd)); + +static llvm::cl::opt<bool> +OverflowChecking("ftrapv", + llvm::cl::desc("Trap on integer overflow"), + llvm::cl::init(false)); + +static llvm::cl::opt<bool> +ObjCSenderDispatch("fobjc-sender-dependent-dispatch", + llvm::cl::desc("Enable sender-dependent dispatch for" + "Objective-C messages"), llvm::cl::init(false)); + +/// InitializeBaseLanguage - Handle the -x foo options. +static void InitializeBaseLanguage() { + if (LangObjC) + BaseLang = langkind_objc; + else if (LangObjCXX) + BaseLang = langkind_objcxx; +} + +static LangKind GetLanguage(const std::string &Filename) { + if (BaseLang != langkind_unspecified) + return BaseLang; + + std::string::size_type DotPos = Filename.rfind('.'); + + if (DotPos == std::string::npos) { + BaseLang = langkind_c; // Default to C if no extension. + return langkind_c; + } + + std::string Ext = std::string(Filename.begin()+DotPos+1, Filename.end()); + // C header: .h + // C++ header: .hh or .H; + // assembler no preprocessing: .s + // assembler: .S + if (Ext == "c") + return langkind_c; + else if (Ext == "S" || + // If the compiler is run on a .s file, preprocess it as .S + Ext == "s") + return langkind_asm_cpp; + else if (Ext == "i") + return langkind_c_cpp; + else if (Ext == "ii") + return langkind_cxx_cpp; + else if (Ext == "m") + return langkind_objc; + else if (Ext == "mi") + return langkind_objc_cpp; + else if (Ext == "mm" || Ext == "M") + return langkind_objcxx; + else if (Ext == "mii") + return langkind_objcxx_cpp; + else if (Ext == "C" || Ext == "cc" || Ext == "cpp" || Ext == "CPP" || + Ext == "c++" || Ext == "cp" || Ext == "cxx") + return langkind_cxx; + else + return langkind_c; +} + + +static void InitializeCOptions(LangOptions &Options) { + // Do nothing. +} + +static void InitializeObjCOptions(LangOptions &Options) { + Options.ObjC1 = Options.ObjC2 = 1; +} + + +static void InitializeLangOptions(LangOptions &Options, LangKind LK){ + // FIXME: implement -fpreprocessed mode. + bool NoPreprocess = false; + + switch (LK) { + default: assert(0 && "Unknown language kind!"); + case langkind_asm_cpp: + Options.AsmPreprocessor = 1; + // FALLTHROUGH + case langkind_c_cpp: + NoPreprocess = true; + // FALLTHROUGH + case langkind_c: + InitializeCOptions(Options); + break; + case langkind_cxx_cpp: + NoPreprocess = true; + // FALLTHROUGH + case langkind_cxx: + Options.CPlusPlus = 1; + break; + case langkind_objc_cpp: + NoPreprocess = true; + // FALLTHROUGH + case langkind_objc: + InitializeObjCOptions(Options); + break; + case langkind_objcxx_cpp: + NoPreprocess = true; + // FALLTHROUGH + case langkind_objcxx: + Options.ObjC1 = Options.ObjC2 = 1; + Options.CPlusPlus = 1; + break; + } + + if (ObjCExclusiveGC) + Options.setGCMode(LangOptions::GCOnly); + else if (ObjCEnableGC) + Options.setGCMode(LangOptions::HybridGC); + + if (ObjCEnableGCBitmapPrint) + Options.ObjCGCBitmapPrint = 1; + + Options.setVisibilityMode(SymbolVisibility); + Options.OverflowChecking = OverflowChecking; +} + +/// LangStds - Language standards we support. +enum LangStds { + lang_unspecified, + lang_c89, lang_c94, lang_c99, + lang_gnu_START, + lang_gnu89 = lang_gnu_START, lang_gnu99, + lang_cxx98, lang_gnucxx98, + lang_cxx0x, lang_gnucxx0x +}; + +static llvm::cl::opt<LangStds> +LangStd("std", llvm::cl::desc("Language standard to compile for"), + llvm::cl::init(lang_unspecified), + llvm::cl::values(clEnumValN(lang_c89, "c89", "ISO C 1990"), + clEnumValN(lang_c89, "c90", "ISO C 1990"), + clEnumValN(lang_c89, "iso9899:1990", "ISO C 1990"), + clEnumValN(lang_c94, "iso9899:199409", + "ISO C 1990 with amendment 1"), + clEnumValN(lang_c99, "c99", "ISO C 1999"), + clEnumValN(lang_c99, "c9x", "ISO C 1999"), + clEnumValN(lang_c99, "iso9899:1999", "ISO C 1999"), + clEnumValN(lang_c99, "iso9899:199x", "ISO C 1999"), + clEnumValN(lang_gnu89, "gnu89", + "ISO C 1990 with GNU extensions"), + clEnumValN(lang_gnu99, "gnu99", + "ISO C 1999 with GNU extensions (default for C)"), + clEnumValN(lang_gnu99, "gnu9x", + "ISO C 1999 with GNU extensions"), + clEnumValN(lang_cxx98, "c++98", + "ISO C++ 1998 with amendments"), + clEnumValN(lang_gnucxx98, "gnu++98", + "ISO C++ 1998 with amendments and GNU " + "extensions (default for C++)"), + clEnumValN(lang_cxx0x, "c++0x", + "Upcoming ISO C++ 200x with amendments"), + clEnumValN(lang_gnucxx0x, "gnu++0x", + "Upcoming ISO C++ 200x with amendments and GNU " + "extensions"), + clEnumValEnd)); + +static llvm::cl::opt<bool> +NoOperatorNames("fno-operator-names", + llvm::cl::desc("Do not treat C++ operator name keywords as " + "synonyms for operators")); + +static llvm::cl::opt<bool> +PascalStrings("fpascal-strings", + llvm::cl::desc("Recognize and construct Pascal-style " + "string literals")); + +static llvm::cl::opt<bool> +MSExtensions("fms-extensions", + llvm::cl::desc("Accept some non-standard constructs used in " + "Microsoft header files ")); + +static llvm::cl::opt<bool> +WritableStrings("fwritable-strings", + llvm::cl::desc("Store string literals as writable data")); + +static llvm::cl::opt<bool> +NoLaxVectorConversions("fno-lax-vector-conversions", + llvm::cl::desc("Disallow implicit conversions between " + "vectors with a different number of " + "elements or different element types")); + +static llvm::cl::opt<bool> +EnableBlocks("fblocks", llvm::cl::desc("enable the 'blocks' language feature")); + +static llvm::cl::opt<bool> +EnableHeinousExtensions("fheinous-gnu-extensions", + llvm::cl::desc("enable GNU extensions that you really really shouldn't use"), + llvm::cl::ValueDisallowed, llvm::cl::Hidden); + +static llvm::cl::opt<bool> +ObjCNonFragileABI("fobjc-nonfragile-abi", + llvm::cl::desc("enable objective-c's nonfragile abi")); + + +static llvm::cl::opt<bool> +EmitAllDecls("femit-all-decls", + llvm::cl::desc("Emit all declarations, even if unused")); + +static llvm::cl::opt<bool> +Exceptions("fexceptions", + llvm::cl::desc("Enable support for exception handling")); + +static llvm::cl::opt<bool> +GNURuntime("fgnu-runtime", + llvm::cl::desc("Generate output compatible with the standard GNU " + "Objective-C runtime")); + +static llvm::cl::opt<bool> +NeXTRuntime("fnext-runtime", + llvm::cl::desc("Generate output compatible with the NeXT " + "runtime")); + + + +static llvm::cl::opt<bool> +Trigraphs("trigraphs", llvm::cl::desc("Process trigraph sequences")); + +static llvm::cl::opt<unsigned> +TemplateDepth("ftemplate-depth", llvm::cl::init(99), + llvm::cl::desc("Maximum depth of recursive template " + "instantiation")); +static llvm::cl::opt<bool> +DollarsInIdents("fdollars-in-identifiers", + llvm::cl::desc("Allow '$' in identifiers")); + + +static llvm::cl::opt<bool> +OptSize("Os", llvm::cl::desc("Optimize for size")); + +static llvm::cl::opt<bool> +NoCommon("fno-common", + llvm::cl::desc("Compile common globals like normal definitions"), + llvm::cl::ValueDisallowed); + +static llvm::cl::opt<std::string> +MainFileName("main-file-name", + llvm::cl::desc("Main file name to use for debug info")); + +// FIXME: Also add an "-fno-access-control" option. +static llvm::cl::opt<bool> +AccessControl("faccess-control", + llvm::cl::desc("Enable C++ access control")); + +// It might be nice to add bounds to the CommandLine library directly. +struct OptLevelParser : public llvm::cl::parser<unsigned> { + bool parse(llvm::cl::Option &O, const char *ArgName, + const std::string &Arg, unsigned &Val) { + if (llvm::cl::parser<unsigned>::parse(O, ArgName, Arg, Val)) + return true; + if (Val > 3) + return O.error(": '" + Arg + "' invalid optimization level!"); + return false; + } +}; +static llvm::cl::opt<unsigned, false, OptLevelParser> +OptLevel("O", llvm::cl::Prefix, + llvm::cl::desc("Optimization level"), + llvm::cl::init(0)); + +static llvm::cl::opt<unsigned> +PICLevel("pic-level", llvm::cl::desc("Value for __PIC__")); + +static llvm::cl::opt<bool> +StaticDefine("static-define", llvm::cl::desc("Should __STATIC__ be defined")); + +static void InitializeLanguageStandard(LangOptions &Options, LangKind LK, + TargetInfo *Target, + const llvm::StringMap<bool> &Features) { + // Allow the target to set the default the langauge options as it sees fit. + Target->getDefaultLangOptions(Options); + + // Pass the map of target features to the target for validation and + // processing. + Target->HandleTargetFeatures(Features); + + if (LangStd == lang_unspecified) { + // Based on the base language, pick one. + switch (LK) { + case lang_unspecified: assert(0 && "Unknown base language"); + case langkind_c: + case langkind_asm_cpp: + case langkind_c_cpp: + case langkind_objc: + case langkind_objc_cpp: + LangStd = lang_gnu99; + break; + case langkind_cxx: + case langkind_cxx_cpp: + case langkind_objcxx: + case langkind_objcxx_cpp: + LangStd = lang_gnucxx98; + break; + } + } + + switch (LangStd) { + default: assert(0 && "Unknown language standard!"); + + // Fall through from newer standards to older ones. This isn't really right. + // FIXME: Enable specifically the right features based on the language stds. + case lang_gnucxx0x: + case lang_cxx0x: + Options.CPlusPlus0x = 1; + // FALL THROUGH + case lang_gnucxx98: + case lang_cxx98: + Options.CPlusPlus = 1; + Options.CXXOperatorNames = !NoOperatorNames; + // FALL THROUGH. + case lang_gnu99: + case lang_c99: + Options.C99 = 1; + Options.HexFloats = 1; + // FALL THROUGH. + case lang_gnu89: + Options.BCPLComment = 1; // Only for C99/C++. + // FALL THROUGH. + case lang_c94: + Options.Digraphs = 1; // C94, C99, C++. + // FALL THROUGH. + case lang_c89: + break; + } + + // GNUMode - Set if we're in gnu99, gnu89, gnucxx98, etc. + Options.GNUMode = LangStd >= lang_gnu_START; + + if (Options.CPlusPlus) { + Options.C99 = 0; + Options.HexFloats = Options.GNUMode; + } + + if (LangStd == lang_c89 || LangStd == lang_c94 || LangStd == lang_gnu89) + Options.ImplicitInt = 1; + else + Options.ImplicitInt = 0; + + // Mimicing gcc's behavior, trigraphs are only enabled if -trigraphs + // is specified, or -std is set to a conforming mode. + Options.Trigraphs = !Options.GNUMode; + if (Trigraphs.getPosition()) + Options.Trigraphs = Trigraphs; // Command line option wins if specified. + + // If in a conformant language mode (e.g. -std=c99) Blocks defaults to off + // even if they are normally on for the target. In GNU modes (e.g. + // -std=gnu99) the default for blocks depends on the target settings. + // However, blocks are not turned off when compiling Obj-C or Obj-C++ code. + if (!Options.ObjC1 && !Options.GNUMode) + Options.Blocks = 0; + + // Default to not accepting '$' in identifiers when preprocessing assembler, + // but do accept when preprocessing C. FIXME: these defaults are right for + // darwin, are they right everywhere? + Options.DollarIdents = LK != langkind_asm_cpp; + if (DollarsInIdents.getPosition()) // Explicit setting overrides default. + Options.DollarIdents = DollarsInIdents; + + if (PascalStrings.getPosition()) + Options.PascalStrings = PascalStrings; + Options.Microsoft = MSExtensions; + Options.WritableStrings = WritableStrings; + if (NoLaxVectorConversions.getPosition()) + Options.LaxVectorConversions = 0; + Options.Exceptions = Exceptions; + if (EnableBlocks.getPosition()) + Options.Blocks = EnableBlocks; + + if (!AllowBuiltins) + Options.NoBuiltin = 1; + if (Freestanding) + Options.Freestanding = Options.NoBuiltin = 1; + + if (EnableHeinousExtensions) + Options.HeinousExtensions = 1; + + if (AccessControl) + Options.AccessControl = 1; + + Options.MathErrno = MathErrno; + + Options.InstantiationDepth = TemplateDepth; + + // Override the default runtime if the user requested it. + if (NeXTRuntime) + Options.NeXTRuntime = 1; + else if (GNURuntime) + Options.NeXTRuntime = 0; + + if (ObjCNonFragileABI) + Options.ObjCNonFragileABI = 1; + + Options.ObjCSenderDispatch = ObjCSenderDispatch; + + if (EmitAllDecls) + Options.EmitAllDecls = 1; + + // The __OPTIMIZE_SIZE__ define is tied to -Oz, which we don't + // support. + Options.OptimizeSize = 0; + + // -Os implies -O2 + if (OptSize || OptLevel) + Options.Optimize = 1; + + assert(PICLevel <= 2 && "Invalid value for -pic-level"); + Options.PICLevel = PICLevel; + + Options.GNUInline = !Options.C99; + // FIXME: This is affected by other options (-fno-inline). + Options.NoInline = !OptSize && !OptLevel; + + Options.Static = StaticDefine; + + if (MainFileName.getPosition()) + Options.setMainFileName(MainFileName.c_str()); +} + +//===----------------------------------------------------------------------===// +// Target Triple Processing. +//===----------------------------------------------------------------------===// + +static llvm::cl::opt<std::string> +TargetTriple("triple", + llvm::cl::desc("Specify target triple (e.g. i686-apple-darwin9)")); + +static llvm::cl::opt<std::string> +MacOSVersionMin("mmacosx-version-min", + llvm::cl::desc("Specify target Mac OS X version (e.g. 10.5)")); + +// If -mmacosx-version-min=10.3.9 is specified, change the triple from being +// something like powerpc-apple-darwin9 to powerpc-apple-darwin7 + +// FIXME: We should have the driver do this instead. +static void HandleMacOSVersionMin(std::string &Triple) { + std::string::size_type DarwinDashIdx = Triple.find("-darwin"); + if (DarwinDashIdx == std::string::npos) { + fprintf(stderr, + "-mmacosx-version-min only valid for darwin (Mac OS X) targets\n"); + exit(1); + } + unsigned DarwinNumIdx = DarwinDashIdx + strlen("-darwin"); + + // Remove the number. + Triple.resize(DarwinNumIdx); + + // Validate that MacOSVersionMin is a 'version number', starting with 10.[3-9] + bool MacOSVersionMinIsInvalid = false; + int VersionNum = 0; + if (MacOSVersionMin.size() < 4 || + MacOSVersionMin.substr(0, 3) != "10." || + !isdigit(MacOSVersionMin[3])) { + MacOSVersionMinIsInvalid = true; + } else { + const char *Start = MacOSVersionMin.c_str()+3; + char *End = 0; + VersionNum = (int)strtol(Start, &End, 10); + + // The version number must be in the range 0-9. + MacOSVersionMinIsInvalid = (unsigned)VersionNum > 9; + + // Turn MacOSVersionMin into a darwin number: e.g. 10.3.9 is 3 -> 7. + Triple += llvm::itostr(VersionNum+4); + + if (End[0] == '.' && isdigit(End[1]) && End[2] == '\0') { // 10.4.7 is ok. + // Add the period piece (.7) to the end of the triple. This gives us + // something like ...-darwin8.7 + Triple += End; + } else if (End[0] != '\0') { // "10.4" is ok. 10.4x is not. + MacOSVersionMinIsInvalid = true; + } + } + + if (MacOSVersionMinIsInvalid) { + fprintf(stderr, + "-mmacosx-version-min=%s is invalid, expected something like '10.4'.\n", + MacOSVersionMin.c_str()); + exit(1); + } + else if (VersionNum <= 4 && + !strncmp(Triple.c_str(), "x86_64", strlen("x86_64"))) { + fprintf(stderr, + "-mmacosx-version-min=%s is invalid with -arch x86_64.\n", + MacOSVersionMin.c_str()); + exit(1); + } + +} + +static llvm::cl::opt<std::string> +IPhoneOSVersionMin("miphoneos-version-min", + llvm::cl::desc("Specify target iPhone OS version (e.g. 2.0)")); + +// If -miphoneos-version-min=2.2 is specified, change the triple from being +// something like armv6-apple-darwin10 to armv6-apple-darwin9.2.2. We use +// 9 as the default major Darwin number, and encode the iPhone OS version +// number in the minor version and revision. + +// FIXME: We should have the driver do this instead. +static void HandleIPhoneOSVersionMin(std::string &Triple) { + std::string::size_type DarwinDashIdx = Triple.find("-darwin"); + if (DarwinDashIdx == std::string::npos) { + fprintf(stderr, + "-miphoneos-version-min only valid for darwin (Mac OS X) targets\n"); + exit(1); + } + unsigned DarwinNumIdx = DarwinDashIdx + strlen("-darwin"); + + // Remove the number. + Triple.resize(DarwinNumIdx); + + // Validate that IPhoneOSVersionMin is a 'version number', starting with [2-9].[0-9] + bool IPhoneOSVersionMinIsInvalid = false; + int VersionNum = 0; + if (IPhoneOSVersionMin.size() < 3 || + !isdigit(IPhoneOSVersionMin[0])) { + IPhoneOSVersionMinIsInvalid = true; + } else { + const char *Start = IPhoneOSVersionMin.c_str(); + char *End = 0; + VersionNum = (int)strtol(Start, &End, 10); + + // The version number must be in the range 0-9. + IPhoneOSVersionMinIsInvalid = (unsigned)VersionNum > 9; + + // Turn IPhoneOSVersionMin into a darwin number: e.g. 2.0 is 2 -> 9.2. + Triple += "9." + llvm::itostr(VersionNum); + + if (End[0] == '.' && isdigit(End[1]) && End[2] == '\0') { // 2.2 is ok. + // Add the period piece (.2) to the end of the triple. This gives us + // something like ...-darwin9.2.2 + Triple += End; + } else if (End[0] != '\0') { // "2.2" is ok. 2x is not. + IPhoneOSVersionMinIsInvalid = true; + } + } + + if (IPhoneOSVersionMinIsInvalid) { + fprintf(stderr, + "-miphoneos-version-min=%s is invalid, expected something like '2.0'.\n", + IPhoneOSVersionMin.c_str()); + exit(1); + } +} + +/// CreateTargetTriple - Process the various options that affect the target +/// triple and build a final aggregate triple that we are compiling for. +static std::string CreateTargetTriple() { + // Initialize base triple. If a -triple option has been specified, use + // that triple. Otherwise, default to the host triple. + std::string Triple = TargetTriple; + if (Triple.empty()) + Triple = llvm::sys::getHostTriple(); + + // If -mmacosx-version-min=10.3.9 is specified, change the triple from being + // something like powerpc-apple-darwin9 to powerpc-apple-darwin7 + if (!MacOSVersionMin.empty()) + HandleMacOSVersionMin(Triple); + else if (!IPhoneOSVersionMin.empty()) + HandleIPhoneOSVersionMin(Triple);; + + return Triple; +} + +//===----------------------------------------------------------------------===// +// SourceManager initialization. +//===----------------------------------------------------------------------===// + +static bool InitializeSourceManager(Preprocessor &PP, + const std::string &InFile) { + // Figure out where to get and map in the main file. + SourceManager &SourceMgr = PP.getSourceManager(); + FileManager &FileMgr = PP.getFileManager(); + + if (EmptyInputOnly) { + const char *EmptyStr = ""; + llvm::MemoryBuffer *SB = + llvm::MemoryBuffer::getMemBuffer(EmptyStr, EmptyStr, "<empty input>"); + SourceMgr.createMainFileIDForMemBuffer(SB); + } else if (InFile != "-") { + const FileEntry *File = FileMgr.getFile(InFile); + if (File) SourceMgr.createMainFileID(File, SourceLocation()); + if (SourceMgr.getMainFileID().isInvalid()) { + PP.getDiagnostics().Report(FullSourceLoc(), diag::err_fe_error_reading) + << InFile.c_str(); + return true; + } + } else { + llvm::MemoryBuffer *SB = llvm::MemoryBuffer::getSTDIN(); + + // If stdin was empty, SB is null. Cons up an empty memory + // buffer now. + if (!SB) { + const char *EmptyStr = ""; + SB = llvm::MemoryBuffer::getMemBuffer(EmptyStr, EmptyStr, "<stdin>"); + } + + SourceMgr.createMainFileIDForMemBuffer(SB); + if (SourceMgr.getMainFileID().isInvalid()) { + PP.getDiagnostics().Report(FullSourceLoc(), + diag::err_fe_error_reading_stdin); + return true; + } + } + + return false; +} + + +//===----------------------------------------------------------------------===// +// Preprocessor Initialization +//===----------------------------------------------------------------------===// + +// FIXME: Preprocessor builtins to support. +// -A... - Play with #assertions +// -undef - Undefine all predefined macros + +static llvm::cl::list<std::string> +D_macros("D", llvm::cl::value_desc("macro"), llvm::cl::Prefix, + llvm::cl::desc("Predefine the specified macro")); +static llvm::cl::list<std::string> +U_macros("U", llvm::cl::value_desc("macro"), llvm::cl::Prefix, + llvm::cl::desc("Undefine the specified macro")); + +static llvm::cl::list<std::string> +ImplicitIncludes("include", llvm::cl::value_desc("file"), + llvm::cl::desc("Include file before parsing")); +static llvm::cl::list<std::string> +ImplicitMacroIncludes("imacros", llvm::cl::value_desc("file"), + llvm::cl::desc("Include macros from file before parsing")); + +static llvm::cl::opt<std::string> +ImplicitIncludePCH("include-pch", llvm::cl::value_desc("file"), + llvm::cl::desc("Include precompiled header file")); + +static llvm::cl::opt<std::string> +ImplicitIncludePTH("include-pth", llvm::cl::value_desc("file"), + llvm::cl::desc("Include file before parsing")); + + +//===----------------------------------------------------------------------===// +// Preprocessor include path information. +//===----------------------------------------------------------------------===// + +// This tool exports a large number of command line options to control how the +// preprocessor searches for header files. At root, however, the Preprocessor +// object takes a very simple interface: a list of directories to search for +// +// FIXME: -nostdinc++ +// FIXME: -imultilib +// + +static llvm::cl::opt<bool> +nostdinc("nostdinc", llvm::cl::desc("Disable standard #include directories")); + +// Various command line options. These four add directories to each chain. +static llvm::cl::list<std::string> +F_dirs("F", llvm::cl::value_desc("directory"), llvm::cl::Prefix, + llvm::cl::desc("Add directory to framework include search path")); +static llvm::cl::list<std::string> +I_dirs("I", llvm::cl::value_desc("directory"), llvm::cl::Prefix, + llvm::cl::desc("Add directory to include search path")); +static llvm::cl::list<std::string> +idirafter_dirs("idirafter", llvm::cl::value_desc("directory"), llvm::cl::Prefix, + llvm::cl::desc("Add directory to AFTER include search path")); +static llvm::cl::list<std::string> +iquote_dirs("iquote", llvm::cl::value_desc("directory"), llvm::cl::Prefix, + llvm::cl::desc("Add directory to QUOTE include search path")); +static llvm::cl::list<std::string> +isystem_dirs("isystem", llvm::cl::value_desc("directory"), llvm::cl::Prefix, + llvm::cl::desc("Add directory to SYSTEM include search path")); + +// These handle -iprefix/-iwithprefix/-iwithprefixbefore. +static llvm::cl::list<std::string> +iprefix_vals("iprefix", llvm::cl::value_desc("prefix"), llvm::cl::Prefix, + llvm::cl::desc("Set the -iwithprefix/-iwithprefixbefore prefix")); +static llvm::cl::list<std::string> +iwithprefix_vals("iwithprefix", llvm::cl::value_desc("dir"), llvm::cl::Prefix, + llvm::cl::desc("Set directory to SYSTEM include search path with prefix")); +static llvm::cl::list<std::string> +iwithprefixbefore_vals("iwithprefixbefore", llvm::cl::value_desc("dir"), + llvm::cl::Prefix, + llvm::cl::desc("Set directory to include search path with prefix")); + +static llvm::cl::opt<std::string> +isysroot("isysroot", llvm::cl::value_desc("dir"), llvm::cl::init("/"), + llvm::cl::desc("Set the system root directory (usually /)")); + +// Finally, implement the code that groks the options above. + +/// InitializeIncludePaths - Process the -I options and set them in the +/// HeaderSearch object. +void InitializeIncludePaths(const char *Argv0, HeaderSearch &Headers, + FileManager &FM, const LangOptions &Lang) { + InitHeaderSearch Init(Headers, Verbose, isysroot); + + // Handle -I... and -F... options, walking the lists in parallel. + unsigned Iidx = 0, Fidx = 0; + while (Iidx < I_dirs.size() && Fidx < F_dirs.size()) { + if (I_dirs.getPosition(Iidx) < F_dirs.getPosition(Fidx)) { + Init.AddPath(I_dirs[Iidx], InitHeaderSearch::Angled, false, true, false); + ++Iidx; + } else { + Init.AddPath(F_dirs[Fidx], InitHeaderSearch::Angled, false, true, true); + ++Fidx; + } + } + + // Consume what's left from whatever list was longer. + for (; Iidx != I_dirs.size(); ++Iidx) + Init.AddPath(I_dirs[Iidx], InitHeaderSearch::Angled, false, true, false); + for (; Fidx != F_dirs.size(); ++Fidx) + Init.AddPath(F_dirs[Fidx], InitHeaderSearch::Angled, false, true, true); + + // Handle -idirafter... options. + for (unsigned i = 0, e = idirafter_dirs.size(); i != e; ++i) + Init.AddPath(idirafter_dirs[i], InitHeaderSearch::After, + false, true, false); + + // Handle -iquote... options. + for (unsigned i = 0, e = iquote_dirs.size(); i != e; ++i) + Init.AddPath(iquote_dirs[i], InitHeaderSearch::Quoted, false, true, false); + + // Handle -isystem... options. + for (unsigned i = 0, e = isystem_dirs.size(); i != e; ++i) + Init.AddPath(isystem_dirs[i], InitHeaderSearch::System, false, true, false); + + // Walk the -iprefix/-iwithprefix/-iwithprefixbefore argument lists in + // parallel, processing the values in order of occurance to get the right + // prefixes. + { + std::string Prefix = ""; // FIXME: this isn't the correct default prefix. + unsigned iprefix_idx = 0; + unsigned iwithprefix_idx = 0; + unsigned iwithprefixbefore_idx = 0; + bool iprefix_done = iprefix_vals.empty(); + bool iwithprefix_done = iwithprefix_vals.empty(); + bool iwithprefixbefore_done = iwithprefixbefore_vals.empty(); + while (!iprefix_done || !iwithprefix_done || !iwithprefixbefore_done) { + if (!iprefix_done && + (iwithprefix_done || + iprefix_vals.getPosition(iprefix_idx) < + iwithprefix_vals.getPosition(iwithprefix_idx)) && + (iwithprefixbefore_done || + iprefix_vals.getPosition(iprefix_idx) < + iwithprefixbefore_vals.getPosition(iwithprefixbefore_idx))) { + Prefix = iprefix_vals[iprefix_idx]; + ++iprefix_idx; + iprefix_done = iprefix_idx == iprefix_vals.size(); + } else if (!iwithprefix_done && + (iwithprefixbefore_done || + iwithprefix_vals.getPosition(iwithprefix_idx) < + iwithprefixbefore_vals.getPosition(iwithprefixbefore_idx))) { + Init.AddPath(Prefix+iwithprefix_vals[iwithprefix_idx], + InitHeaderSearch::System, false, false, false); + ++iwithprefix_idx; + iwithprefix_done = iwithprefix_idx == iwithprefix_vals.size(); + } else { + Init.AddPath(Prefix+iwithprefixbefore_vals[iwithprefixbefore_idx], + InitHeaderSearch::Angled, false, false, false); + ++iwithprefixbefore_idx; + iwithprefixbefore_done = + iwithprefixbefore_idx == iwithprefixbefore_vals.size(); + } + } + } + + Init.AddDefaultEnvVarPaths(Lang); + + // Add the clang headers, which are relative to the clang binary. + llvm::sys::Path MainExecutablePath = + llvm::sys::Path::GetMainExecutable(Argv0, + (void*)(intptr_t)InitializeIncludePaths); + if (!MainExecutablePath.isEmpty()) { + MainExecutablePath.eraseComponent(); // Remove /clang from foo/bin/clang + MainExecutablePath.eraseComponent(); // Remove /bin from foo/bin + + // Get foo/lib/clang/<version>/include + MainExecutablePath.appendComponent("lib"); + MainExecutablePath.appendComponent("clang"); + MainExecutablePath.appendComponent(CLANG_VERSION_STRING); + MainExecutablePath.appendComponent("include"); + + // We pass true to ignore sysroot so that we *always* look for clang headers + // relative to our executable, never relative to -isysroot. + Init.AddPath(MainExecutablePath.c_str(), InitHeaderSearch::System, + false, false, false, true /*ignore sysroot*/); + } + + if (!nostdinc) + Init.AddDefaultSystemIncludePaths(Lang); + + // Now that we have collected all of the include paths, merge them all + // together and tell the preprocessor about them. + + Init.Realize(); +} + +void InitializePreprocessorInitOptions(PreprocessorInitOptions &InitOpts) +{ + // Add macros from the command line. + unsigned d = 0, D = D_macros.size(); + unsigned u = 0, U = U_macros.size(); + while (d < D || u < U) { + if (u == U || (d < D && D_macros.getPosition(d) < U_macros.getPosition(u))) + InitOpts.addMacroDef(D_macros[d++]); + else + InitOpts.addMacroUndef(U_macros[u++]); + } + + // If -imacros are specified, include them now. These are processed before + // any -include directives. + for (unsigned i = 0, e = ImplicitMacroIncludes.size(); i != e; ++i) + InitOpts.addMacroInclude(ImplicitMacroIncludes[i]); + + if (!ImplicitIncludePTH.empty() || !ImplicitIncludes.empty() || + (!ImplicitIncludePCH.empty() && ProgAction == PrintPreprocessedInput)) { + // We want to add these paths to the predefines buffer in order, make a + // temporary vector to sort by their occurrence. + llvm::SmallVector<std::pair<unsigned, std::string*>, 8> OrderedPaths; + + if (!ImplicitIncludePTH.empty()) + OrderedPaths.push_back(std::make_pair(ImplicitIncludePTH.getPosition(), + &ImplicitIncludePTH)); + if (!ImplicitIncludePCH.empty() && ProgAction == PrintPreprocessedInput) + OrderedPaths.push_back(std::make_pair(ImplicitIncludePCH.getPosition(), + &ImplicitIncludePCH)); + for (unsigned i = 0, e = ImplicitIncludes.size(); i != e; ++i) + OrderedPaths.push_back(std::make_pair(ImplicitIncludes.getPosition(i), + &ImplicitIncludes[i])); + llvm::array_pod_sort(OrderedPaths.begin(), OrderedPaths.end()); + + + // Now that they are ordered by position, add to the predefines buffer. + for (unsigned i = 0, e = OrderedPaths.size(); i != e; ++i) { + std::string *Ptr = OrderedPaths[i].second; + if (!ImplicitIncludes.empty() && + Ptr >= &ImplicitIncludes[0] && + Ptr <= &ImplicitIncludes[ImplicitIncludes.size()-1]) { + InitOpts.addInclude(*Ptr, false); + } else if (Ptr == &ImplicitIncludePTH) { + InitOpts.addInclude(*Ptr, true); + } else { + // We end up here when we're producing preprocessed output and + // we loaded a PCH file. In this case, just include the header + // file that was used to build the precompiled header. + assert(Ptr == &ImplicitIncludePCH); + std::string OriginalFile = PCHReader::getOriginalSourceFile(*Ptr); + if (!OriginalFile.empty()) { + InitOpts.addInclude(OriginalFile, false); + ImplicitIncludePCH.clear(); + } + } + } + } +} + +//===----------------------------------------------------------------------===// +// Driver PreprocessorFactory - For lazily generating preprocessors ... +//===----------------------------------------------------------------------===// + +namespace { +class VISIBILITY_HIDDEN DriverPreprocessorFactory : public PreprocessorFactory { + Diagnostic &Diags; + const LangOptions &LangInfo; + TargetInfo &Target; + SourceManager &SourceMgr; + HeaderSearch &HeaderInfo; + +public: + DriverPreprocessorFactory(Diagnostic &diags, const LangOptions &opts, + TargetInfo &target, SourceManager &SM, + HeaderSearch &Headers) + : Diags(diags), LangInfo(opts), Target(target), + SourceMgr(SM), HeaderInfo(Headers) {} + + + virtual ~DriverPreprocessorFactory() {} + + virtual Preprocessor* CreatePreprocessor() { + llvm::OwningPtr<PTHManager> PTHMgr; + + if (!TokenCache.empty() && !ImplicitIncludePTH.empty()) { + fprintf(stderr, "error: cannot use both -token-cache and -include-pth " + "options\n"); + exit(1); + } + + // Use PTH? + if (!TokenCache.empty() || !ImplicitIncludePTH.empty()) { + const std::string& x = TokenCache.empty() ? ImplicitIncludePTH:TokenCache; + PTHMgr.reset(PTHManager::Create(x, &Diags, + TokenCache.empty() ? Diagnostic::Error + : Diagnostic::Warning)); + } + + if (Diags.hasErrorOccurred()) + exit(1); + + // Create the Preprocessor. + llvm::OwningPtr<Preprocessor> PP(new Preprocessor(Diags, LangInfo, Target, + SourceMgr, HeaderInfo, + PTHMgr.get())); + + // Note that this is different then passing PTHMgr to Preprocessor's ctor. + // That argument is used as the IdentifierInfoLookup argument to + // IdentifierTable's ctor. + if (PTHMgr) { + PTHMgr->setPreprocessor(PP.get()); + PP->setPTHManager(PTHMgr.take()); + } + + PreprocessorInitOptions InitOpts; + InitializePreprocessorInitOptions(InitOpts); + if (InitializePreprocessor(*PP, InitOpts)) + return 0; + + return PP.take(); + } +}; +} + +//===----------------------------------------------------------------------===// +// Basic Parser driver +//===----------------------------------------------------------------------===// + +static void ParseFile(Preprocessor &PP, MinimalAction *PA) { + Parser P(PP, *PA); + PP.EnterMainSourceFile(); + + // Parsing the specified input file. + P.ParseTranslationUnit(); + delete PA; +} + +//===----------------------------------------------------------------------===// +// Code generation options +//===----------------------------------------------------------------------===// + +static llvm::cl::opt<bool> +GenerateDebugInfo("g", + llvm::cl::desc("Generate source level debug information")); + +static llvm::cl::opt<std::string> +TargetCPU("mcpu", + llvm::cl::desc("Target a specific cpu type (-mcpu=help for details)")); + +static llvm::cl::list<std::string> +TargetFeatures("target-feature", llvm::cl::desc("Target specific attributes")); + +/// ComputeTargetFeatures - Recompute the target feature list to only +/// be the list of things that are enabled, based on the target cpu +/// and feature list. +static void ComputeFeatureMap(TargetInfo *Target, + llvm::StringMap<bool> &Features) { + assert(Features.empty() && "invalid map"); + + // Initialize the feature map based on the target. + Target->getDefaultFeatures(TargetCPU, Features); + + // Apply the user specified deltas. + for (llvm::cl::list<std::string>::iterator it = TargetFeatures.begin(), + ie = TargetFeatures.end(); it != ie; ++it) { + const char *Name = it->c_str(); + + // FIXME: Don't handle errors like this. + if (Name[0] != '-' && Name[0] != '+') { + fprintf(stderr, "error: clang-cc: invalid target feature string: %s\n", + Name); + exit(1); + } + if (!Target->setFeatureEnabled(Features, Name + 1, (Name[0] == '+'))) { + fprintf(stderr, "error: clang-cc: invalid target feature name: %s\n", + Name + 1); + exit(1); + } + } +} + +static void InitializeCompileOptions(CompileOptions &Opts, + const LangOptions &LangOpts, + const llvm::StringMap<bool> &Features) { + Opts.OptimizeSize = OptSize; + Opts.DebugInfo = GenerateDebugInfo; + if (OptSize) { + // -Os implies -O2 + // FIXME: Diagnose conflicting options. + Opts.OptimizationLevel = 2; + } else { + Opts.OptimizationLevel = OptLevel; + } + + // FIXME: There are llvm-gcc options to control these selectively. + Opts.InlineFunctions = (Opts.OptimizationLevel > 1); + Opts.UnrollLoops = (Opts.OptimizationLevel > 1 && !OptSize); + Opts.SimplifyLibCalls = !LangOpts.NoBuiltin; + +#ifdef NDEBUG + Opts.VerifyModule = 0; +#endif + + Opts.CPU = TargetCPU; + Opts.Features.clear(); + for (llvm::StringMap<bool>::const_iterator it = Features.begin(), + ie = Features.end(); it != ie; ++it) { + // FIXME: If we are completely confident that we have the right + // set, we only need to pass the minuses. + std::string Name(it->second ? "+" : "-"); + Name += it->first(); + Opts.Features.push_back(Name); + } + + Opts.NoCommon = NoCommon | LangOpts.CPlusPlus; + + // Handle -ftime-report. + Opts.TimePasses = TimeReport; +} + +//===----------------------------------------------------------------------===// +// Fix-It Options +//===----------------------------------------------------------------------===// +static llvm::cl::list<ParsedSourceLocation> +FixItAtLocations("fixit-at", llvm::cl::value_desc("source-location"), + llvm::cl::desc("Perform Fix-It modifications at the given source location")); + +//===----------------------------------------------------------------------===// +// ObjC Rewriter Options +//===----------------------------------------------------------------------===// +static llvm::cl::opt<bool> +SilenceRewriteMacroWarning("Wno-rewrite-macros", llvm::cl::init(false), + llvm::cl::desc("Silence ObjC rewriting warnings")); + +//===----------------------------------------------------------------------===// +// Warning Options +//===----------------------------------------------------------------------===// + +// This gets all -W options, including -Werror, -W[no-]system-headers, etc. The +// driver has stripped off -Wa,foo etc. The driver has also translated -W to +// -Wextra, so we don't need to worry about it. +static llvm::cl::list<std::string> +OptWarnings("W", llvm::cl::Prefix, llvm::cl::ValueOptional); + +static llvm::cl::opt<bool> OptPedantic("pedantic"); +static llvm::cl::opt<bool> OptPedanticErrors("pedantic-errors"); +static llvm::cl::opt<bool> OptNoWarnings("w"); + +//===----------------------------------------------------------------------===// +// Preprocessing (-E mode) Options +//===----------------------------------------------------------------------===// +static llvm::cl::opt<bool> +DisableLineMarkers("P", llvm::cl::desc("Disable linemarker output in -E mode")); +static llvm::cl::opt<bool> +EnableCommentOutput("C", llvm::cl::desc("Enable comment output in -E mode")); +static llvm::cl::opt<bool> +EnableMacroCommentOutput("CC", + llvm::cl::desc("Enable comment output in -E mode, " + "even from macro expansions")); +static llvm::cl::opt<bool> +DumpMacros("dM", llvm::cl::desc("Print macro definitions in -E mode instead of" + " normal output")); +static llvm::cl::opt<bool> +DumpDefines("dD", llvm::cl::desc("Print macro definitions in -E mode in " + "addition to normal output")); + +//===----------------------------------------------------------------------===// +// Dependency file options +//===----------------------------------------------------------------------===// +static llvm::cl::opt<std::string> +DependencyFile("dependency-file", + llvm::cl::desc("Filename (or -) to write dependency output to")); + +static llvm::cl::opt<bool> +DependenciesIncludeSystemHeaders("sys-header-deps", + llvm::cl::desc("Include system headers in dependency output")); + +static llvm::cl::list<std::string> +DependencyTargets("MT", + llvm::cl::desc("Specify target for dependency")); + +// FIXME: Implement feature +static llvm::cl::opt<bool> +PhonyDependencyTarget("MP", + llvm::cl::desc("Create phony target for each dependency " + "(other than main file)")); + +//===----------------------------------------------------------------------===// +// Analysis options +//===----------------------------------------------------------------------===// + +static llvm::cl::list<Analyses> +AnalysisList(llvm::cl::desc("Source Code Analysis - Checks and Analyses"), +llvm::cl::values( +#define ANALYSIS(NAME, CMDFLAG, DESC, SCOPE)\ +clEnumValN(NAME, CMDFLAG, DESC), +#include "clang/Frontend/Analyses.def" +clEnumValEnd)); + +static llvm::cl::opt<AnalysisStores> +AnalysisStoreOpt("analyzer-store", + llvm::cl::desc("Source Code Analysis - Abstract Memory Store Models"), + llvm::cl::init(BasicStoreModel), + llvm::cl::values( +#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATFN)\ +clEnumValN(NAME##Model, CMDFLAG, DESC), +#include "clang/Frontend/Analyses.def" +clEnumValEnd)); + +static llvm::cl::opt<AnalysisConstraints> +AnalysisConstraintsOpt("analyzer-constraints", + llvm::cl::desc("Source Code Analysis - Symbolic Constraint Engines"), + llvm::cl::init(RangeConstraintsModel), + llvm::cl::values( +#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATFN)\ +clEnumValN(NAME##Model, CMDFLAG, DESC), +#include "clang/Frontend/Analyses.def" +clEnumValEnd)); + +static llvm::cl::opt<AnalysisDiagClients> +AnalysisDiagOpt("analyzer-output", + llvm::cl::desc("Source Code Analysis - Output Options"), + llvm::cl::init(PD_HTML), + llvm::cl::values( +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATFN, AUTOCREATE)\ +clEnumValN(PD_##NAME, CMDFLAG, DESC), +#include "clang/Frontend/Analyses.def" +clEnumValEnd)); + +static llvm::cl::opt<bool> +VisualizeEGDot("analyzer-viz-egraph-graphviz", + llvm::cl::desc("Display exploded graph using GraphViz")); + +static llvm::cl::opt<bool> +VisualizeEGUbi("analyzer-viz-egraph-ubigraph", + llvm::cl::desc("Display exploded graph using Ubigraph")); + +static llvm::cl::opt<bool> +AnalyzeAll("analyzer-opt-analyze-headers", + llvm::cl::desc("Force the static analyzer to analyze " + "functions defined in header files")); + +static llvm::cl::opt<bool> +AnalyzerDisplayProgress("analyzer-display-progress", + llvm::cl::desc("Emit verbose output about the analyzer's progress.")); + +static llvm::cl::opt<bool> +PurgeDead("analyzer-purge-dead", + llvm::cl::init(true), + llvm::cl::desc("Remove dead symbols, bindings, and constraints before" + " processing a statement.")); + +static llvm::cl::opt<bool> +EagerlyAssume("analyzer-eagerly-assume", + llvm::cl::init(false), + llvm::cl::desc("Eagerly assume the truth/falseness of some " + "symbolic constraints.")); + +static llvm::cl::opt<std::string> +AnalyzeSpecificFunction("analyze-function", + llvm::cl::desc("Run analysis on specific function")); + +static llvm::cl::opt<bool> +TrimGraph("trim-egraph", + llvm::cl::desc("Only show error-related paths in the analysis graph")); + +static AnalyzerOptions ReadAnalyzerOptions() { + AnalyzerOptions Opts; + Opts.AnalysisList = AnalysisList; + Opts.AnalysisStoreOpt = AnalysisStoreOpt; + Opts.AnalysisConstraintsOpt = AnalysisConstraintsOpt; + Opts.AnalysisDiagOpt = AnalysisDiagOpt; + Opts.VisualizeEGDot = VisualizeEGDot; + Opts.VisualizeEGUbi = VisualizeEGUbi; + Opts.AnalyzeAll = AnalyzeAll; + Opts.AnalyzerDisplayProgress = AnalyzerDisplayProgress; + Opts.PurgeDead = PurgeDead; + Opts.EagerlyAssume = EagerlyAssume; + Opts.AnalyzeSpecificFunction = AnalyzeSpecificFunction; + Opts.TrimGraph = TrimGraph; + return Opts; +} + +//===----------------------------------------------------------------------===// +// -dump-build-information Stuff +//===----------------------------------------------------------------------===// + +static llvm::cl::opt<std::string> +DumpBuildInformation("dump-build-information", + llvm::cl::value_desc("filename"), + llvm::cl::desc("output a dump of some build information to a file")); + +static llvm::raw_ostream *BuildLogFile = 0; + +/// LoggingDiagnosticClient - This is a simple diagnostic client that forwards +/// all diagnostics to both BuildLogFile and a chained DiagnosticClient. +namespace { +class LoggingDiagnosticClient : public DiagnosticClient { + llvm::OwningPtr<DiagnosticClient> Chain1; + llvm::OwningPtr<DiagnosticClient> Chain2; +public: + + LoggingDiagnosticClient(DiagnosticClient *Normal) { + // Output diags both where requested... + Chain1.reset(Normal); + // .. and to our log file. + Chain2.reset(new TextDiagnosticPrinter(*BuildLogFile, + !NoShowColumn, + !NoCaretDiagnostics, + !NoShowLocation, + PrintSourceRangeInfo, + PrintDiagnosticOption, + !NoDiagnosticsFixIt, + MessageLength)); + } + + virtual void setLangOptions(const LangOptions *LO) { + Chain1->setLangOptions(LO); + Chain2->setLangOptions(LO); + } + + virtual bool IncludeInDiagnosticCounts() const { + return Chain1->IncludeInDiagnosticCounts(); + } + + virtual void HandleDiagnostic(Diagnostic::Level DiagLevel, + const DiagnosticInfo &Info) { + Chain1->HandleDiagnostic(DiagLevel, Info); + Chain2->HandleDiagnostic(DiagLevel, Info); + } +}; +} // end anonymous namespace. + +static void SetUpBuildDumpLog(unsigned argc, char **argv, + llvm::OwningPtr<DiagnosticClient> &DiagClient) { + + std::string ErrorInfo; + BuildLogFile = new llvm::raw_fd_ostream(DumpBuildInformation.c_str(), false, + ErrorInfo); + + if (!ErrorInfo.empty()) { + llvm::errs() << "error opening -dump-build-information file '" + << DumpBuildInformation << "', option ignored!\n"; + delete BuildLogFile; + BuildLogFile = 0; + DumpBuildInformation = ""; + return; + } + + (*BuildLogFile) << "clang-cc command line arguments: "; + for (unsigned i = 0; i != argc; ++i) + (*BuildLogFile) << argv[i] << ' '; + (*BuildLogFile) << '\n'; + + // LoggingDiagnosticClient - Insert a new logging diagnostic client in between + // the diagnostic producers and the normal receiver. + DiagClient.reset(new LoggingDiagnosticClient(DiagClient.take())); +} + + + +//===----------------------------------------------------------------------===// +// Main driver +//===----------------------------------------------------------------------===// + +static llvm::raw_ostream* ComputeOutFile(const std::string& InFile, + const char* Extension, + bool Binary, + llvm::sys::Path& OutPath) { + 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); + } + OutPath = OutFile; + } + + return Ret; +} + +/// ProcessInputFile - Process a single input file with the specified state. +/// +static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF, + const std::string &InFile, ProgActions PA, + const llvm::StringMap<bool> &Features) { + llvm::OwningPtr<llvm::raw_ostream> OS; + llvm::OwningPtr<ASTConsumer> Consumer; + bool ClearSourceMgr = false; + FixItRewriter *FixItRewrite = 0; + bool CompleteTranslationUnit = true; + llvm::sys::Path OutPath; + + switch (PA) { + default: + fprintf(stderr, "Unexpected program action!\n"); + HadErrors = true; + return; + + case ASTPrint: + OS.reset(ComputeOutFile(InFile, 0, false, OutPath)); + Consumer.reset(CreateASTPrinter(OS.get())); + break; + + case ASTPrintXML: + OS.reset(ComputeOutFile(InFile, "xml", false, OutPath)); + Consumer.reset(CreateASTPrinterXML(OS.get())); + break; + + case ASTDump: + Consumer.reset(CreateASTDumper()); + break; + + case ASTView: + Consumer.reset(CreateASTViewer()); + break; + + case PrintDeclContext: + Consumer.reset(CreateDeclContextPrinter()); + break; + + case EmitHTML: + OS.reset(ComputeOutFile(InFile, 0, true, OutPath)); + Consumer.reset(CreateHTMLPrinter(OS.get(), PP.getDiagnostics(), &PP, &PPF)); + break; + + case InheritanceView: + Consumer.reset(CreateInheritanceViewer(InheritanceViewCls)); + break; + + case EmitAssembly: + case EmitLLVM: + case EmitBC: + case EmitLLVMOnly: { + BackendAction Act; + if (ProgAction == EmitAssembly) { + Act = Backend_EmitAssembly; + OS.reset(ComputeOutFile(InFile, "s", true, OutPath)); + } else if (ProgAction == EmitLLVM) { + Act = Backend_EmitLL; + OS.reset(ComputeOutFile(InFile, "ll", true, OutPath)); + } else if (ProgAction == EmitLLVMOnly) { + Act = Backend_EmitNothing; + } else { + Act = Backend_EmitBC; + OS.reset(ComputeOutFile(InFile, "bc", true, OutPath)); + } + + CompileOptions Opts; + InitializeCompileOptions(Opts, PP.getLangOptions(), Features); + Consumer.reset(CreateBackendConsumer(Act, PP.getDiagnostics(), + PP.getLangOptions(), Opts, InFile, + OS.get())); + break; + } + + case GeneratePCH: + OS.reset(ComputeOutFile(InFile, 0, true, OutPath)); + Consumer.reset(CreatePCHGenerator(PP, OS.get())); + CompleteTranslationUnit = false; + break; + + case RewriteObjC: + OS.reset(ComputeOutFile(InFile, "cpp", true, OutPath)); + Consumer.reset(CreateObjCRewriter(InFile, OS.get(), PP.getDiagnostics(), + PP.getLangOptions(), + SilenceRewriteMacroWarning)); + break; + + case RewriteBlocks: + Consumer.reset(CreateBlockRewriter(InFile, PP.getDiagnostics(), + PP.getLangOptions())); + break; + + case RunAnalysis: { + Consumer.reset(CreateAnalysisConsumer(PP.getDiagnostics(), &PP, &PPF, + PP.getLangOptions(), OutputFile, + ReadAnalyzerOptions())); + break; + } + + case DumpRawTokens: { + llvm::TimeRegion Timer(ClangFrontendTimer); + SourceManager &SM = PP.getSourceManager(); + // Start lexing the specified input file. + Lexer RawLex(SM.getMainFileID(), SM, PP.getLangOptions()); + RawLex.SetKeepWhitespaceMode(true); + + Token RawTok; + RawLex.LexFromRawLexer(RawTok); + while (RawTok.isNot(tok::eof)) { + PP.DumpToken(RawTok, true); + fprintf(stderr, "\n"); + RawLex.LexFromRawLexer(RawTok); + } + ClearSourceMgr = true; + break; + } + case DumpTokens: { // Token dump mode. + llvm::TimeRegion Timer(ClangFrontendTimer); + Token Tok; + // Start preprocessing the specified input file. + PP.EnterMainSourceFile(); + do { + PP.Lex(Tok); + PP.DumpToken(Tok, true); + fprintf(stderr, "\n"); + } while (Tok.isNot(tok::eof)); + ClearSourceMgr = true; + break; + } + case RunPreprocessorOnly: + break; + + case GeneratePTH: { + llvm::TimeRegion Timer(ClangFrontendTimer); + if (OutputFile.empty() || OutputFile == "-") { + // FIXME: Don't fail this way. + // FIXME: Verify that we can actually seek in the given file. + llvm::cerr << "ERROR: PTH requires an seekable file for output!\n"; + ::exit(1); + } + OS.reset(ComputeOutFile(InFile, 0, true, OutPath)); + CacheTokens(PP, static_cast<llvm::raw_fd_ostream*>(OS.get())); + ClearSourceMgr = true; + break; + } + + case PrintPreprocessedInput: + OS.reset(ComputeOutFile(InFile, 0, true, OutPath)); + break; + + case ParseNoop: + break; + + case ParsePrintCallbacks: { + llvm::TimeRegion Timer(ClangFrontendTimer); + OS.reset(ComputeOutFile(InFile, 0, true, OutPath)); + ParseFile(PP, CreatePrintParserActionsAction(PP, OS.get())); + ClearSourceMgr = true; + break; + } + + case ParseSyntaxOnly: { // -fsyntax-only + llvm::TimeRegion Timer(ClangFrontendTimer); + Consumer.reset(new ASTConsumer()); + break; + } + + case RewriteMacros: + OS.reset(ComputeOutFile(InFile, 0, true, OutPath)); + RewriteMacrosInInput(PP, OS.get()); + ClearSourceMgr = true; + break; + + case RewriteTest: + OS.reset(ComputeOutFile(InFile, 0, true, OutPath)); + DoRewriteTest(PP, OS.get()); + ClearSourceMgr = true; + break; + + case FixIt: + llvm::TimeRegion Timer(ClangFrontendTimer); + Consumer.reset(new ASTConsumer()); + FixItRewrite = new FixItRewriter(PP.getDiagnostics(), + PP.getSourceManager(), + PP.getLangOptions()); + break; + } + + if (FixItAtLocations.size() > 0) { + // Even without the "-fixit" flag, with may have some specific + // locations where the user has requested fixes. Process those + // locations now. + if (!FixItRewrite) + FixItRewrite = new FixItRewriter(PP.getDiagnostics(), + PP.getSourceManager(), + PP.getLangOptions()); + + bool AddedFixitLocation = false; + for (unsigned Idx = 0, Last = FixItAtLocations.size(); + Idx != Last; ++Idx) { + RequestedSourceLocation Requested; + if (FixItAtLocations[Idx].ResolveLocation(PP.getFileManager(), + Requested)) { + fprintf(stderr, "FIX-IT could not find file \"%s\"\n", + FixItAtLocations[Idx].FileName.c_str()); + } else { + FixItRewrite->addFixItLocation(Requested); + AddedFixitLocation = true; + } + } + + if (!AddedFixitLocation) { + // All of the fix-it locations were bad. Don't fix anything. + delete FixItRewrite; + FixItRewrite = 0; + } + } + + llvm::OwningPtr<ASTContext> ContextOwner; + if (Consumer) + ContextOwner.reset(new ASTContext(PP.getLangOptions(), + PP.getSourceManager(), + PP.getTargetInfo(), + PP.getIdentifierTable(), + PP.getSelectorTable(), + /* FreeMemory = */ !DisableFree, + /* size_reserve = */0, + /* InitializeBuiltins = */ImplicitIncludePCH.empty())); + llvm::OwningPtr<PCHReader> Reader; + llvm::OwningPtr<ExternalASTSource> Source; + + if (!ImplicitIncludePCH.empty()) { + Reader.reset(new PCHReader(PP, ContextOwner.get())); + + // The user has asked us to include a precompiled header. Load + // the precompiled header into the AST context. + switch (Reader->ReadPCH(ImplicitIncludePCH)) { + case PCHReader::Success: { + // Set the predefines buffer as suggested by the PCH + // reader. Typically, the predefines buffer will be empty. + PP.setPredefines(Reader->getSuggestedPredefines()); + + // Attach the PCH reader to the AST context as an external AST + // source, so that declarations will be deserialized from the + // PCH file as needed. + if (ContextOwner) { + Source.reset(Reader.take()); + ContextOwner->setExternalSource(Source); + } + break; + } + + case PCHReader::Failure: + // Unrecoverable failure: don't even try to process the input + // file. + return; + + case PCHReader::IgnorePCH: + // No suitable PCH file could be found. Return an error. + return; + +#if 0 + // FIXME: We can recover from failed attempts to load PCH + // files. This code will do so, if we ever want to enable it. + + // We delayed the initialization of builtins in the hope of + // loading the PCH file. Since the PCH file could not be + // loaded, initialize builtins now. + if (ContextOwner) + ContextOwner->InitializeBuiltins(PP.getIdentifierTable()); +#endif + } + + // Finish preprocessor initialization. We do this now (rather + // than earlier) because this initialization creates new source + // location entries in the source manager, which must come after + // the source location entries for the PCH file. + if (InitializeSourceManager(PP, InFile)) + return; + } + + + // If we have an ASTConsumer, run the parser with it. + if (Consumer) + ParseAST(PP, Consumer.get(), *ContextOwner.get(), Stats, + CompleteTranslationUnit); + + if (PA == RunPreprocessorOnly) { // Just lex as fast as we can, no output. + llvm::TimeRegion Timer(ClangFrontendTimer); + Token Tok; + // Start parsing the specified input file. + PP.EnterMainSourceFile(); + do { + PP.Lex(Tok); + } while (Tok.isNot(tok::eof)); + ClearSourceMgr = true; + } else if (PA == ParseNoop) { // -parse-noop + llvm::TimeRegion Timer(ClangFrontendTimer); + ParseFile(PP, new MinimalAction(PP)); + ClearSourceMgr = true; + } else if (PA == PrintPreprocessedInput){ // -E mode. + llvm::TimeRegion Timer(ClangFrontendTimer); + if (DumpMacros) + DoPrintMacros(PP, OS.get()); + else + DoPrintPreprocessedInput(PP, OS.get(), EnableCommentOutput, + EnableMacroCommentOutput, + DisableLineMarkers, DumpDefines); + ClearSourceMgr = true; + } + + if (FixItRewrite) + FixItRewrite->WriteFixedFile(InFile, OutputFile); + + // If in -disable-free mode, don't deallocate ASTContext. + if (DisableFree) + ContextOwner.take(); + else + ContextOwner.reset(); // Delete ASTContext + + if (VerifyDiagnostics) + if (CheckDiagnostics(PP)) + exit(1); + + if (Stats) { + fprintf(stderr, "\nSTATISTICS FOR '%s':\n", InFile.c_str()); + PP.PrintStats(); + PP.getIdentifierTable().PrintStats(); + PP.getHeaderSearchInfo().PrintStats(); + PP.getSourceManager().PrintStats(); + fprintf(stderr, "\n"); + } + + // For a multi-file compilation, some things are ok with nuking the source + // manager tables, other require stable fileid/macroid's across multiple + // files. + if (ClearSourceMgr) + PP.getSourceManager().clearIDTables(); + + if (DisableFree) + Consumer.take(); + else + Consumer.reset(); + + // Always delete the output stream because we don't want to leak file + // handles. Also, we don't want to try to erase an open file. + OS.reset(); + + if ((HadErrors || (PP.getDiagnostics().getNumErrors() != 0)) && + !OutPath.isEmpty()) { + // If we had errors, try to erase the output file. + OutPath.eraseFromDisk(); + } +} + +static llvm::cl::list<std::string> +InputFilenames(llvm::cl::Positional, llvm::cl::desc("<input files>")); + +int main(int argc, char **argv) { + llvm::sys::PrintStackTraceOnErrorSignal(); + llvm::PrettyStackTraceProgram X(argc, argv); + llvm::cl::ParseCommandLineOptions(argc, argv, + "LLVM 'Clang' Compiler: http://clang.llvm.org\n"); + + if (TimeReport) + ClangFrontendTimer = new llvm::Timer("Clang front-end time"); + + if (Verbose) + fprintf(stderr, "clang-cc version 1.0 based upon " PACKAGE_STRING + " hosted on " LLVM_HOSTTRIPLE "\n"); + + // If no input was specified, read from stdin. + if (InputFilenames.empty()) + InputFilenames.push_back("-"); + + // Create the diagnostic client for reporting errors or for + // implementing -verify. + llvm::OwningPtr<DiagnosticClient> DiagClient; + if (VerifyDiagnostics) { + // When checking diagnostics, just buffer them up. + DiagClient.reset(new TextDiagnosticBuffer()); + if (InputFilenames.size() != 1) { + fprintf(stderr, "-verify only works on single input files for now.\n"); + return 1; + } + if (!HTMLDiag.empty()) { + fprintf(stderr, "-verify and -html-diags don't work together\n"); + return 1; + } + } else if (HTMLDiag.empty()) { + // Print diagnostics to stderr by default. + + // If -fmessage-length=N was not specified, determine whether this + // is a terminal and, if so, implicitly define -fmessage-length + // appropriately. + if (MessageLength.getNumOccurrences() == 0) + MessageLength.setValue(llvm::sys::Process::StandardErrColumns()); + + DiagClient.reset(new TextDiagnosticPrinter(llvm::errs(), + !NoShowColumn, + !NoCaretDiagnostics, + !NoShowLocation, + PrintSourceRangeInfo, + PrintDiagnosticOption, + !NoDiagnosticsFixIt, + MessageLength)); + } else { + DiagClient.reset(CreateHTMLDiagnosticClient(HTMLDiag)); + } + + if (!DumpBuildInformation.empty()) { + if (!HTMLDiag.empty()) { + fprintf(stderr, + "-dump-build-information and -html-diags don't work together\n"); + return 1; + } + + SetUpBuildDumpLog(argc, argv, DiagClient); + } + + + // Configure our handling of diagnostics. + Diagnostic Diags(DiagClient.get()); + if (ProcessWarningOptions(Diags, OptWarnings, OptPedantic, OptPedanticErrors, + OptNoWarnings)) + return 1; + + // -I- is a deprecated GCC feature, scan for it and reject it. + for (unsigned i = 0, e = I_dirs.size(); i != e; ++i) { + if (I_dirs[i] == "-") { + Diags.Report(FullSourceLoc(), diag::err_pp_I_dash_not_supported); + I_dirs.erase(I_dirs.begin()+i); + --i; + } + } + + // Get information about the target being compiled for. + std::string Triple = CreateTargetTriple(); + llvm::OwningPtr<TargetInfo> Target(TargetInfo::CreateTargetInfo(Triple)); + + if (Target == 0) { + Diags.Report(FullSourceLoc(), diag::err_fe_unknown_triple) + << Triple.c_str(); + return 1; + } + + if (!InheritanceViewCls.empty()) // C++ visualization? + ProgAction = InheritanceView; + + llvm::OwningPtr<SourceManager> SourceMgr; + + // Create a file manager object to provide access to and cache the filesystem. + FileManager FileMgr; + + // Compute the feature set, unfortunately this effects the language! + llvm::StringMap<bool> Features; + ComputeFeatureMap(Target.get(), Features); + + for (unsigned i = 0, e = InputFilenames.size(); i != e; ++i) { + const std::string &InFile = InputFilenames[i]; + + /// Create a SourceManager object. This tracks and owns all the file + /// buffers allocated to a translation unit. + if (!SourceMgr) + SourceMgr.reset(new SourceManager()); + else + SourceMgr->clearIDTables(); + + // Initialize language options, inferring file types from input filenames. + LangOptions LangInfo; + DiagClient->setLangOptions(&LangInfo); + + InitializeBaseLanguage(); + LangKind LK = GetLanguage(InFile); + InitializeLangOptions(LangInfo, LK); + InitializeLanguageStandard(LangInfo, LK, Target.get(), Features); + + // Process the -I options and set them in the HeaderInfo. + HeaderSearch HeaderInfo(FileMgr); + + + InitializeIncludePaths(argv[0], HeaderInfo, FileMgr, LangInfo); + + // Set up the preprocessor with these options. + DriverPreprocessorFactory PPFactory(Diags, LangInfo, *Target, + *SourceMgr.get(), HeaderInfo); + + llvm::OwningPtr<Preprocessor> PP(PPFactory.CreatePreprocessor()); + + if (!PP) + continue; + + // Handle generating dependencies, if requested + if (!DependencyFile.empty()) { + llvm::raw_ostream *DependencyOS; + if (DependencyTargets.empty()) { + // FIXME: Use a proper diagnostic + llvm::cerr << "-dependency-file requires at least one -MT option\n"; + HadErrors = true; + continue; + } + std::string ErrStr; + DependencyOS = + new llvm::raw_fd_ostream(DependencyFile.c_str(), false, ErrStr); + if (!ErrStr.empty()) { + // FIXME: Use a proper diagnostic + llvm::cerr << "unable to open dependency file: " + ErrStr; + HadErrors = true; + continue; + } + + AttachDependencyFileGen(PP.get(), DependencyOS, DependencyTargets, + DependenciesIncludeSystemHeaders, + PhonyDependencyTarget); + } + + if (ImplicitIncludePCH.empty() && + InitializeSourceManager(*PP.get(), InFile)) + continue; + + if (!HTMLDiag.empty()) + ((PathDiagnosticClient*)DiagClient.get())->SetPreprocessor(PP.get()); + + // Process the source file. + ProcessInputFile(*PP, PPFactory, InFile, ProgAction, Features); + + HeaderInfo.ClearFileInfo(); + DiagClient->setLangOptions(0); + } + + if (!NoCaretDiagnostics) + if (unsigned NumDiagnostics = Diags.getNumDiagnostics()) + fprintf(stderr, "%d diagnostic%s generated.\n", NumDiagnostics, + (NumDiagnostics == 1 ? "" : "s")); + + if (Stats) { + FileMgr.PrintStats(); + fprintf(stderr, "\n"); + } + + delete ClangFrontendTimer; + delete BuildLogFile; + + // If verifying diagnostics and we reached here, all is well. + if (VerifyDiagnostics) + return 0; + + // Managed static deconstruction. Useful for making things like + // -time-passes usable. + llvm::llvm_shutdown(); + + return HadErrors || (Diags.getNumErrors() != 0); +} diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt new file mode 100644 index 0000000..fc7464a --- /dev/null +++ b/tools/driver/CMakeLists.txt @@ -0,0 +1,12 @@ +set(LLVM_NO_RTTI 1) + +set( LLVM_USED_LIBS + clangDriver + clangBasic + ) + +set(LLVM_LINK_COMPONENTS system support bitreader bitwriter) + +add_clang_executable(clang + driver.cpp + ) diff --git a/tools/driver/Makefile b/tools/driver/Makefile new file mode 100644 index 0000000..8e9c291 --- /dev/null +++ b/tools/driver/Makefile @@ -0,0 +1,23 @@ +##===- tools/driver/Makefile -------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +LEVEL = ../../../.. + +TOOLNAME = clang +CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../include +CXXFLAGS = -fno-rtti + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +# FIXME: It is unfortunate we need to pull in the bitcode reader and +# writer just to get the serializer stuff used by clangBasic. +LINK_COMPONENTS := system support bitreader bitwriter +USEDLIBS = clangDriver.a clangBasic.a + +include $(LEVEL)/Makefile.common diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp new file mode 100644 index 0000000..804bef4 --- /dev/null +++ b/tools/driver/driver.cpp @@ -0,0 +1,228 @@ +//===-- driver.cpp - Clang GCC-Compatible Driver --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is the entry point to the clang driver; it is a thin wrapper +// for functionality in the Driver clang library. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/Option.h" +#include "clang/Driver/Options.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Config/config.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Host.h" +#include "llvm/System/Path.h" +#include "llvm/System/Signals.h" +using namespace clang; +using namespace clang::driver; + +class DriverDiagnosticPrinter : public DiagnosticClient { + std::string ProgName; + llvm::raw_ostream &OS; + +public: + DriverDiagnosticPrinter(const std::string _ProgName, + llvm::raw_ostream &_OS) + : ProgName(_ProgName), + OS(_OS) {} + + virtual void HandleDiagnostic(Diagnostic::Level DiagLevel, + const DiagnosticInfo &Info); +}; + +void DriverDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, + const DiagnosticInfo &Info) { + OS << ProgName << ": "; + + switch (Level) { + case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type"); + case Diagnostic::Note: OS << "note: "; break; + case Diagnostic::Warning: OS << "warning: "; break; + case Diagnostic::Error: OS << "error: "; break; + case Diagnostic::Fatal: OS << "fatal error: "; break; + } + + llvm::SmallString<100> OutStr; + Info.FormatDiagnostic(OutStr); + OS.write(OutStr.begin(), OutStr.size()); + OS << '\n'; +} + +llvm::sys::Path GetExecutablePath(const char *Argv0) { + // This just needs to be some symbol in the binary; C++ doesn't + // allow taking the address of ::main however. + void *P = (void*) (intptr_t) GetExecutablePath; + return llvm::sys::Path::GetMainExecutable(Argv0, P); +} + +static const char *SaveStringInSet(std::set<std::string> &SavedStrings, + const std::string &S) { + return SavedStrings.insert(S).first->c_str(); +} + +/// ApplyQAOverride - Apply a list of edits to the input argument lists. +/// +/// The input string is a space separate list of edits to perform, +/// they are applied in order to the input argument lists. Edits +/// should be one of the following forms: +/// +/// '^': Add FOO as a new argument at the beginning of the command line. +/// +/// '+': Add FOO as a new argument at the end of the command line. +/// +/// 's/XXX/YYY/': Replace the literal argument XXX by YYY in the +/// command line. +/// +/// 'xOPTION': Removes all instances of the literal argument OPTION. +/// +/// 'XOPTION': Removes all instances of the literal argument OPTION, +/// and the following argument. +/// +/// 'Ox': Removes all flags matching 'O' or 'O[sz0-9]' and adds 'Ox' +/// at the end of the command line. +void ApplyOneQAOverride(std::vector<const char*> &Args, + const std::string &Edit, + std::set<std::string> &SavedStrings) { + // This does not need to be efficient. + + if (Edit[0] == '^') { + const char *Str = + SaveStringInSet(SavedStrings, Edit.substr(1, std::string::npos)); + llvm::errs() << "### Adding argument " << Str << " at beginning\n"; + Args.insert(Args.begin() + 1, Str); + } else if (Edit[0] == '+') { + const char *Str = + SaveStringInSet(SavedStrings, Edit.substr(1, std::string::npos)); + llvm::errs() << "### Adding argument " << Str << " at end\n"; + Args.push_back(Str); + } else if (Edit[0] == 'x' || Edit[0] == 'X') { + std::string Option = Edit.substr(1, std::string::npos); + for (unsigned i = 1; i < Args.size();) { + if (Option == Args[i]) { + llvm::errs() << "### Deleting argument " << Args[i] << '\n'; + Args.erase(Args.begin() + i); + if (Edit[0] == 'X') { + if (i < Args.size()) { + llvm::errs() << "### Deleting argument " << Args[i] << '\n'; + Args.erase(Args.begin() + i); + } else + llvm::errs() << "### Invalid X edit, end of command line!\n"; + } + } else + ++i; + } + } else if (Edit[0] == 'O') { + for (unsigned i = 1; i < Args.size();) { + const char *A = Args[i]; + if (A[0] == '-' && A[1] == 'O' && + (A[2] == '\0' || + (A[3] == '\0' && (A[2] == 's' || A[2] == 'z' || + ('0' <= A[2] && A[2] <= '9'))))) { + llvm::errs() << "### Deleting argument " << Args[i] << '\n'; + Args.erase(Args.begin() + i); + } else + ++i; + } + llvm::errs() << "### Adding argument " << Edit << " at end\n"; + Args.push_back(SaveStringInSet(SavedStrings, '-' + Edit)); + } else { + llvm::errs() << "### Unrecognized edit: " << Edit << "\n"; + } +} + +/// ApplyQAOverride - Apply a comma separate list of edits to the +/// input argument lists. See ApplyOneQAOverride. +void ApplyQAOverride(std::vector<const char*> &Args, const char *OverrideStr, + std::set<std::string> &SavedStrings) { + llvm::errs() << "### QA_OVERRIDE_GCC3_OPTIONS: " << OverrideStr << "\n"; + + // This does not need to be efficient. + + const char *S = OverrideStr; + while (*S) { + const char *End = ::strchr(S, ' '); + if (!End) + End = S + strlen(S); + if (End != S) + ApplyOneQAOverride(Args, std::string(S, End), SavedStrings); + S = End; + if (*S != '\0') + ++S; + } +} + +int main(int argc, const char **argv) { + llvm::sys::PrintStackTraceOnErrorSignal(); + llvm::PrettyStackTraceProgram X(argc, argv); + + llvm::sys::Path Path = GetExecutablePath(argv[0]); + DriverDiagnosticPrinter DiagClient(Path.getBasename(), llvm::errs()); + + Diagnostic Diags(&DiagClient); + + Driver TheDriver(Path.getBasename().c_str(), Path.getDirname().c_str(), + llvm::sys::getHostTriple().c_str(), + "a.out", Diags); + + llvm::OwningPtr<Compilation> C; + + // Handle QA_OVERRIDE_GCC3_OPTIONS and CCC_ADD_ARGS, used for editing a + // command line behind the scenes. + std::set<std::string> SavedStrings; + if (const char *OverrideStr = ::getenv("QA_OVERRIDE_GCC3_OPTIONS")) { + // FIXME: Driver shouldn't take extra initial argument. + std::vector<const char*> StringPointers(argv, argv + argc); + + ApplyQAOverride(StringPointers, OverrideStr, SavedStrings); + + C.reset(TheDriver.BuildCompilation(StringPointers.size(), + &StringPointers[0])); + } else if (const char *Cur = ::getenv("CCC_ADD_ARGS")) { + std::vector<const char*> StringPointers; + + // FIXME: Driver shouldn't take extra initial argument. + StringPointers.push_back(argv[0]); + + for (;;) { + const char *Next = strchr(Cur, ','); + + if (Next) { + StringPointers.push_back(SaveStringInSet(SavedStrings, + std::string(Cur, Next))); + Cur = Next + 1; + } else { + if (*Cur != '\0') + StringPointers.push_back(SaveStringInSet(SavedStrings, Cur)); + break; + } + } + + StringPointers.insert(StringPointers.end(), argv + 1, argv + argc); + + C.reset(TheDriver.BuildCompilation(StringPointers.size(), + &StringPointers[0])); + } else + C.reset(TheDriver.BuildCompilation(argc, argv)); + + int Res = 0; + if (C.get()) + Res = C->Execute(); + + llvm::llvm_shutdown(); + + return Res; +} + diff --git a/tools/scan-view/Reporter.py b/tools/scan-view/Reporter.py new file mode 100644 index 0000000..9560fc1 --- /dev/null +++ b/tools/scan-view/Reporter.py @@ -0,0 +1,248 @@ +"""Methods for reporting bugs.""" + +import subprocess, sys, os + +__all__ = ['ReportFailure', 'BugReport', 'getReporters'] + +# + +class ReportFailure(Exception): + """Generic exception for failures in bug reporting.""" + def __init__(self, value): + self.value = value + +# Collect information about a bug. + +class BugReport: + def __init__(self, title, description, files): + self.title = title + self.description = description + self.files = files + +# Reporter interfaces. + +import os + +import email, mimetypes, smtplib +from email import encoders +from email.message import Message +from email.mime.base import MIMEBase +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText + +#===------------------------------------------------------------------------===# +# ReporterParameter +#===------------------------------------------------------------------------===# + +class ReporterParameter: + def __init__(self, n): + self.name = n + def getName(self): + return self.name + def getValue(self,r,bugtype,getConfigOption): + return getConfigOption(r.getName(),self.getName()) + def saveConfigValue(self): + return True + +class TextParameter (ReporterParameter): + def getHTML(self,r,bugtype,getConfigOption): + return """\ +<tr> +<td class="form_clabel">%s:</td> +<td class="form_value"><input type="text" name="%s_%s" value="%s"></td> +</tr>"""%(self.getName(),r.getName(),self.getName(),self.getValue(r,bugtype,getConfigOption)) + +class SelectionParameter (ReporterParameter): + def __init__(self, n, values): + ReporterParameter.__init__(self,n) + self.values = values + + def getHTML(self,r,bugtype,getConfigOption): + default = self.getValue(r,bugtype,getConfigOption) + return """\ +<tr> +<td class="form_clabel">%s:</td><td class="form_value"><select name="%s_%s"> +%s +</select></td>"""%(self.getName(),r.getName(),self.getName(),'\n'.join(["""\ +<option value="%s"%s>%s</option>"""%(o[0], + o[0] == default and ' selected="selected"' or '', + o[1]) for o in self.values])) + +#===------------------------------------------------------------------------===# +# Reporters +#===------------------------------------------------------------------------===# + +class EmailReporter: + def getName(self): + return 'Email' + + def getParameters(self): + return map(lambda x:TextParameter(x),['To', 'From', 'SMTP Server', 'SMTP Port']) + + # Lifted from python email module examples. + def attachFile(self, outer, path): + # Guess the content type based on the file's extension. Encoding + # will be ignored, although we should check for simple things like + # gzip'd or compressed files. + ctype, encoding = mimetypes.guess_type(path) + if ctype is None or encoding is not None: + # No guess could be made, or the file is encoded (compressed), so + # use a generic bag-of-bits type. + ctype = 'application/octet-stream' + maintype, subtype = ctype.split('/', 1) + if maintype == 'text': + fp = open(path) + # Note: we should handle calculating the charset + msg = MIMEText(fp.read(), _subtype=subtype) + fp.close() + else: + fp = open(path, 'rb') + msg = MIMEBase(maintype, subtype) + msg.set_payload(fp.read()) + fp.close() + # Encode the payload using Base64 + encoders.encode_base64(msg) + # Set the filename parameter + msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(path)) + outer.attach(msg) + + def fileReport(self, report, parameters): + mainMsg = """\ +BUG REPORT +--- +Title: %s +Description: %s +"""%(report.title, report.description) + + if not parameters.get('To'): + raise ReportFailure('No "To" address specified.') + if not parameters.get('From'): + raise ReportFailure('No "From" address specified.') + + msg = MIMEMultipart() + msg['Subject'] = 'BUG REPORT: %s'%(report.title) + # FIXME: Get config parameters + msg['To'] = parameters.get('To') + msg['From'] = parameters.get('From') + msg.preamble = mainMsg + + msg.attach(MIMEText(mainMsg, _subtype='text/plain')) + for file in report.files: + self.attachFile(msg, file) + + try: + s = smtplib.SMTP(host=parameters.get('SMTP Server'), + port=parameters.get('SMTP Port')) + s.sendmail(msg['From'], msg['To'], msg.as_string()) + s.close() + except: + raise ReportFailure('Unable to send message via SMTP.') + + return "Message sent!" + +class BugzillaReporter: + def getName(self): + return 'Bugzilla' + + def getParameters(self): + return map(lambda x:TextParameter(x),['URL','Product']) + + def fileReport(self, report, parameters): + raise NotImplementedError + + +class RadarClassificationParameter(SelectionParameter): + def __init__(self): + SelectionParameter.__init__(self,"Classification", + [['1', 'Security'], ['2', 'Crash/Hang/Data Loss'], + ['3', 'Performance'], ['4', 'UI/Usability'], + ['6', 'Serious Bug'], ['7', 'Other']]) + + def saveConfigValue(self): + return False + + def getValue(self,r,bugtype,getConfigOption): + if bugtype.find("leak") != -1: + return '3' + elif bugtype.find("dereference") != -1: + return '2' + elif bugtype.find("missing ivar release") != -1: + return '3' + else: + return '7' + +class RadarReporter: + @staticmethod + def isAvailable(): + # FIXME: Find this .scpt better + path = os.path.join(os.path.dirname(__file__),'Resources/GetRadarVersion.scpt') + try: + p = subprocess.Popen(['osascript',path], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except: + return False + data,err = p.communicate() + res = p.wait() + # FIXME: Check version? Check for no errors? + return res == 0 + + def getName(self): + return 'Radar' + + def getParameters(self): + return [ TextParameter('Component'), TextParameter('Component Version'), + RadarClassificationParameter() ] + + def fileReport(self, report, parameters): + component = parameters.get('Component', '') + componentVersion = parameters.get('Component Version', '') + classification = parameters.get('Classification', '') + personID = "" + diagnosis = "" + config = "" + + if not component.strip(): + component = 'Bugs found by clang Analyzer' + if not componentVersion.strip(): + componentVersion = 'X' + + script = os.path.join(os.path.dirname(__file__),'Resources/FileRadar.scpt') + args = ['osascript', script, component, componentVersion, classification, personID, report.title, + report.description, diagnosis, config] + map(os.path.abspath, report.files) +# print >>sys.stderr, args + try: + p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except: + raise ReportFailure("Unable to file radar (AppleScript failure).") + data, err = p.communicate() + res = p.wait() + + if res: + raise ReportFailure("Unable to file radar (AppleScript failure).") + + try: + values = eval(data) + except: + raise ReportFailure("Unable to process radar results.") + + # We expect (int: bugID, str: message) + if len(values) != 2 or not isinstance(values[0], int): + raise ReportFailure("Unable to process radar results.") + + bugID,message = values + bugID = int(bugID) + + if not bugID: + raise ReportFailure(message) + + return "Filed: <a href=\"rdar://%d/\">%d</a>"%(bugID,bugID) + +### + +def getReporters(): + reporters = [] + if RadarReporter.isAvailable(): + reporters.append(RadarReporter()) + reporters.append(EmailReporter()) + return reporters + diff --git a/tools/scan-view/Resources/FileRadar.scpt b/tools/scan-view/Resources/FileRadar.scpt Binary files differnew file mode 100644 index 0000000..1c74552 --- /dev/null +++ b/tools/scan-view/Resources/FileRadar.scpt diff --git a/tools/scan-view/Resources/GetRadarVersion.scpt b/tools/scan-view/Resources/GetRadarVersion.scpt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tools/scan-view/Resources/GetRadarVersion.scpt diff --git a/tools/scan-view/Resources/bugcatcher.ico b/tools/scan-view/Resources/bugcatcher.ico Binary files differnew file mode 100644 index 0000000..22d39b5 --- /dev/null +++ b/tools/scan-view/Resources/bugcatcher.ico diff --git a/tools/scan-view/ScanView.py b/tools/scan-view/ScanView.py new file mode 100644 index 0000000..837adae --- /dev/null +++ b/tools/scan-view/ScanView.py @@ -0,0 +1,770 @@ +import BaseHTTPServer +import SimpleHTTPServer +import os +import sys +import urllib, urlparse +import posixpath +import StringIO +import re +import shutil +import threading +import time +import socket +import itertools + +import Reporter +import ConfigParser + +### +# Various patterns matched or replaced by server. + +kReportFileRE = re.compile('(.*/)?report-(.*)\\.html') + +kBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->') + +# <!-- REPORTPROBLEM file="crashes/clang_crash_ndSGF9.mi" stderr="crashes/clang_crash_ndSGF9.mi.stderr.txt" info="crashes/clang_crash_ndSGF9.mi.info" --> + +kReportCrashEntryRE = re.compile('<!-- REPORTPROBLEM (.*?)-->') +kReportCrashEntryKeyValueRE = re.compile(' ?([^=]+)="(.*?)"') + +kReportReplacements = [] + +# Add custom javascript. +kReportReplacements.append((re.compile('<!-- SUMMARYENDHEAD -->'), """\ +<script language="javascript" type="text/javascript"> +function load(url) { + if (window.XMLHttpRequest) { + req = new XMLHttpRequest(); + } else if (window.ActiveXObject) { + req = new ActiveXObject("Microsoft.XMLHTTP"); + } + if (req != undefined) { + req.open("GET", url, true); + req.send(""); + } +} +</script>""")) + +# Insert additional columns. +kReportReplacements.append((re.compile('<!-- REPORTBUGCOL -->'), + '<td></td><td></td>')) + +# Insert report bug and open file links. +kReportReplacements.append((re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->'), + ('<td class="Button"><a href="report/\\1">Report Bug</a></td>' + + '<td class="Button"><a href="javascript:load(\'open/\\1\')">Open File</a></td>'))) + +kReportReplacements.append((re.compile('<!-- REPORTHEADER -->'), + '<h3><a href="/">Summary</a> > Report %(report)s</h3>')) + +kReportReplacements.append((re.compile('<!-- REPORTSUMMARYEXTRA -->'), + '<td class="Button"><a href="report/%(report)s">Report Bug</a></td>')) + +# Insert report crashes link. + +# Disabled for the time being until we decide exactly when this should +# be enabled. Also the radar reporter needs to be fixed to report +# multiple files. + +#kReportReplacements.append((re.compile('<!-- REPORTCRASHES -->'), +# '<br>These files will automatically be attached to ' + +# 'reports filed here: <a href="report_crashes">Report Crashes</a>.')) + +### +# Other simple parameters + +kResources = posixpath.join(posixpath.dirname(__file__), 'Resources') +kConfigPath = os.path.expanduser('~/.scanview.cfg') + +### + +__version__ = "0.1" + +__all__ = ["create_server"] + +class ReporterThread(threading.Thread): + def __init__(self, report, reporter, parameters, server): + threading.Thread.__init__(self) + self.report = report + self.server = server + self.reporter = reporter + self.parameters = parameters + self.success = False + self.status = None + + def run(self): + result = None + try: + if self.server.options.debug: + print >>sys.stderr, "%s: SERVER: submitting bug."%(sys.argv[0],) + self.status = self.reporter.fileReport(self.report, self.parameters) + self.success = True + time.sleep(3) + if self.server.options.debug: + print >>sys.stderr, "%s: SERVER: submission complete."%(sys.argv[0],) + except Reporter.ReportFailure,e: + self.status = e.value + except Exception,e: + s = StringIO.StringIO() + import traceback + print >>s,'<b>Unhandled Exception</b><br><pre>' + traceback.print_exc(e,file=s) + print >>s,'</pre>' + self.status = s.getvalue() + +class ScanViewServer(BaseHTTPServer.HTTPServer): + def __init__(self, address, handler, root, reporters, options): + BaseHTTPServer.HTTPServer.__init__(self, address, handler) + self.root = root + self.reporters = reporters + self.options = options + self.halted = False + self.config = None + self.load_config() + + def load_config(self): + self.config = ConfigParser.RawConfigParser() + + # Add defaults + self.config.add_section('ScanView') + for r in self.reporters: + self.config.add_section(r.getName()) + for p in r.getParameters(): + if p.saveConfigValue(): + self.config.set(r.getName(), p.getName(), '') + + # Ignore parse errors + try: + self.config.read([kConfigPath]) + except: + pass + + # Save on exit + import atexit + atexit.register(lambda: self.save_config()) + + def save_config(self): + # Ignore errors (only called on exit). + try: + f = open(kConfigPath,'w') + self.config.write(f) + f.close() + except: + pass + + def halt(self): + self.halted = True + if self.options.debug: + print >>sys.stderr, "%s: SERVER: halting." % (sys.argv[0],) + + def serve_forever(self): + while not self.halted: + if self.options.debug > 1: + print >>sys.stderr, "%s: SERVER: waiting..." % (sys.argv[0],) + try: + self.handle_request() + except OSError,e: + print 'OSError',e.errno + + def finish_request(self, request, client_address): + if self.options.autoReload: + import ScanView + self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler + BaseHTTPServer.HTTPServer.finish_request(self, request, client_address) + + def handle_error(self, request, client_address): + # Ignore socket errors + info = sys.exc_info() + if info and isinstance(info[1], socket.error): + if self.options.debug > 1: + print >>sys.stderr, "%s: SERVER: ignored socket error." % (sys.argv[0],) + return + BaseHTTPServer.HTTPServer.handle_error(self, request, client_address) + +# Borrowed from Quixote, with simplifications. +def parse_query(qs, fields=None): + if fields is None: + fields = {} + for chunk in filter(None, qs.split('&')): + if '=' not in chunk: + name = chunk + value = '' + else: + name, value = chunk.split('=', 1) + name = urllib.unquote(name.replace('+', ' ')) + value = urllib.unquote(value.replace('+', ' ')) + item = fields.get(name) + if item is None: + fields[name] = [value] + else: + item.append(value) + return fields + +class ScanViewRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): + server_version = "ScanViewServer/" + __version__ + dynamic_mtime = time.time() + + def do_HEAD(self): + try: + SimpleHTTPServer.SimpleHTTPRequestHandler.do_HEAD(self) + except Exception,e: + self.handle_exception(e) + + def do_GET(self): + try: + SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) + except Exception,e: + self.handle_exception(e) + + def do_POST(self): + """Serve a POST request.""" + try: + length = self.headers.getheader('content-length') or "0" + try: + length = int(length) + except: + length = 0 + content = self.rfile.read(length) + fields = parse_query(content) + f = self.send_head(fields) + if f: + self.copyfile(f, self.wfile) + f.close() + except Exception,e: + self.handle_exception(e) + + def log_message(self, format, *args): + if self.server.options.debug: + sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" % + (sys.argv[0], + self.address_string(), + self.log_date_time_string(), + format%args)) + + def load_report(self, report): + path = os.path.join(self.server.root, 'report-%s.html'%report) + data = open(path).read() + keys = {} + for item in kBugKeyValueRE.finditer(data): + k,v = item.groups() + keys[k] = v + return keys + + def load_crashes(self): + path = posixpath.join(self.server.root, 'index.html') + data = open(path).read() + problems = [] + for item in kReportCrashEntryRE.finditer(data): + fieldData = item.group(1) + fields = dict([i.groups() for i in + kReportCrashEntryKeyValueRE.finditer(fieldData)]) + problems.append(fields) + return problems + + def handle_exception(self, exc): + import traceback + s = StringIO.StringIO() + print >>s, "INTERNAL ERROR\n" + traceback.print_exc(exc, s) + f = self.send_string(s.getvalue(), 'text/plain') + if f: + self.copyfile(f, self.wfile) + f.close() + + def get_scalar_field(self, name): + if name in self.fields: + return self.fields[name][0] + else: + return None + + def submit_bug(self, c): + title = self.get_scalar_field('title') + description = self.get_scalar_field('description') + report = self.get_scalar_field('report') + reporterIndex = self.get_scalar_field('reporter') + files = [] + for fileID in self.fields.get('files',[]): + try: + i = int(fileID) + except: + i = None + if i is None or i<0 or i>=len(c.files): + return (False, 'Invalid file ID') + files.append(c.files[i]) + + if not title: + return (False, "Missing title.") + if not description: + return (False, "Missing description.") + try: + reporterIndex = int(reporterIndex) + except: + return (False, "Invalid report method.") + + # Get the reporter and parameters. + reporter = self.server.reporters[reporterIndex] + parameters = {} + for o in reporter.getParameters(): + name = '%s_%s'%(reporter.getName(),o.getName()) + if name not in self.fields: + return (False, + 'Missing field "%s" for %s report method.'%(name, + reporter.getName())) + parameters[o.getName()] = self.get_scalar_field(name) + + # Update config defaults. + if report != 'None': + self.server.config.set('ScanView', 'reporter', reporterIndex) + for o in reporter.getParameters(): + if o.saveConfigValue(): + name = o.getName() + self.server.config.set(reporter.getName(), name, parameters[name]) + + # Create the report. + bug = Reporter.BugReport(title, description, files) + + # Kick off a reporting thread. + t = ReporterThread(bug, reporter, parameters, self.server) + t.start() + + # Wait for thread to die... + while t.isAlive(): + time.sleep(.25) + submitStatus = t.status + + return (t.success, t.status) + + def send_report_submit(self): + report = self.get_scalar_field('report') + c = self.get_report_context(report) + if c.reportSource is None: + reportingFor = "Report Crashes > " + fileBug = """\ +<a href="/report_crashes">File Bug</a> > """%locals() + else: + reportingFor = '<a href="/%s">Report %s</a> > ' % (c.reportSource, + report) + fileBug = '<a href="/report/%s">File Bug</a> > ' % report + title = self.get_scalar_field('title') + description = self.get_scalar_field('description') + + res,message = self.submit_bug(c) + + if res: + statusClass = 'SubmitOk' + statusName = 'Succeeded' + else: + statusClass = 'SubmitFail' + statusName = 'Failed' + + result = """ +<head> + <title>Bug Submission</title> + <link rel="stylesheet" type="text/css" href="/scanview.css" /> +</head> +<body> +<h3> +<a href="/">Summary</a> > +%(reportingFor)s +%(fileBug)s +Submit</h3> +<form name="form" action=""> +<table class="form"> +<tr><td> +<table class="form_group"> +<tr> + <td class="form_clabel">Title:</td> + <td class="form_value"> + <input type="text" name="title" size="50" value="%(title)s" disabled> + </td> +</tr> +<tr> + <td class="form_label">Description:</td> + <td class="form_value"> +<textarea rows="10" cols="80" name="description" disabled> +%(description)s +</textarea> + </td> +</table> +</td></tr> +</table> +</form> +<h1 class="%(statusClass)s">Submission %(statusName)s</h1> +%(message)s +<p> +<hr> +<a href="/">Return to Summary</a> +</body> +</html>"""%locals() + return self.send_string(result) + + def send_open_report(self, report): + try: + keys = self.load_report(report) + except IOError: + return self.send_error(400, 'Invalid report.') + + file = keys.get('FILE') + if not file or not posixpath.exists(file): + return self.send_error(400, 'File does not exist: "%s"' % file) + + import startfile + if self.server.options.debug: + print >>sys.stderr, '%s: SERVER: opening "%s"'%(sys.argv[0], + file) + + status = startfile.open(file) + if status: + res = 'Opened: "%s"' % file + else: + res = 'Open failed: "%s"' % file + + return self.send_string(res, 'text/plain') + + def get_report_context(self, report): + class Context: + pass + if report is None or report == 'None': + data = self.load_crashes() + # Don't allow empty reports. + if not data: + raise ValueError, 'No crashes detected!' + c = Context() + c.title = 'clang static analyzer failures' + + stderrSummary = "" + for item in data: + if 'stderr' in item: + path = posixpath.join(self.server.root, item['stderr']) + if os.path.exists(path): + lns = itertools.islice(open(path), 0, 10) + stderrSummary += '%s\n--\n%s' % (item.get('src', + '<unknown>'), + ''.join(lns)) + + c.description = """\ +The clang static analyzer failed on these inputs: +%s + +STDERR Summary +-------------- +%s +""" % ('\n'.join([item.get('src','<unknown>') for item in data]), + stderrSummary) + c.reportSource = None + c.navMarkup = "Report Crashes > " + c.files = [] + for item in data: + c.files.append(item.get('src','')) + c.files.append(posixpath.join(self.server.root, + item.get('file',''))) + c.files.append(posixpath.join(self.server.root, + item.get('clangfile',''))) + c.files.append(posixpath.join(self.server.root, + item.get('stderr',''))) + c.files.append(posixpath.join(self.server.root, + item.get('info',''))) + # Just in case something failed, ignore files which don't + # exist. + c.files = [f for f in c.files + if os.path.exists(f) and os.path.isfile(f)] + else: + # Check that this is a valid report. + path = posixpath.join(self.server.root, 'report-%s.html' % report) + if not posixpath.exists(path): + raise ValueError, 'Invalid report ID' + keys = self.load_report(report) + c = Context() + c.title = keys.get('DESC','clang error (unrecognized') + c.description = """\ +Bug reported by the clang static analyzer. + +Description: %s +File: %s +Line: %s +"""%(c.title, keys.get('FILE','<unknown>'), keys.get('LINE', '<unknown>')) + c.reportSource = 'report-%s.html' % report + c.navMarkup = """<a href="/%s">Report %s</a> > """ % (c.reportSource, + report) + + c.files = [path] + return c + + def send_report(self, report, configOverrides=None): + def getConfigOption(section, field): + if (configOverrides is not None and + section in configOverrides and + field in configOverrides[section]): + return configOverrides[section][field] + return self.server.config.get(section, field) + + # report is None is used for crashes + try: + c = self.get_report_context(report) + except ValueError, e: + return self.send_error(400, e.message) + + title = c.title + description= c.description + reportingFor = c.navMarkup + if c.reportSource is None: + extraIFrame = "" + else: + extraIFrame = """\ +<iframe src="/%s" width="100%%" height="40%%" + scrolling="auto" frameborder="1"> + <a href="/%s">View Bug Report</a> +</iframe>""" % (c.reportSource, c.reportSource) + + reporterSelections = [] + reporterOptions = [] + + try: + active = int(getConfigOption('ScanView','reporter')) + except: + active = 0 + for i,r in enumerate(self.server.reporters): + selected = (i == active) + if selected: + selectedStr = ' selected' + else: + selectedStr = '' + reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName())) + options = '\n'.join([ o.getHTML(r,title,getConfigOption) for o in r.getParameters()]) + display = ('none','')[selected] + reporterOptions.append("""\ +<tr id="%sReporterOptions" style="display:%s"> + <td class="form_label">%s Options</td> + <td class="form_value"> + <table class="form_inner_group"> +%s + </table> + </td> +</tr> +"""%(r.getName(),display,r.getName(),options)) + reporterSelections = '\n'.join(reporterSelections) + reporterOptionsDivs = '\n'.join(reporterOptions) + reportersArray = '[%s]'%(','.join([`r.getName()` for r in self.server.reporters])) + + if c.files: + fieldSize = min(5, len(c.files)) + attachFileOptions = '\n'.join(["""\ +<option value="%d" selected>%s</option>""" % (i,v) for i,v in enumerate(c.files)]) + attachFileRow = """\ +<tr> + <td class="form_label">Attach:</td> + <td class="form_value"> +<select style="width:100%%" name="files" multiple size=%d> +%s +</select> + </td> +</tr> +""" % (min(5, len(c.files)), attachFileOptions) + else: + attachFileRow = "" + + result = """<html> +<head> + <title>File Bug</title> + <link rel="stylesheet" type="text/css" href="/scanview.css" /> +</head> +<script language="javascript" type="text/javascript"> +var reporters = %(reportersArray)s; +function updateReporterOptions() { + index = document.getElementById('reporter').selectedIndex; + for (var i=0; i < reporters.length; ++i) { + o = document.getElementById(reporters[i] + "ReporterOptions"); + if (i == index) { + o.style.display = ""; + } else { + o.style.display = "none"; + } + } +} +</script> +<body onLoad="updateReporterOptions()"> +<h3> +<a href="/">Summary</a> > +%(reportingFor)s +File Bug</h3> +<form name="form" action="/report_submit" method="post"> +<input type="hidden" name="report" value="%(report)s"> + +<table class="form"> +<tr><td> +<table class="form_group"> +<tr> + <td class="form_clabel">Title:</td> + <td class="form_value"> + <input type="text" name="title" size="50" value="%(title)s"> + </td> +</tr> +<tr> + <td class="form_label">Description:</td> + <td class="form_value"> +<textarea rows="10" cols="80" name="description"> +%(description)s +</textarea> + </td> +</tr> + +%(attachFileRow)s + +</table> +<br> +<table class="form_group"> +<tr> + <td class="form_clabel">Method:</td> + <td class="form_value"> + <select id="reporter" name="reporter" onChange="updateReporterOptions()"> + %(reporterSelections)s + </select> + </td> +</tr> +%(reporterOptionsDivs)s +</table> +<br> +</td></tr> +<tr><td class="form_submit"> + <input align="right" type="submit" name="Submit" value="Submit"> +</td></tr> +</table> +</form> + +%(extraIFrame)s + +</body> +</html>"""%locals() + + return self.send_string(result) + + def send_head(self, fields=None): + if (self.server.options.onlyServeLocal and + self.client_address[0] != '127.0.0.1'): + return self.send_error('401', 'Unauthorized host.') + + if fields is None: + fields = {} + self.fields = fields + + o = urlparse.urlparse(self.path) + self.fields = parse_query(o.query, fields) + path = posixpath.normpath(urllib.unquote(o.path)) + + # Split the components and strip the root prefix. + components = path.split('/')[1:] + + # Special case some top-level entries. + if components: + name = components[0] + if len(components)==2: + if name=='report': + return self.send_report(components[1]) + elif name=='open': + return self.send_open_report(components[1]) + elif len(components)==1: + if name=='quit': + self.server.halt() + return self.send_string('Goodbye.', 'text/plain') + elif name=='report_submit': + return self.send_report_submit() + elif name=='report_crashes': + overrides = { 'ScanView' : {}, + 'Radar' : {}, + 'Email' : {} } + for i,r in enumerate(self.server.reporters): + if r.getName() == 'Radar': + overrides['ScanView']['reporter'] = i + break + overrides['Radar']['Component'] = 'llvm - checker' + overrides['Radar']['Component Version'] = 'X' + return self.send_report(None, overrides) + elif name=='favicon.ico': + return self.send_path(posixpath.join(kResources,'bugcatcher.ico')) + + # Match directory entries. + if components[-1] == '': + components[-1] = 'index.html' + + suffix = '/'.join(components) + + # The summary may reference source files on disk using rooted + # paths. Make sure these resolve correctly for now. + # FIXME: This isn't a very good idea... we should probably + # mark rooted paths somehow. + if os.path.exists(posixpath.join('/', suffix)): + path = posixpath.join('/', suffix) + else: + path = posixpath.join(self.server.root, suffix) + + if self.server.options.debug > 1: + print >>sys.stderr, '%s: SERVER: sending path "%s"'%(sys.argv[0], + path) + return self.send_path(path) + + def send_404(self): + self.send_error(404, "File not found") + return None + + def send_path(self, path): + ctype = self.guess_type(path) + if ctype.startswith('text/'): + # Patch file instead + return self.send_patched_file(path, ctype) + else: + mode = 'rb' + try: + f = open(path, mode) + except IOError: + return self.send_404() + return self.send_file(f, ctype) + + def send_file(self, f, ctype): + # Patch files to add links, but skip binary files. + self.send_response(200) + self.send_header("Content-type", ctype) + fs = os.fstat(f.fileno()) + self.send_header("Content-Length", str(fs[6])) + self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) + self.end_headers() + return f + + def send_string(self, s, ctype='text/html', headers=True, mtime=None): + if headers: + self.send_response(200) + self.send_header("Content-type", ctype) + self.send_header("Content-Length", str(len(s))) + if mtime is None: + mtime = self.dynamic_mtime + self.send_header("Last-Modified", self.date_time_string(mtime)) + self.end_headers() + return StringIO.StringIO(s) + + def send_patched_file(self, path, ctype): + # Allow a very limited set of variables. This is pretty gross. + variables = {} + variables['report'] = '' + m = kReportFileRE.match(path) + if m: + variables['report'] = m.group(2) + + try: + f = open(path,'r') + except IOError: + return self.send_404() + fs = os.fstat(f.fileno()) + data = f.read() + for a,b in kReportReplacements: + data = a.sub(b % variables, data) + return self.send_string(data, ctype, mtime=fs.st_mtime) + + +def create_server(address, options, root): + import Reporter + + reporters = Reporter.getReporters() + + return ScanViewServer(address, ScanViewRequestHandler, + root, + reporters, + options) diff --git a/tools/scan-view/scan-view b/tools/scan-view/scan-view new file mode 100755 index 0000000..fb27da6 --- /dev/null +++ b/tools/scan-view/scan-view @@ -0,0 +1,131 @@ +#!/usr/bin/env python + +"""The clang static analyzer results viewer. +""" + +import sys +import posixpath +import thread +import time +import urllib +import webbrowser + +# How long to wait for server to start. +kSleepTimeout = .05 +kMaxSleeps = int(60 / kSleepTimeout) + +# Default server parameters + +kDefaultHost = '127.0.0.1' +kDefaultPort = 8181 +kMaxPortsToTry = 100 + +### + +def url_is_up(url): + try: + o = urllib.urlopen(url) + except IOError: + return False + o.close() + return True + +def start_browser(port, options): + import urllib, webbrowser + + url = 'http://%s:%d'%(options.host, port) + + # Wait for server to start... + if options.debug: + sys.stderr.write('%s: Waiting for server.' % sys.argv[0]) + sys.stderr.flush() + for i in range(kMaxSleeps): + if url_is_up(url): + break + if options.debug: + sys.stderr.write('.') + sys.stderr.flush() + time.sleep(kSleepTimeout) + else: + print >>sys.stderr,'WARNING: Unable to detect that server started.' + + if options.debug: + print >>sys.stderr,'%s: Starting webbrowser...' % sys.argv[0] + webbrowser.open(url) + +def run(port, options, root): + import ScanView + try: + print 'Starting scan-view at: http://%s:%d'%(options.host, + port) + print ' Use Ctrl-C to exit.' + httpd = ScanView.create_server((options.host, port), + options, root) + httpd.serve_forever() + except KeyboardInterrupt: + pass + +def port_is_open(port): + import SocketServer + try: + t = SocketServer.TCPServer((kDefaultHost,port),None) + except: + return False + t.server_close() + return True + +def main(): + from optparse import OptionParser + parser = OptionParser('usage: %prog [options] <results directory>') + parser.set_description(__doc__) + parser.add_option( + '--host', dest="host", default=kDefaultHost, type="string", + help="Host interface to listen on. (default=%s)" % kDefaultHost) + parser.add_option( + '--port', dest="port", default=None, type="int", + help="Port to listen on. (default=%s)" % kDefaultPort) + parser.add_option("--debug", dest="debug", default=0, + action="count", + help="Print additional debugging information.") + parser.add_option("--auto-reload", dest="autoReload", default=False, + action="store_true", + help="Automatically update module for each request.") + parser.add_option("--no-browser", dest="startBrowser", default=True, + action="store_false", + help="Don't open a webbrowser on startup.") + parser.add_option("--allow-all-hosts", dest="onlyServeLocal", default=True, + action="store_false", + help='Allow connections from any host (access restricted to "127.0.0.1" by default)') + (options, args) = parser.parse_args() + + if len(args) != 1: + parser.error('No results directory specified.') + root, = args + + # Make sure this directory is in a reasonable state to view. + if not posixpath.exists(posixpath.join(root,'index.html')): + parser.error('Invalid directory, analysis results not found!') + + # Find an open port. We aren't particularly worried about race + # conditions here. Note that if the user specified a port we only + # use that one. + if options.port is not None: + port = options.port + else: + for i in range(kMaxPortsToTry): + if port_is_open(kDefaultPort + i): + port = kDefaultPort + i + break + else: + parser.error('Unable to find usable port in [%d,%d)'%(kDefaultPort, + kDefaultPort+kMaxPortsToTry)) + + # Kick off thread to wait for server and start web browser, if + # requested. + if options.startBrowser: + t = thread.start_new_thread(start_browser, (port,options)) + + run(port, options, root) + +if __name__ == '__main__': + main() diff --git a/tools/scan-view/startfile.py b/tools/scan-view/startfile.py new file mode 100644 index 0000000..e8fbe2d --- /dev/null +++ b/tools/scan-view/startfile.py @@ -0,0 +1,203 @@ +"""Utility for opening a file using the default application in a cross-platform +manner. Modified from http://code.activestate.com/recipes/511443/. +""" + +__version__ = '1.1x' +__all__ = ['open'] + +import os +import sys +import webbrowser +import subprocess + +_controllers = {} +_open = None + + +class BaseController(object): + '''Base class for open program controllers.''' + + def __init__(self, name): + self.name = name + + def open(self, filename): + raise NotImplementedError + + +class Controller(BaseController): + '''Controller for a generic open program.''' + + def __init__(self, *args): + super(Controller, self).__init__(os.path.basename(args[0])) + self.args = list(args) + + def _invoke(self, cmdline): + if sys.platform[:3] == 'win': + closefds = False + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + else: + closefds = True + startupinfo = None + + if (os.environ.get('DISPLAY') or sys.platform[:3] == 'win' or + sys.platform == 'darwin'): + inout = file(os.devnull, 'r+') + else: + # for TTY programs, we need stdin/out + inout = None + + # if possible, put the child precess in separate process group, + # so keyboard interrupts don't affect child precess as well as + # Python + setsid = getattr(os, 'setsid', None) + if not setsid: + setsid = getattr(os, 'setpgrp', None) + + pipe = subprocess.Popen(cmdline, stdin=inout, stdout=inout, + stderr=inout, close_fds=closefds, + preexec_fn=setsid, startupinfo=startupinfo) + + # It is assumed that this kind of tools (gnome-open, kfmclient, + # exo-open, xdg-open and open for OSX) immediately exit after lauching + # the specific application + returncode = pipe.wait() + if hasattr(self, 'fixreturncode'): + returncode = self.fixreturncode(returncode) + return not returncode + + def open(self, filename): + if isinstance(filename, basestring): + cmdline = self.args + [filename] + else: + # assume it is a sequence + cmdline = self.args + filename + try: + return self._invoke(cmdline) + except OSError: + return False + + +# Platform support for Windows +if sys.platform[:3] == 'win': + + class Start(BaseController): + '''Controller for the win32 start progam through os.startfile.''' + + def open(self, filename): + try: + os.startfile(filename) + except WindowsError: + # [Error 22] No application is associated with the specified + # file for this operation: '<URL>' + return False + else: + return True + + _controllers['windows-default'] = Start('start') + _open = _controllers['windows-default'].open + + +# Platform support for MacOS +elif sys.platform == 'darwin': + _controllers['open']= Controller('open') + _open = _controllers['open'].open + + +# Platform support for Unix +else: + + import commands + + # @WARNING: use the private API of the webbrowser module + from webbrowser import _iscommand + + class KfmClient(Controller): + '''Controller for the KDE kfmclient program.''' + + def __init__(self, kfmclient='kfmclient'): + super(KfmClient, self).__init__(kfmclient, 'exec') + self.kde_version = self.detect_kde_version() + + def detect_kde_version(self): + kde_version = None + try: + info = commands.getoutput('kde-config --version') + + for line in info.splitlines(): + if line.startswith('KDE'): + kde_version = line.split(':')[-1].strip() + break + except (OSError, RuntimeError): + pass + + return kde_version + + def fixreturncode(self, returncode): + if returncode is not None and self.kde_version > '3.5.4': + return returncode + else: + return os.EX_OK + + def detect_desktop_environment(): + '''Checks for known desktop environments + + Return the desktop environments name, lowercase (kde, gnome, xfce) + or "generic" + + ''' + + desktop_environment = 'generic' + + if os.environ.get('KDE_FULL_SESSION') == 'true': + desktop_environment = 'kde' + elif os.environ.get('GNOME_DESKTOP_SESSION_ID'): + desktop_environment = 'gnome' + else: + try: + info = commands.getoutput('xprop -root _DT_SAVE_MODE') + if ' = "xfce4"' in info: + desktop_environment = 'xfce' + except (OSError, RuntimeError): + pass + + return desktop_environment + + + def register_X_controllers(): + if _iscommand('kfmclient'): + _controllers['kde-open'] = KfmClient() + + for command in ('gnome-open', 'exo-open', 'xdg-open'): + if _iscommand(command): + _controllers[command] = Controller(command) + + def get(): + controllers_map = { + 'gnome': 'gnome-open', + 'kde': 'kde-open', + 'xfce': 'exo-open', + } + + desktop_environment = detect_desktop_environment() + + try: + controller_name = controllers_map[desktop_environment] + return _controllers[controller_name].open + + except KeyError: + if _controllers.has_key('xdg-open'): + return _controllers['xdg-open'].open + else: + return webbrowser.open + + + if os.environ.get("DISPLAY"): + register_X_controllers() + _open = get() + + +def open(filename): + '''Open a file or an URL in the registered default application.''' + + return _open(filename) |