コード例 #1
0
        public void CreateDatabaseFromDirectory()
        {
            // cannot really create a database from nothing.
            var dir = Path.GetDirectoryName(new Uri(GetType().Assembly.CodeBase).LocalPath);

            ClangService.CreateDatabaseFromDirectory(dir);
        }
コード例 #2
0
 public void OptionFlags()
 {
     using (var idx = ClangService.CreateIndex()) {
         Assert.AreEqual(GlobalOptionFlags.None, idx.GlobalOptions, "default flags");
         idx.GlobalOptions = GlobalOptionFlags.ThreadBackgroundPriorityForAll;
         Assert.AreEqual(GlobalOptionFlags.ThreadBackgroundPriorityForAll, idx.GlobalOptions, "setter results");
     }
 }
コード例 #3
0
        Tuple <ClangIndex, ClangTranslationUnit> CreateTranslationUnit(string filename, string content = null)
        {
            content = content ?? @"#include <stdio.h> void main () { printf (""hello world""); }";
            File.WriteAllText(filename, content);
            var idx = ClangService.CreateIndex();

            return(new Tuple <ClangIndex, ClangTranslationUnit> (idx, idx.CreateTranslationUnitFromSourceFile(filename, new string [0], new ClangUnsavedFile [0])));
        }
コード例 #4
0
        public void ParseTranslationUnitFromSourceFile()
        {
            string file = "ClangIndexTest.CreateTranslationUnit.c";

            File.WriteAllText(file, @"#include <stdio.h> void main () { printf (""hello world""); }");
            try {
                using (var idx = ClangService.CreateIndex()) {
                    var tu = idx.ParseTranslationUnit(file, new string [0], new ClangUnsavedFile [0], TranslationUnitFlags.None);
                    tu.Dispose();
                }
            } finally {
                File.Delete(file);
            }
        }
コード例 #5
0
        public void NullCursorType()
        {
            var c = ClangService.GetNullCursor();

            Assert.AreEqual(-1, c.ArgumentCount, "ArgumentCount");
            Assert.AreEqual(AvailabilityKind.Available, c.AvailabilityKind, "AvailabilityKind");

            var t = c.CursorType;

            Assert.IsNull(t, "IsNull");

            /*
             * Assert.AreEqual (-1, t.AlignOf, "AlignOf");
             * Assert.AreEqual (-1, t.ArgumentTypeCount, "ArgumentTypeCount");
             * var tt = t.ArrayElementType;
             * Assert.IsNotNull (tt, "ArrayElementType");
             * Assert.AreEqual (-1, t.ArraySize, "ArraySize");
             * tt = t.CanonicalType;
             * Assert.IsNotNull (tt, "CanonicalType");
             * tt = t.ClassType;
             * Assert.IsNotNull (tt, "ClassType");
             * Assert.AreEqual (-1, t.ElementCount, "ElementCount");
             * tt = t.ElementType;
             * Assert.IsNotNull (tt, "ElementType");
             * Assert.AreEqual (CallingConvention.Invalid, t.FunctionTypeCallingConvention, "FunctionTypeCallingConvention");
             * Assert.AreEqual (false, t.IsConstQualifiedType, "IsConstQualifiedType");
             * Assert.AreEqual (false, t.IsFunctionTypeVariadic, "IsFunctionTypeVariadic");
             * Assert.AreEqual (false, t.IsPODType, "IsPODType");
             * Assert.AreEqual (false, t.IsRestrictQualifiedType, "IsRestrictQualifiedType");
             * Assert.AreEqual (false, t.IsVolatileQualifiedType, "IsVolatileQualifiedType");
             * Assert.AreEqual (TypeKind.Invalid, t.Kind, "Kind");
             * tt = t.PointeeType;
             * Assert.IsNotNull (tt, "PointeeType");
             * Assert.AreEqual (RefQualifierKind.None, t.RefQualifier, "RefQualifier");
             * tt = t.ResultType;
             * Assert.IsNotNull (tt, "ResultType");
             * Assert.AreEqual (-1, t.SizeOf, "SizeOf");
             * Assert.AreEqual (string.Empty, t.Spelling, "Spelling");
             * // not in libclang 3.5
             * // Assert.AreEqual (-1, t.TemplateArgumentCount, "TemplateArgumentCount");
             * var cc = t.TypeDeclaration;
             * Assert.IsNotNull (cc, "TypeDeclaration");
             */
        }
