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
This commit is contained in:
Daniel Dunbar 2010-02-13 18:33:18 +00:00
Родитель b51abe9b5f
Коммит ef7f798c09
7 изменённых файлов: 152 добавлений и 0 удалений

Просмотреть файл

@ -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]

Просмотреть файл

@ -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()

Просмотреть файл

@ -0,0 +1,6 @@
#ifndef HEADER1
#define HEADER1
#include "header3.h"
#endif

Просмотреть файл

@ -0,0 +1,6 @@
#ifndef HEADER2
#define HEADER2
#include "header3.h"
#endif

Просмотреть файл

@ -0,0 +1,3 @@
// Not a guarded header!
void f();

Просмотреть файл

@ -0,0 +1,5 @@
#include "header1.h"
#include "header2.h"
#include "header1.h"
int main() { }

Просмотреть файл

@ -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])