"""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
"""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
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]
--- /dev/null
+#!/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()
+
--- /dev/null
+#ifndef HEADER1
+#define HEADER1
+
+#include "header3.h"
+
+#endif
--- /dev/null
+#ifndef HEADER2
+#define HEADER2
+
+#include "header3.h"
+
+#endif
--- /dev/null
+// Not a guarded header!
+
+void f();
--- /dev/null
+#include "header1.h"
+#include "header2.h"
+#include "header1.h"
+
+int main() { }
('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])
+
+