コード例 #6
0
ファイル: ClangCursorTest.cs プロジェクト: takeshich/nclang
        public void NullCursor()
        {
            var c = ClangService.GetNullCursor();

            Assert.AreEqual(-1, c.ArgumentCount, "ArgumentCount");
            Assert.AreEqual(AvailabilityKind.Available, c.AvailabilityKind, "AvailabilityKind");

            var t = c.CursorType;

            Assert.IsNull(t, "CursorType");

            Assert.AreEqual(CXXAccessSpecifier.Invalid, c.CxxAccessSpecifier, "CxxAccessSpecifier");
            Assert.AreEqual(string.Empty, c.DeclObjCTypeEncoding, "DeclObjCTypeEncoding");
            Assert.AreEqual(18446744073709551615m, c.EnumConstantDeclUnsignedValue, "EnumConstantDeclUnsignedValue");
            Assert.AreEqual(-9223372036854775808m, c.EnumConstantDeclValue, "EnumConstantDeclValue");

            t = c.EnumDeclIntegerType;
            Assert.IsNull(t, "EnumDeclIntegerType");

            Assert.AreEqual(-1, c.FieldDeclBitWidth, "FieldDeclBitWidth");
            Assert.AreEqual(null, c.IncludedFile, "IncludedFile");
            Assert.AreEqual(false, c.IsBitField, "IsBitField");
            Assert.AreEqual(false, c.IsVirtualBase, "IsVirtualBase");
            Assert.AreEqual(CursorKind.FirstInvalid, c.Kind, "Kind");
            Assert.AreEqual(LanguageKind.Invalid, c.Language, "Language");

            var cc = c.LexicalParent;

            Assert.IsNotNull(cc, "LexicalParent");

            Assert.AreEqual(LinkageKind.Invalid, c.Linkage, "Linkage");
            Assert.AreEqual(0, c.OverloadedDeclarationCount, "OverloadedDeclarationCount");
            t = c.ResultType;
            Assert.IsNull(t, "ResultType");

            cc = c.SemanticParent;
            Assert.IsNotNull(cc, "SemanticParent");

            Assert.IsNull(c.TranslationUnit, "TranslationUnit");
            t = c.TypeDefDeclUnderlyingType;
            Assert.IsNull(t, "TypeDefDeclUnderlyingType");
        }
コード例 #7
0
ファイル: ClangCursorTest.cs プロジェクト: takeshich/nclang
        public void NodeTraversalWithVisitor()
        {
            string source   = @"
bool foo()
{
    return true;
}

void bar()
{
    foo();
    for (int i = 0; i < 10; ++i)
        foo();
}

int main()
{
    bar();
    if (foo())
        bar();
}";
            string filename = "ClangCursorTest.NodeTraversalWithVisitor.c";
            var    file     = new ClangUnsavedFile(filename, source);

            using (var idx = ClangService.CreateIndex()) {
                using (var tu = idx.ParseTranslationUnit(filename, new string [0], new ClangUnsavedFile [] { file }, TranslationUnitFlags.None)) {
                    Func <ClangCursor, ClangCursor, IntPtr, ChildVisitResult> func = (cursor, parent, clientData) => {
                        if (cursor.Kind == CursorKind.CallExpression)
                        {
                            Console.Error.WriteLine("Found {0} [line:{1}, column:{2}]", cursor.DisplayName, cursor.Location.FileLocation.Line, cursor.Location.FileLocation.Column);
                        }
                        else
                        {
                            Console.Error.WriteLine("different kind {0} ({1}, {2})", cursor.Kind, cursor.Location.FileLocation.Line, cursor.Location.FileLocation.Column);
                        }
                        return(ChildVisitResult.Recurse);
                    };
                    tu.GetCursor().VisitChildren(func, IntPtr.Zero);
                }
            }
        }
