diff options
author | Daniel Dunbar <daniel@zuster.org> | 2010-02-13 18:33:18 +0000 |
---|---|---|
committer | Daniel Dunbar <daniel@zuster.org> | 2010-02-13 18:33:18 +0000 |
commit | ef7f798c0921cde7e665935a5630578cc1065e0f (patch) | |
tree | cf30aae9ac2c1e83a2384280f8dbbb41e1ff930d | |
parent | b51abe9b5f76e5d06d348c8ef2d0a7cd9d797b58 (diff) |
cindex/Python: Add TranslationUnit.get_includes, patch by Andrew Sutton!
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@96106 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | bindings/python/clang/cindex.py | 48 | ||||
-rw-r--r-- | bindings/python/examples/cindex/cindex-includes.py | 62 | ||||
-rw-r--r-- | bindings/python/tests/cindex/INPUTS/header1.h | 6 | ||||
-rw-r--r-- | bindings/python/tests/cindex/INPUTS/header2.h | 6 | ||||
-rw-r--r-- | bindings/python/tests/cindex/INPUTS/header3.h | 3 | ||||
-rw-r--r-- | bindings/python/tests/cindex/INPUTS/include.cpp | 5 | ||||
-rw-r--r-- | bindings/python/tests/cindex/test_translation_unit.py | 22 |
7 files changed, 152 insertions, 0 deletions
diff --git a/bindings/python/clang/cindex.py b/bindings/python/clang/cindex.py index b6383fb228..c37c69b79b 100644 --- a/bindings/python/clang/cindex.py +++ b/bindings/python/clang/cindex.py @@ -719,6 +719,25 @@ class TranslationUnit(ClangObject): """Get the original translation unit source file name.""" return TranslationUnit_spelling(self) + def get_includes(self): + """ + Return an iterable sequence of FileInclusion objects that describe the + sequence of inclusions in a translation unit. The first object in + this sequence is always the input file. Note that this method will not + recursively iterate over header files included through precompiled + headers. + """ + def visitor(fobj, lptr, depth, includes): + loc = lptr.contents + includes.append(FileInclusion(loc.file, File(fobj), loc, depth)) + + # Automatically adapt CIndex/ctype pointers to python objects + includes = [] + TranslationUnit_includes(self, + TranslationUnit_includes_callback(visitor), + includes) + return iter(includes) + class File(ClangObject): """ The File class represents a particular source file that is part of a @@ -735,6 +754,26 @@ class File(ClangObject): """Return the last modification time of the file.""" return File_time(self) +class FileInclusion(object): + """ + The FileInclusion class represents the inclusion of one source file by + another via a '#include' directive or as the input file for the translation + unit. This class provides information about the included file, the including + file, the location of the '#include' directive and the depth of the included + file in the stack. Note that the input file has depth 0. + """ + + def __init__(self, src, tgt, loc, depth): + self.source = src + self.include = tgt + self.location = loc + self.depth = depth + + @property + def is_input_file(self): + """True if the included file is the input file.""" + return self.depth == 0 + # Additional Functions and Types # String Functions @@ -870,6 +909,15 @@ TranslationUnit_spelling.errcheck = _CXString.from_result TranslationUnit_dispose = lib.clang_disposeTranslationUnit TranslationUnit_dispose.argtypes = [TranslationUnit] +TranslationUnit_includes_callback = CFUNCTYPE(None, + c_object_p, + POINTER(SourceLocation), + c_uint, py_object) +TranslationUnit_includes = lib.clang_getInclusions +TranslationUnit_includes.argtypes = [TranslationUnit, + TranslationUnit_includes_callback, + py_object] + # File Functions File_name = lib.clang_getFileName File_name.argtypes = [File] diff --git a/bindings/python/examples/cindex/cindex-includes.py b/bindings/python/examples/cindex/cindex-includes.py new file mode 100644 index 0000000000..2220552d08 --- /dev/null +++ b/bindings/python/examples/cindex/cindex-includes.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +#===- cindex-includes.py - cindex/Python Inclusion Graph -----*- python -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +""" +A simple command line tool for dumping a Graphviz description (dot) that +describes include dependencies. +""" + +def main(): + import sys + from clang.cindex import Index + + # FIXME: Allow the user to pass command line options to clang so that + # we can use -D and -U. + from optparse import OptionParser, OptionGroup + + parser = OptionParser("usage: %prog [options] {filename} [clang-args*]") + parser.disable_interspersed_args() + (opts, args) = parser.parse_args() + if len(args) == 0: + parser.error('invalid number arguments') + + # FIXME: Add an output file option + out = sys.stdout + + input_path = args.pop(0) + + + index = Index.create() + tu = index.parse(input_path, args) + if not tu: + parser.error("unable to load input") + + # A helper function for generating the node name. + def name(f): + return "\"" + f.name + "\"" + + # Generate the include graph + out.write("digraph G {\n") + for i in tu.get_includes(): + line = " "; + if i.is_input_file: + # Always write the input file as a node just in case it doesn't + # actually include anything. This would generate a 1 node graph. + line += name(i.include) + else: + line += name(i.source) + "->" + name(i.include) + line += "\n"; + out.write(line) + out.write("}\n") + +if __name__ == '__main__': + main() + diff --git a/bindings/python/tests/cindex/INPUTS/header1.h b/bindings/python/tests/cindex/INPUTS/header1.h new file mode 100644 index 0000000000..b4eacbee37 --- /dev/null +++ b/bindings/python/tests/cindex/INPUTS/header1.h @@ -0,0 +1,6 @@ +#ifndef HEADER1 +#define HEADER1 + +#include "header3.h" + +#endif diff --git a/bindings/python/tests/cindex/INPUTS/header2.h b/bindings/python/tests/cindex/INPUTS/header2.h new file mode 100644 index 0000000000..c4eddc0c56 --- /dev/null +++ b/bindings/python/tests/cindex/INPUTS/header2.h @@ -0,0 +1,6 @@ +#ifndef HEADER2 +#define HEADER2 + +#include "header3.h" + +#endif diff --git a/bindings/python/tests/cindex/INPUTS/header3.h b/bindings/python/tests/cindex/INPUTS/header3.h new file mode 100644 index 0000000000..6dca764860 --- /dev/null +++ b/bindings/python/tests/cindex/INPUTS/header3.h @@ -0,0 +1,3 @@ +// Not a guarded header! + +void f(); diff --git a/bindings/python/tests/cindex/INPUTS/include.cpp b/bindings/python/tests/cindex/INPUTS/include.cpp new file mode 100644 index 0000000000..60cfdaae4d --- /dev/null +++ b/bindings/python/tests/cindex/INPUTS/include.cpp @@ -0,0 +1,5 @@ +#include "header1.h" +#include "header2.h" +#include "header1.h" + +int main() { } diff --git a/bindings/python/tests/cindex/test_translation_unit.py b/bindings/python/tests/cindex/test_translation_unit.py index ec12e689d9..3c05c3f06a 100644 --- a/bindings/python/tests/cindex/test_translation_unit.py +++ b/bindings/python/tests/cindex/test_translation_unit.py @@ -49,3 +49,25 @@ def test_unsaved_files_2(): ('fake.c', StringIO.StringIO('int x;'))]) spellings = [c.spelling for c in tu.cursor.get_children()] assert spellings[-1] == 'x' + + +def test_includes(): + def eq(expected, actual): + if not actual.is_input_file: + return expected[0] == actual.source.name and \ + expected[1] == actual.include.name + else: + return expected[1] == actual.include.name + + src = os.path.join(kInputsDir, 'include.cpp') + h1 = os.path.join(kInputsDir, "header1.h") + h2 = os.path.join(kInputsDir, "header2.h") + h3 = os.path.join(kInputsDir, "header3.h") + inc = [(None, src), (src, h1), (h1, h3), (src, h2), (h2, h3)] + + index = Index.create() + tu = index.parse(src) + for i in zip(inc, tu.get_includes()): + assert eq(i[0], i[1]) + + |