Skip to content

Commit 73ebf5a

Browse files
authored
[tools] Add sharpie. Fixes #23962. (#24622)
Open-source the Objective-Sharpie tool. * Port the old closed-source version of Objective-Sharpie to modern .NET, and have it use ClangSharp for its clang integration. * This is a simplified port, any features that were not frequently used have been dropped. In particular, only two features remain: * Binding an entire OS SDK (which we use for our own bindings). * Binding a .framework (to bind third-party bindings). * The new sharpie will be distributed as a .NET command-line tool. Fixes #23962. Fixes #21573.
1 parent a72eb38 commit 73ebf5a

File tree

624 files changed

+351474
-437
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

624 files changed

+351474
-437
lines changed

Directory.Build.props

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@
1717
<SystemDrawingCommonPackageVersion>4.7.2</SystemDrawingCommonPackageVersion>
1818
<!-- Fix transient dependency issue found by component governance 4.7.0 -> 4.7.1 brought by Microsoft.Build.Tasks.Core package -->
1919
<SystemSecurityCryptographyXmlPackageVersion>8.0.0</SystemSecurityCryptographyXmlPackageVersion>
20+
21+
<ClangSharpPackageVersion>21.1.8.2</ClangSharpPackageVersion>
22+
<ICSharpCodeNRefactoryPackageVersion>5.5.1</ICSharpCodeNRefactoryPackageVersion>
23+
<libclangPackageVersion>21.1.8</libclangPackageVersion>
24+
<MicrosoftNETTestSdkPackageVersion>17.14.0</MicrosoftNETTestSdkPackageVersion>
25+
<MonoCecilPackageVersion>0.11.6</MonoCecilPackageVersion>
26+
<NUnit3TestAdapterPackageVersion>5.0.0</NUnit3TestAdapterPackageVersion>
27+
<NUnitAnalyzersPackageVersion>4.7.0</NUnitAnalyzersPackageVersion>
28+
<NUnitPackageVersion>4.3.2</NUnitPackageVersion>
2029
</PropertyGroup>
2130
<Import Project="Build.props" Condition="Exists('Build.props')" />
2231
</Project>

NOTICE.txt

Lines changed: 290 additions & 0 deletions
Large diffs are not rendered by default.

dotnet/Workloads/SignList.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@
130130
<!-- Sign dylibs -->
131131
<MacDeveloperSign Include="MonoBundle\*.dylib" />
132132
<MacDeveloperSign Include="native\*.dylib" />
133+
<MacDeveloperSign Include="libclang.dylib" />
134+
<MacDeveloperSign Include="libClangSharp.dylib" />
133135
</ItemGroup>
134136

135137
<Import Project="$(MSBuildThisFileDirectory)SignList.targets" />

tests/common/Configuration.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ public static void FixupTestFiles (string directory, string mode)
683683