コード例 #8
0
ファイル: ClangTestHelpers.cs プロジェクト: takeshich/nclang
        public static void WithTranslationUnit(Action <ClangIndex, ClangTranslationUnit> test, string testFullName, string content = null)
        {
            string filename = testFullName;

            content = content ?? @"
#include <stdio.h>

void main () {
	printf (""hello world""); 
}
";
            File.WriteAllText(filename, content);
            var idx = ClangService.CreateIndex();
            var tu  = idx.CreateTranslationUnitFromSourceFile(filename, new string [0], new ClangUnsavedFile [0]);

            try {
                test(idx, tu);
            } finally {
                File.Delete(filename);
                tu.Dispose();
                idx.Dispose();
            }
        }
コード例 #9
0
 public void NewIndex()
 {
     ClangService.CreateIndex().Dispose();
 }
コード例 #10
0
ファイル: Program.cs プロジェクト: takeshich/nclang
        void Run(string [] args)
        {
            var        idx    = ClangService.CreateIndex();
            var        tus    = new List <ClangTranslationUnit> ();
            TextWriter output = Console.Out;
            var        opts   = new CApiGeneratorOptions();

            // We are going to parse C++ sources.
            opts.ClangArgs.Add("-x");
            opts.ClangArgs.Add("c++");
            opts.ClangArgs.Add("-std=c++1y");

            foreach (var arg in args)
            {
                if (arg == "--help" || arg == "-?")
                {
                    Console.Error.WriteLine($"[USAGE] {GetType ().Assembly.GetName ().CodeBase} [options] [inputs]");
                    Console.Error.WriteLine(@"options:
	--out:[filename]	output source file name.
	--lib:[library]		library name specified on [DllImport].
	--match:[regex]		process only matching files to [regex].
	--arg:[namespace]	compiler arguments to parse the sources."        );
                    return;
                }
                else if (arg.StartsWith("--out:", StringComparison.Ordinal))
                {
                    output = File.CreateText(arg.Substring(6));
                }
                else if (arg.StartsWith("--lib:", StringComparison.Ordinal))
                {
                    opts.LibraryName = arg.Substring(6);
                }
                else if (arg.StartsWith("--arg:", StringComparison.Ordinal))
                {
                    opts.ClangArgs.Add(arg.Substring(6));
                }
                else if (arg == "--only-explicit")
                {
                    opts.OnlyExplicit = true;
                }
                else if (arg.StartsWith("--match:", StringComparison.Ordinal))
                {
                    opts.FileMatches.Add(arg.Substring(8));
                }
                else if (arg.Contains(Path.DirectorySeparatorChar) || arg.Contains(Path.AltDirectorySeparatorChar))
                {
                    foreach (var file in Directory.GetFiles(Path.GetDirectoryName(arg), Path.GetFileName(arg)))
                    {
                        opts.Sources.Add(file);
                    }
                }
                else
                {
                    opts.Sources.Add(arg);
                }
            }


            foreach (var source in opts.Sources)
            {
                if (!File.Exists(source))
                {
                    throw new ArgumentException("File not found: " + source);
                }
                tus.Add(idx.ParseTranslationUnit(source, opts.ClangArgs.ToArray(), null, TranslationUnitFlags.None));
            }

            var members = new HeaderParser().Run(opts, tus);

            new CCodeWriter().Write(output, members, opts);
            output.Close();
        }
コード例 #11
0
        void Run(string [] args)
        {
            var          idx                = ClangService.CreateIndex();
            var          tus                = new List <ClangTranslationUnit> ();
            TextWriter   output             = Console.Out;
            List <Regex> fileMatches        = new List <Regex> ();
            bool         onlyExplicit       = false;
            bool         skipStandardCTypes = false;
            string       type_scope         = "internal";

            Args.Add("-x");
            Args.Add("c++");
            Args.Add("--std=c++1y");
            foreach (var arg in args)
            {
                if (arg == "--help" || arg == "-?")
                {
                    Console.Error.WriteLine($"[USAGE] {GetType ().Assembly.GetName ().CodeBase} [options] [inputs]");
                    Console.Error.WriteLine(@"options:
	--out:[filename]	output source file name.
	--lib:[library]		library name specified on [DllImport].
	--ns:[namespace]	namespace name that wraps the entire code.
	--match:[regex]		when specified, process only matching files.
	--arg:[namespace]	compiler arguments to parse the sources."        );
                    return;
                }
                else if (arg.StartsWith("--out:", StringComparison.Ordinal))
                {
                    output = File.CreateText(arg.Substring(6));
                }
                else if (arg.StartsWith("--lib:", StringComparison.Ordinal))
                {
                    LibraryName = arg.Substring(6);
                }
                else if (arg.StartsWith("--ns:", StringComparison.Ordinal))
                {
                    Namespace = arg.Substring(5);
                }
                else if (arg.StartsWith("--arg:", StringComparison.Ordinal))
                {
                    Args.Add(arg.Substring(6));
                }
                else if (arg.StartsWith("--match:", StringComparison.Ordinal))
                {
                    fileMatches.Add(new Regex(arg.Substring(8)));
                }
                else if (arg == "--only-explicit")
                {
                    onlyExplicit = true;
                }
                else if (arg == "--skip-standard-c-types")
                {
                    skipStandardCTypes = true;
                }
                else if (arg == "--expose-types")
                {
                    type_scope = "public";
                }
                else if (arg == "--prefer-string-over-byte-array")
                {
                    PreferStringOverByteArray = true;
                }
                else
                {
                    sources.Add(arg);
                }
            }
            foreach (var source in sources)
            {
                ClangTranslationUnit tu;
                var err = idx.ParseTranslationUnit(source, Args.ToArray(), null, TranslationUnitFlags.SkipFunctionBodies, out tu);
                if (err == ErrorCode.Success)
                {
                    tus.Add(tu);
                }
            }

            var    members = new List <Named> ();
            Struct current = null;
            string current_typedef_name = null;
            int    anonymous_type_count = 0;

            Action <ClangCursor> removeDuplicates = c => {
                var dups = members.Where(m => m.Name == c.Spelling || m.Line == c.Location.FileLocation.Line && m.Column == c.Location.FileLocation.Column && m.SourceFile == c.Location.FileLocation.File.FileName).ToArray();
                foreach (var d in dups)
                {
                    members.Remove(d);
                }
            };

            foreach (var tu in tus)
            {
                for (int i = 0; i < tu.DiagnosticCount; i++)
                {
                    Console.Error.WriteLine($"[diag] {tu.GetDiagnostic (i).Location}: {tu.GetDiagnostic (i).Spelling}");
                }

                Func <ClangCursor, ClangCursor, IntPtr, ChildVisitResult> func = null;
                func = (cursor, parent, clientData) => {
                    // skip ignored file.
                    if (onlyExplicit && !sources.Contains(cursor.Location.FileLocation.File.FileName))
                    {
                        return(ChildVisitResult.Continue);
                    }
                    if (fileMatches.Any() && !fileMatches.Any(fm => fm.IsMatch(cursor.Location.FileLocation.File.FileName)))
                    {
                        return(ChildVisitResult.Continue);
                    }

                    // FIXME: this doesn't work.
                    if (cursor.Kind == CursorKind.InclusionDirective)
                    {
                        Console.Error.WriteLine("[diag] Include File " + cursor.IncludedFile);
                        idx.ParseTranslationUnit(cursor.IncludedFile.FileName, null, null, TranslationUnitFlags.None).GetCursor().VisitChildren(func, IntPtr.Zero);
                    }
                    else if (cursor.Kind == CursorKind.TypedefDeclaration)
                    {
                        if (cursor.Location.FileLocation.File != null)
                        {
                            var alias = ToTypeName(cursor.CursorType);
                            InsideUsingDeclaration = true;
                            if (usings.All(u => u.Alias != alias))
                            {
                                var actual = ToTypeName(cursor.TypeDefDeclUnderlyingType);
                                var td     = new TypeDef {
                                    Alias     = alias,
                                    ActualInC = cursor.TypeDefDeclUnderlyingType.Spelling,
                                    Managed   = cursor.TypeDefDeclUnderlyingType.ArraySize < 0 ? actual : $"System.IntPtr/*{actual}[]*/",
                                    Location  = cursor.Location,
                                };
                                usings.Add(td);
                            }
                            InsideUsingDeclaration = false;

                            current_typedef_name = alias;
                            foreach (var child in cursor.GetChildren())
                            {
                                func(child, cursor, clientData);
                            }
                            current_typedef_name = null;

                            return(ChildVisitResult.Continue);
                        }
                    }
                    else if (cursor.Kind == CursorKind.EnumConstantDeclaration)
                    {
                        removeDuplicates(cursor);
                        current.Fields.Add(new Variable()
                        {
                            Type = ToTypeName(cursor.CursorType),
                            Name = cursor.Spelling,
                            // FIXME: this is HACK.
                            Value = (cursor.EnumConstantDeclValue != 0 ? (decimal)cursor.EnumConstantDeclValue : (decimal)cursor.EnumConstantDeclUnsignedValue).ToString(),
                        });
                        return(ChildVisitResult.Continue);
                    }
                    else if (cursor.Kind == CursorKind.FieldDeclaration)
                    {
                        removeDuplicates(cursor);
                        var typeCursor = cursor.CursorType.TypeDeclaration;
                        var type       = !string.IsNullOrEmpty(typeCursor.DisplayName) ?
                                         ToTypeName(cursor.CursorType) :
                                         members.FirstOrDefault(m => m.Line == typeCursor.Location.FileLocation.Line && m.Column == typeCursor.Location.FileLocation.Column && m.SourceFile == typeCursor.Location.FileLocation.File.FileName)?.Name ??
                                         ToTypeName(cursor.CursorType);
                        var variable = new Variable()
                        {
                            Type        = type,
                            TypeDetails = GetTypeDetails(cursor.CursorType),
                            ArraySize   = cursor.CursorType.ArraySize,
                            SizeOf      = cursor.CursorType.SizeOf,
                            Name        = cursor.Spelling
                        };
                        if (current == null)
                        {
                            Console.Error.WriteLine($"[warn] {cursor.Spelling}: globally declared fields are ignored");
                        }
                        else
                        {
                            current.Fields.Add(variable);
                        }
                        return(ChildVisitResult.Continue);
                    }
                    else if (cursor.Kind == CursorKind.StructDeclaration || cursor.Kind == CursorKind.UnionDeclaration || cursor.Kind == CursorKind.EnumDeclaration)
                    {
                        removeDuplicates(cursor);
                        var parentType = current;
                        current = new Struct()
                        {
                            Name = current_typedef_name != null && parentType == null ? current_typedef_name :
                                   string.IsNullOrEmpty(cursor.DisplayName) ? "anonymous_type_" + (anonymous_type_count++) : cursor.DisplayName,
                            Location = cursor.Location,
                            IsUnion  = cursor.Kind == CursorKind.UnionDeclaration,
                            IsEnum   = cursor.Kind == CursorKind.EnumDeclaration,
                        };
                        members.Add(current);
                        foreach (var child in cursor.GetChildren())
                        {
                            func(child, cursor, clientData);
                        }
                        current = parentType;
                        return(ChildVisitResult.Continue);
                    }
                    else if (cursor.Kind == CursorKind.FunctionDeclaration)
                    {
                        removeDuplicates(cursor);
                        var fargs = Enumerable.Range(0, cursor.ArgumentCount)
                                    .Select(i => cursor.GetArgument(i));

                        // function with va_list isn't supported in P/Invoke.
                        if (fargs.Any(a => a.CursorType.Spelling == "va_list"))
                        {
                            Console.Error.WriteLine($"Cannot bind {cursor.DisplayName} because it contains va_list.");
                            return(ChildVisitResult.Continue);
                        }

                        members.Add(new Function()
                        {
                            Name     = cursor.Spelling,
                            Return   = ToTypeName(cursor.ResultType),
                            Location = cursor.Location,
                            Args     = fargs.Select(a => new Variable()
                            {
                                Name        = a.Spelling,
                                Type        = ToTypeName(a.CursorType),
                                TypeDetails = GetTypeDetails(a.CursorType)
                            })
                                       .ToArray()
                        });
                        return(ChildVisitResult.Continue);
                    }
                    return(ChildVisitResult.Recurse);
                };
                tu.GetCursor().VisitChildren(func, IntPtr.Zero);
            }

            output.WriteLine("// This source file is generated by nclang PInvokeGenerator.");
            output.WriteLine("using System;");
            output.WriteLine("using System.Runtime.InteropServices;");
            if (!skipStandardCTypes)
            {
                output.WriteLine("using time_t = System.IntPtr;");
                output.WriteLine("using size_t = System.IntPtr;");
            }

            foreach (var u in usings.Distinct(new KeyComparer()))
            {
                if (u.Managed == "System.IntPtr" || u.Managed.StartsWith(Namespace + ".Pointer<"))
                {
                    // FIXME: maybe it's hacky, but do no further existence checks.
                    output.WriteLine($"using {u.Alias} = {u.Managed};");
                    continue;
                }

                // FIXME: hacky matching
                var mbr = members.FirstOrDefault(m => u.Managed.EndsWith(m.Name, StringComparison.Ordinal));
                if (mbr != null)
                {
                    mbr.Name = u.Alias;
                }
                else
                {
                    // FIXME: this does not seem to work
                    var del = delegates.FirstOrDefault(d => u.Managed.EndsWith(d.TypeNameForDeclaration, StringComparison.Ordinal));
                    if (del != null)
                    {
                        del.TypeNameForDeclaration = u.Alias;
                        del.TypeNameForReference   = "Delegates." + u.Alias;
                        output.WriteLine("using {0} = {1}; // {2} ({3},{4})", u.Managed.Substring(Namespace.Length + 1), WithNamespace(del.TypeNameForReference), u.SourceFileName, u.Line, u.Column);
                    }
                    else
                    {
                        Console.Error.WriteLine($"[warn] typedef \"{u.Alias}\" is being missed. It should be mapped to \"{u.ActualInC}\", but \"{u.Managed}\" did not match anything. (Declared at {u.SourceFile}({u.Line}, {u.Column}).)");
                    }
                }
            }
            // some code references function pointer types without "Delegates.", so workaround that with hacky "Delegates." addition.
            foreach (var del in delegates)
            {
                output.WriteLine("using {0} = {1};", del.TypeNameForDeclaration, WithNamespace(del.TypeNameForReference));
            }
            output.WriteLine();

            if (Namespace != null)
            {
                output.WriteLine("namespace {0} {{", Namespace);
            }

            foreach (var o in members.OfType <Struct> ())
            {
                o.Write(output, type_scope);
                output.WriteLine();
            }

            output.WriteLine($"{type_scope} partial class Natives");
            output.WriteLine("{");
            output.WriteLine("\tconst string LibraryName = \"{0}\";", LibraryName);

            foreach (var o in members.OfType <Function> ())
            {
                o.Write(output, type_scope);
                output.WriteLine();
            }
            output.WriteLine("}");
            output.WriteLine();

            if (delegates.Any())
            {
                output.WriteLine($"{type_scope} class Delegates");
                output.WriteLine("{");
                foreach (var s in delegates)
                {
                    output.WriteLine("public " + s.DelegateDefinition + ";");
                }
                output.WriteLine("}");
            }

            output.WriteLine($@"
{type_scope} struct Pointer<T>
{{
	public IntPtr Handle;
	public static implicit operator IntPtr (Pointer<T> value) {{ return value.Handle; }}
	public static implicit operator Pointer<T> (IntPtr value) {{ return new Pointer<T> (value); }}

	public Pointer (IntPtr handle)
	{{
		Handle = handle;
	}}

	public override bool Equals (object obj)
	{{
		return obj is Pointer<T> && this == (Pointer<T>) obj;
	}}

	public override int GetHashCode ()
	{{
		return (int) Handle;
	}}

	public static bool operator == (Pointer<T> p1, Pointer<T> p2)
	{{
		return p1.Handle == p2.Handle;
	}}

	public static bool operator != (Pointer<T> p1, Pointer<T> p2)
	{{
		return p1.Handle != p2.Handle;
	}}
}}
{type_scope} struct ArrayOf<T> {{}}
{type_scope} struct ConstArrayOf<T> {{}}
{type_scope} class CTypeDetailsAttribute : Attribute
{{
	public CTypeDetailsAttribute (string value)
	{{
		Value = value;
	}}

	public string Value {{ get; set; }}
}}
");

            if (Namespace != null)
            {
                output.WriteLine("}");
            }

            output.Close();
        }
コード例 #12
0
        void Run(string[] args)
        {
            var        idx    = ClangService.CreateIndex();
            var        tus    = new List <ClangTranslationUnit> ();
            TextWriter output = Console.Out;

            foreach (var arg in args)
            {
                if (arg.StartsWith("--out:", StringComparison.Ordinal))
                {
                    output = File.CreateText(arg.Substring(6));
                }
                else if (arg.StartsWith("--lib:", StringComparison.Ordinal))
                {
                    LibraryName = arg.Substring(6);
                }
                else if (arg.StartsWith("--ns:", StringComparison.Ordinal))
                {
                    Namespace = arg.Substring(5);
                }
                else
                {
                    tus.Add(idx.ParseTranslationUnit(arg, null, null, TranslationUnitFlags.None));
                }
            }

            var    members = new List <Locatable> ();
            Struct current = null;

            foreach (var tu in tus)
            {
                Func <ClangCursor, ClangCursor, IntPtr, ChildVisitResult> func = null;
                func = (cursor, parent, clientData) => {
                    // FIXME: this doesn't work.
                    if (cursor.Kind == CursorKind.InclusionDirective)
                    {
                        Console.Error.WriteLine("Include File " + cursor.IncludedFile);
                        idx.ParseTranslationUnit(cursor.IncludedFile.FileName, null, null, TranslationUnitFlags.None).GetCursor().VisitChildren(func, IntPtr.Zero);
                    }
                    if (cursor.Kind == CursorKind.TypedefDeclaration)
                    {
                        if (cursor.Location.FileLocation.File != null)
                        {
                            var alias = ToTypeName(cursor.CursorType);
                            InsideUsingDeclaration = true;
                            if (usings.All(u => u.Alias != alias))
                            {
                                var actual = ToTypeName(cursor.TypeDefDeclUnderlyingType);
                                usings.Add(new TypeDef()
                                {
                                    Alias = alias, Actual = actual
                                });
                            }
                            InsideUsingDeclaration = false;
                        }
                    }
                    if (cursor.Kind == CursorKind.EnumConstantDeclaration)
                    {
                        current.Fields.Add(new Variable()
                        {
                            Type = ToTypeName(cursor.CursorType),
                            Name = cursor.Spelling,
                            // FIXME: this is HACK.
                            Value = (cursor.EnumConstantDeclValue != 0 ? (decimal)cursor.EnumConstantDeclValue : (decimal)cursor.EnumConstantDeclUnsignedValue).ToString(),
                        });
                    }
                    if (cursor.Kind == CursorKind.FieldDeclaration)
                    {
                        current.Fields.Add(new Variable()
                        {
                            Type = ToTypeName(cursor.CursorType),
                            Name = cursor.Spelling
                        });
                    }
                    if (cursor.Kind == CursorKind.StructDeclaration || cursor.Kind == CursorKind.UnionDeclaration || cursor.Kind == CursorKind.EnumDeclaration)
                    {
                        current = new Struct()
                        {
                            Name       = cursor.DisplayName,
                            SourceFile = cursor.Location.FileLocation.File.FileName,
                            Line       = cursor.Location.FileLocation.Line,
                            Column     = cursor.Location.FileLocation.Column,
                            IsUnion    = cursor.Kind == CursorKind.UnionDeclaration,
                            IsEnum     = cursor.Kind == CursorKind.EnumDeclaration,
                        };
                        if (members.All(m => m.Line != current.Line || m.Column != current.Column))
                        {
                            var dup = members.OfType <Struct> ().Where(m => m.Name == current.Name).ToArray();
                            foreach (var d in dup)
                            {
                                members.Remove(d);
                            }
                            members.Add(current);
                        }
                    }
                    if (cursor.Kind == CursorKind.FunctionDeclaration)
                    {
                        members.Add(new Function()
                        {
                            Name       = cursor.Spelling,
                            Return     = ToTypeName(cursor.ResultType),
                            SourceFile = cursor.Location.FileLocation.File.FileName,
                            Line       = cursor.Location.FileLocation.Line,
                            Column     = cursor.Location.FileLocation.Column,
                            Args       = Enumerable.Range(0, cursor.ArgumentCount).Select(i => cursor.GetArgument(i)).Select(a => new Variable()
                            {
                                Name = a.Spelling, Type = ToTypeName(a.CursorType)
                            }).ToArray()
                        });
                    }
                    return(ChildVisitResult.Recurse);
                };
                tu.GetCursor().VisitChildren(func, IntPtr.Zero);
            }

            output.WriteLine("// This source file is generated by nclang PInvokeGenerator.");
            output.WriteLine("using System;");
            output.WriteLine("using System.Runtime.InteropServices;");
            foreach (var u in usings.Distinct(new KeyComparer()))
            {
                if (u.Alias != u.Actual)
                {
                    output.WriteLine("using {0} = {1};", u.Alias, u.Actual);
                }
            }
            output.WriteLine();

            if (Namespace != null)
            {
                output.WriteLine("namespace {0} {{", Namespace);
            }

            foreach (var o in members.OfType <Struct> ())
            {
                o.Write(output);
                output.WriteLine();
            }

            output.WriteLine("class Natives");
            output.WriteLine("{");
            foreach (var o in members.OfType <Function> ())
            {
                o.Write(output);
                output.WriteLine();
            }
            output.WriteLine("}");
            output.WriteLine();

            output.WriteLine(@"
public struct Pointer<T>
{
	public IntPtr Handle;
	public static implicit operator IntPtr (Pointer<T> value) { return value.Handle; }
	public static implicit operator Pointer<T> (IntPtr value) { return new Pointer<T> (value); }

	public Pointer (IntPtr handle)
	{
		Handle = handle;
	}
}
public struct ArrayOf<T> {}
public struct ConstArrayOf<T> {}
");

            if (Namespace != null)
            {
                output.WriteLine("}");
            }

            output.Close();
        }