-
-
Notifications
You must be signed in to change notification settings - Fork 457
Expand file tree
/
Copy pathClangScraper.cs
More file actions
183 lines (155 loc) · 6.57 KB
/
ClangScraper.cs
File metadata and controls
183 lines (155 loc) · 6.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using ClangSharp;
using ClangSharp.Interop;
using Silk.NET.SilkTouch.Symbols;
namespace Silk.NET.SilkTouch.Scraper;
/// <summary>
/// Primary entrypoint of the Scraper. This class defines logic to extract <see cref="Symbol"/>s using ClangSharp
/// </summary>
public sealed class ClangScraper
{
/// <summary>
/// Placeholder used in place of library paths
/// </summary>
public static readonly string LibraryPathPlaceholder = "LIBRARY_PATH";
/// <summary>
/// Placeholder used in place of library paths
/// </summary>
public static readonly string LibraryNamespacePlaceholder = "LIBRARY_NAMESPACE";
/// <summary>
/// Scrapes the given XML document for symbols
/// </summary>
/// <param name="document">A XML Document, the format is assumed to be similar to what ClangSharp would output.</param>
/// <returns>Any number of symbols scraped from the given xml</returns>
public IEnumerable<Symbol> ScrapeXML(XmlDocument document)
{
var bindings = document.ChildNodes.Cast<XmlNode>().OfType<XmlElement>().FirstOrDefault();
if (bindings is null)
{
return Enumerable.Empty<Symbol>();
}
var visitor = new XmlVisitor();
return visitor.Visit(bindings);
}
/// <summary>
/// Calls into Clang to generate XML used to scrape symbols.
/// </summary>
/// <param name="headerFile">The root header file to scrape</param>
/// <param name="includedNames">Names to traverse</param>
/// <param name="excludedNames">Names to exclude</param>
/// <param name="includeDirectories">Directories to include other files from</param>
/// <param name="definedMacros">Macros to define during scraping. Do not include platform specific macros.</param>
/// <returns>The XMLDocument containing nodes representing code in the given header file. Null if generation failed for some reason.</returns>
/// <exception cref="InvalidOperationException">Will be thrown when errors during parsing are encountered</exception>
public XmlDocument? GenerateXML(string headerFile, string[] includedNames, string[] excludedNames, string[] includeDirectories, string[] definedMacros)
{
var opts = PInvokeGeneratorConfigurationOptions.None;
opts |= PInvokeGeneratorConfigurationOptions.NoDefaultRemappings;
var config = new PInvokeGeneratorConfiguration
(
LibraryNamespacePlaceholder, LibraryPathPlaceholder, null, // TODO: "License Header File"?
PInvokeGeneratorOutputMode.Xml, opts
);
var files = new Dictionary<string, MemoryStream>();
Stream OutputStreamFactory(string fileName)
{
if (files.ContainsKey(fileName))
{
throw new InvalidOperationException();
}
return files[fileName] = new MemoryStream();
}
var commandLineArgs = new string[3 + definedMacros.Length + includeDirectories.Length];
commandLineArgs[0] = "--language=c++";
commandLineArgs[1] = "--std=c++17";
commandLineArgs[2] = "-Wno-pragma-once-outside-header";
for (int i = 0; i < definedMacros.Length; i++)
{
commandLineArgs[3 + i] = "--define-macro=" + definedMacros[i];
}
for (int i = 0; i < includeDirectories.Length; i++)
{
commandLineArgs[3 + definedMacros.Length] = "--include-directory=" + definedMacros[i];
}
var translationFlags = CXTranslationUnit_Flags.CXTranslationUnit_None;
// ReSharper disable BitwiseOperatorOnEnumWithoutFlags
translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_IncludeAttributedTypes;
translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_VisitImplicitAttributes;
translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_DetailedPreprocessingRecord;
translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_KeepGoing;
// ReSharper restore BitwiseOperatorOnEnumWithoutFlags
try
{
using (var pinvokeGenerator = new PInvokeGenerator(config, OutputStreamFactory))
GenerateBindings(pinvokeGenerator, headerFile, commandLineArgs, translationFlags);
foreach (var (name, stream) in files)
{
Console.WriteLine(name);
var doc = new XmlDocument();
stream.Position = 0;
doc.Load(stream);
return doc;
}
}
finally
{
foreach (var (_, stream) in files)
{
stream.Dispose();
}
}
return null;
}
private static void GenerateBindings
(
PInvokeGenerator pinvokeGenerator,
string headerFile,
string[] commandLineArgs,
CXTranslationUnit_Flags translationFlags
)
{
var result = CXTranslationUnit.TryParse
(
pinvokeGenerator.IndexHandle, headerFile, commandLineArgs, Array.Empty<CXUnsavedFile>(), translationFlags,
out var handle
);
try
{
if (result != CXErrorCode.CXError_Success)
{
if (handle.NumDiagnostics > 0)
{
for (uint i = 0; i < handle.NumDiagnostics; i++)
{
var x = handle.GetDiagnostic(i);
if (x.Severity > CXDiagnosticSeverity.CXDiagnostic_Warning)
{
throw new Exception($"Diagnostic raised while parsing c: {x.Category} \"{x.Format(0)}\"");
}
}
}
throw new Exception($"Could not parse translational unit. {Enum.GetName(result)}");
}
using var translationUnit = TranslationUnit.GetOrCreate(handle);
pinvokeGenerator.GenerateBindings(translationUnit, headerFile, commandLineArgs, translationFlags);
foreach (var diagnostic in pinvokeGenerator.Diagnostics)
{
if (diagnostic.Level > DiagnosticLevel.Warning)
{
Console.WriteLine(diagnostic.Message);
}
}
}
finally
{
// handle.Dispose();
}
}
}