684684
public static Dictionary<string, string> GetBuildEnvironment (ApplePlatform platform)
685685
{
686-
Dictionary<string, string> environment = new Dictionary<string, string> ();
686+
var environment = new Dictionary<string, string> ();
687687
SetBuildVariables (platform, ref environment);
688688
return environment;
689689
}
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
namespace Sharpie.Bind.Tests;
5+
6+
using System.Diagnostics;
7+
using System.Drawing;
8+
using ICSharpCode.NRefactory.CSharp;
9+
using Sharpie.Bind;
10+
using Xamarin.Tests;
11+
using Xamarin.Utils;
12+
13+
public static class Extensions {
14+
15+
static Dictionary<ApplePlatform, string> platformAssemblyPaths = new ();
16+
public static string GetPlatformAssemblyPath (ApplePlatform platform)
17+
{
18+
if (!platformAssemblyPaths.TryGetValue (platform, out var path))
19+
platformAssemblyPaths [platform] = path = Configuration.GetBaseLibrary (platform);
20+
return path;
21+
}
22+
23+
public static string GetClangResourceDirectory ()
24+
{
25+
return Path.Combine (Configuration.SourceRoot, "tools", "sharpie", "clang");
26+
}
27+
28+
// Running out-of-process is useful when there's something causing clang to crash, in which case
29+
// running out-of-process will only fail the single test that's running, and not the entire test run.
30+
public static bool UseOutOfProcessBinding = true;
31+
32+
public static BindingResult BindInOrOut (this ObjectiveCBinder binder)
33+
{
34+
BindingResult rv;
35+
if (UseOutOfProcessBinding && !Debugger.IsAttached) {
36+
rv = binder.ExecuteOutOfProcess ();
37+
} else {
38+
rv = binder.Bind ();
39+
}
40+
41+
return rv;
42+
}
43+
44+
/// <summary>
45+
/// Asserts that the binding was successful and that the generated binding code matches the expected binding code.
46+
/// </summary>
47+
/// <param name="bindings"></param>
48+
/// <param name="expectedBindingCode"></param>
49+
/// <param name="additionalInfo"></param>
50+
public static void AssertSuccess (this BindingResult bindings, string? expectedBindingCode, params string [] additionalInfo)
51+
{
52+
bindings.Warnings.ForEach (Console.WriteLine);
53+
bindings.Errors.ForEach (Console.Error.WriteLine);
54+
55+
if (bindings.ExitCode == 139) {
56+
Console.Error.WriteLine ("Binding process crashed with exit code 139 (SIGSEGV).");
57+
Assert.Fail ("Binding process crashed with exit code 139 (SIGSEGV).");
58+
}
59+
60+
Assert.That (bindings.ExitCode, Is.EqualTo (0), "Expected success");
61+
Assert.That (bindings.Errors.Count, Is.EqualTo (0), $"Unexpected number of errors\n\t{string.Join ("\n\t", bindings.Errors)}");
62+
63+
if (expectedBindingCode is null)
64+
return;
65+
66+
var expected = expectedBindingCode.Replace ("\r\n", "\n").Trim ();
67+
var actual = bindings.BindingCode?.Replace ("\r\n", "\n").Trim () ?? "";
68+
if (expected != actual) {
69+
const string splitter = "===========================";
70+
Console.WriteLine ("Expected Binding Code:");
71+
Console.WriteLine (splitter);
72+
Console.WriteLine (expected);
73+
Console.WriteLine (splitter);
74+
Console.WriteLine ("Actual Binding Code:");
75+
Console.WriteLine (splitter);
76+
Console.WriteLine (actual);
77+
Console.WriteLine (splitter);
78+
var diff = ComputeDiff (expected, actual).Split ('\n');
79+
Console.WriteLine ("Diff:");
80+
Console.WriteLine (splitter);
81+
foreach (var line in diff) {
82+
var colorized = false;
83+
if (line.Length >= 2) {
84+
if (line [0] == '+' && line [1] != '+') {
85+
Console.Write (Color.Green);
86+
colorized = true;
87+
} else if (line [0] == '-' && line [1] != '-') {
88+
Console.Write (Color.Red);
89+
colorized = true;
90+
}
91+
}
92+
Console.Write (line);
93+
if (colorized)
94+
Console.Write (Color.Reset);
95+
Console.WriteLine ();
96+
}
97+
Console.WriteLine (splitter);
98+
foreach (var line in additionalInfo)
99+
Console.WriteLine (line);
100+
Assert.That (actual, Is.EqualTo (expected), "Expected binding code");
101+
}
102+
}
103+
104+
static string ComputeDiff (string a, string b)
105+
{
106+
var fa = Path.Combine (Path.GetTempPath (), "sharpie", "tests", "a.cs");
107+
var fb = Path.Combine (Path.GetTempPath (), "sharpie", "tests", "b.cs");
108+
if (File.Exists (fa))
109+
File.Delete (fa);
110+
if (File.Exists (fb))
111+
File.Delete (fb);
112+
Directory.CreateDirectory (Path.GetDirectoryName (fa)!);
113+
File.WriteAllText (fa, a);
114+
File.WriteAllText (fb, b);
115+
116+
var rv = Execution.RunAsync ("diff", new [] { "-u", fa, fb }, timeout: TimeSpan.FromSeconds (10)).Result;
117+
118+
File.Delete (fa);
119+
File.Delete (fb);
120+
121+
return rv.Output.MergedOutput;
122+
}
123+
124+
public static void AssertErrors (this BindingResult bindings, params (int Code, string Message, string? FileName, long? LineNumber) [] expectedErrors)
125+
{
126+
Assert.That (bindings.ExitCode, Is.EqualTo (1), "Expected failure");
127+
Assert.That (bindings.BindingCode, Is.Empty.Or.Null, "Expected no binding code");
128+
AssertMessages ("error", bindings.Errors, expectedErrors);
129+
}
130+
131+
public static void AssertWarnings (this BindingResult bindings, params (int Code, string Message, string? FileName, long? LineNumber) [] expectedWarnings)
132+
{
133+
AssertMessages ("warning", bindings.Warnings, expectedWarnings);
134+
}
135+
136+
public static void AssertNoWarnings (this BindingResult bindings)
137+
{
138+
AssertWarnings (bindings);
139+
}
140+
141+
static void AssertMessages (string type, IList<BindingMessage> messages, params (int Code, string Message, string? FileName, long? LineNumber) [] expectedMessages)
142+
{
143+
Assert.That (messages.Count, Is.EqualTo (expectedMessages.Length), $"Unexpected number of {type}s ({messages.Count})\n\t{string.Join ("\n\t", messages)}");
144+
for (int i = 0; i < expectedMessages.Length; i++) {
145+
Assert.That (messages [i].Code, Is.EqualTo (expectedMessages [i].Code), $"Code for {type} #{i}");
146+
Assert.That (messages [i].Message, Is.EqualTo (expectedMessages [i].Message), $"Message for {type} #{i}");
147+
if (expectedMessages [i].FileName is not null)
148+
Assert.That (messages [i].FileName, Is.EqualTo (expectedMessages [i].FileName), $"FileName for {type} #{i}");
149+
if (expectedMessages [i].LineNumber is not null)
150+
Assert.That (messages [i].LineNumber, Is.EqualTo (expectedMessages [i].LineNumber!.Value), $"LineNumber for {type} #{i}");
151+
}
152+
}
153+
}
154+
155+
static class Color {
156+
internal const char ESC_Char = '\u001b';
157+
internal const string ESC = $"\u001b"; /* \033 - https://github.com/dotnet/csharplang/issues/7400 */
158+
internal const string ST = ESC + "\\";
159+
internal const string OSC = ESC + "]";
160+
161+
public const string DarkBlack = ESC + "[30m";
162+
public const string DarkRed = ESC + "[31m";
163+
public const string DarkGreen = ESC + "[32m";
164+
public const string DarkYellow = ESC + "[33m";
165+
public const string DarkBlue = ESC + "[34m";
166+
public const string DarkMagenta = ESC + "[35m";
167+
public const string DarkCyan = ESC + "[36m";
168+
public const string DarkWhite = ESC + "[37m";
169+
170+
public const string Black = ESC + "[90m";
171+
public const string Red = ESC + "[91m";
172+
public const string Green = ESC + "[92m";
173+
public const string Yellow = ESC + "[93m";
174+
public const string Blue = ESC + "[94m";
175+
public const string Magenta = ESC + "[95m";
176+
public const string Cyan = ESC + "[96m";
177+
public const string White = ESC + "[97m";
178+
public const string Reset = ESC + "[0m";
179+
180+
public const string Bold = ESC + "[1m";
181+
public const string Italicize = ESC + "[3m";
182+
public const string Underline = ESC + "[4m";
183+
public const string Strikethrough = ESC + "[9m";
184+
185+
public const string NotBold = ESC + "[22m";
186+
public const string NotItalic = ESC + "[23m";
187+
public const string NotUnderline = ESC + "[24m";
188+
public const string NotStrikethrough = ESC + "[29m";
189+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
global using System.Runtime.InteropServices;

0 commit comments

Comments
 (0)