Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e4ae9e7
Add clang headers.
rolfbjarne Dec 29, 2025
87c5654
[tools] Add sharpie. Fixes #23962.
rolfbjarne Dec 29, 2025
c2d5fcb
Auto-format source code
Feb 4, 2026
3e86c9b
Update tools/sharpie/Sharpie.Bind/Massagers/NamespaceMassager.cs
rolfbjarne Feb 4, 2026
a1d4caa
No sharpie on linux.
rolfbjarne Feb 4, 2026
98c7297
Add better targets to makefile
rolfbjarne Feb 4, 2026
576edf1
Don't need a separate release build.
rolfbjarne Feb 5, 2026
6bead46
Remove NIEx
rolfbjarne Feb 5, 2026
53a1519
mkdir
rolfbjarne Feb 5, 2026
364ca9f
Try restricting sharpie+xtro to arm64.
rolfbjarne Feb 6, 2026
1ae3734
Everything on arm64.
rolfbjarne Feb 6, 2026
b46db4c
NIEX are no longer NI
rolfbjarne Feb 6, 2026
0cc3036
Enable localization for sharpie.
rolfbjarne Feb 6, 2026
cc245aa
Try to debug localization.
rolfbjarne Feb 6, 2026
a5b9bae
syntax
rolfbjarne Feb 6, 2026
a290d85
this
rolfbjarne Feb 6, 2026
0d9c4f8
simplify
rolfbjarne Feb 6, 2026
fb8ece1
formatting
rolfbjarne Feb 6, 2026
6aa8079
localization works
rolfbjarne Feb 6, 2026
7efe945
Sign new dylibs.
rolfbjarne Feb 6, 2026
7e7c0d3
translation works now
rolfbjarne Feb 6, 2026
16b7a59
format
rolfbjarne Feb 6, 2026
829d4a4
Allow sharpie to execute with newer .NET versions.
rolfbjarne Feb 9, 2026
6f36ce9
Not for now
rolfbjarne Feb 9, 2026
de2cddf
Add README.md, LICENSE and NOTICE.md
rolfbjarne Feb 9, 2026
a0abb39
notice
rolfbjarne Feb 9, 2026
6003cad
Remaining tests
rolfbjarne Feb 9, 2026
a8e6506
copilot
rolfbjarne Feb 11, 2026
896cd03
Merge remote-tracking branch 'origin/main' into dev/rolf/sharpie
rolfbjarne Feb 11, 2026
ec25a80
Auto-format source code
Feb 11, 2026
f38cb93
Cleanup TODO.
rolfbjarne Feb 11, 2026
09205e8
Merge branch 'main' into dev/rolf/sharpie
rolfbjarne Feb 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 9 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@
<SystemDrawingCommonPackageVersion>4.7.2</SystemDrawingCommonPackageVersion>
<!-- Fix transient dependency issue found by component governance 4.7.0 -> 4.7.1 brought by Microsoft.Build.Tasks.Core package -->
<SystemSecurityCryptographyXmlPackageVersion>8.0.0</SystemSecurityCryptographyXmlPackageVersion>

<ClangSharpPackageVersion>21.1.8.2</ClangSharpPackageVersion>
<ICSharpCodeNRefactoryPackageVersion>5.5.1</ICSharpCodeNRefactoryPackageVersion>
<libclangPackageVersion>21.1.8</libclangPackageVersion>
<MicrosoftNETTestSdkPackageVersion>17.14.0</MicrosoftNETTestSdkPackageVersion>
<MonoCecilPackageVersion>0.11.6</MonoCecilPackageVersion>
<NUnit3TestAdapterPackageVersion>5.0.0</NUnit3TestAdapterPackageVersion>
<NUnitAnalyzersPackageVersion>4.7.0</NUnitAnalyzersPackageVersion>
<NUnitPackageVersion>4.3.2</NUnitPackageVersion>
</PropertyGroup>
<Import Project="Build.props" Condition="Exists('Build.props')" />
</Project>
290 changes: 290 additions & 0 deletions NOTICE.txt

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions dotnet/Workloads/SignList.xml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@
<!-- Sign dylibs -->
<MacDeveloperSign Include="MonoBundle\*.dylib" />
<MacDeveloperSign Include="native\*.dylib" />
<MacDeveloperSign Include="libclang.dylib" />
<MacDeveloperSign Include="libClangSharp.dylib" />
</ItemGroup>

<Import Project="$(MSBuildThisFileDirectory)SignList.targets" />
Expand Down
2 changes: 1 addition & 1 deletion tests/common/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ public static void FixupTestFiles (string directory, string mode)

public static Dictionary<string, string> GetBuildEnvironment (ApplePlatform platform)
{
Dictionary<string, string> environment = new Dictionary<string, string> ();
var environment = new Dictionary<string, string> ();
SetBuildVariables (platform, ref environment);
return environment;
}
Expand Down
189 changes: 189 additions & 0 deletions tests/sharpie/Sharpie.Bind.Tests/Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Sharpie.Bind.Tests;

using System.Diagnostics;
using System.Drawing;
using ICSharpCode.NRefactory.CSharp;
using Sharpie.Bind;
using Xamarin.Tests;
using Xamarin.Utils;

public static class Extensions {

static Dictionary<ApplePlatform, string> platformAssemblyPaths = new ();
public static string GetPlatformAssemblyPath (ApplePlatform platform)
{
if (!platformAssemblyPaths.TryGetValue (platform, out var path))
platformAssemblyPaths [platform] = path = Configuration.GetBaseLibrary (platform);
return path;
}

public static string GetClangResourceDirectory ()
{
return Path.Combine (Configuration.SourceRoot, "tools", "sharpie", "clang");
}

// Running out-of-process is useful when there's something causing clang to crash, in which case
// running out-of-process will only fail the single test that's running, and not the entire test run.
public static bool UseOutOfProcessBinding = true;

public static BindingResult BindInOrOut (this ObjectiveCBinder binder)
{
BindingResult rv;
if (UseOutOfProcessBinding && !Debugger.IsAttached) {
rv = binder.ExecuteOutOfProcess ();
} else {
rv = binder.Bind ();
}

return rv;
}

/// <summary>
/// Asserts that the binding was successful and that the generated binding code matches the expected binding code.
/// </summary>
/// <param name="bindings"></param>
/// <param name="expectedBindingCode"></param>
/// <param name="additionalInfo"></param>
public static void AssertSuccess (this BindingResult bindings, string? expectedBindingCode, params string [] additionalInfo)
{
bindings.Warnings.ForEach (Console.WriteLine);
bindings.Errors.ForEach (Console.Error.WriteLine);

if (bindings.ExitCode == 139) {
Console.Error.WriteLine ("Binding process crashed with exit code 139 (SIGSEGV).");
Assert.Fail ("Binding process crashed with exit code 139 (SIGSEGV).");
}

Assert.That (bindings.ExitCode, Is.EqualTo (0), "Expected success");
Assert.That (bindings.Errors.Count, Is.EqualTo (0), $"Unexpected number of errors\n\t{string.Join ("\n\t", bindings.Errors)}");

if (expectedBindingCode is null)
return;

var expected = expectedBindingCode.Replace ("\r\n", "\n").Trim ();
var actual = bindings.BindingCode?.Replace ("\r\n", "\n").Trim () ?? "";
if (expected != actual) {
const string splitter = "===========================";
Console.WriteLine ("Expected Binding Code:");
Console.WriteLine (splitter);
Console.WriteLine (expected);
Console.WriteLine (splitter);
Console.WriteLine ("Actual Binding Code:");
Console.WriteLine (splitter);
Console.WriteLine (actual);
Console.WriteLine (splitter);
var diff = ComputeDiff (expected, actual).Split ('\n');
Console.WriteLine ("Diff:");
Console.WriteLine (splitter);
foreach (var line in diff) {
var colorized = false;
if (line.Length >= 2) {
if (line [0] == '+' && line [1] != '+') {
Console.Write (Color.Green);
colorized = true;
} else if (line [0] == '-' && line [1] != '-') {
Console.Write (Color.Red);
colorized = true;
}
}
Console.Write (line);
if (colorized)
Console.Write (Color.Reset);
Console.WriteLine ();
}
Console.WriteLine (splitter);
foreach (var line in additionalInfo)
Console.WriteLine (line);
Assert.That (actual, Is.EqualTo (expected), "Expected binding code");
}
}

static string ComputeDiff (string a, string b)
{
var fa = Path.Combine (Path.GetTempPath (), "sharpie", "tests", "a.cs");
var fb = Path.Combine (Path.GetTempPath (), "sharpie", "tests", "b.cs");
if (File.Exists (fa))
File.Delete (fa);
if (File.Exists (fb))
File.Delete (fb);
Directory.CreateDirectory (Path.GetDirectoryName (fa)!);
File.WriteAllText (fa, a);
File.WriteAllText (fb, b);

var rv = Execution.RunAsync ("diff", new [] { "-u", fa, fb }, timeout: TimeSpan.FromSeconds (10)).Result;

File.Delete (fa);
File.Delete (fb);

return rv.Output.MergedOutput;
}

public static void AssertErrors (this BindingResult bindings, params (int Code, string Message, string? FileName, long? LineNumber) [] expectedErrors)
{
Assert.That (bindings.ExitCode, Is.EqualTo (1), "Expected failure");
Assert.That (bindings.BindingCode, Is.Empty.Or.Null, "Expected no binding code");
AssertMessages ("error", bindings.Errors, expectedErrors);
}

public static void AssertWarnings (this BindingResult bindings, params (int Code, string Message, string? FileName, long? LineNumber) [] expectedWarnings)
{
AssertMessages ("warning", bindings.Warnings, expectedWarnings);
}

public static void AssertNoWarnings (this BindingResult bindings)
{
AssertWarnings (bindings);
}

static void AssertMessages (string type, IList<BindingMessage> messages, params (int Code, string Message, string? FileName, long? LineNumber) [] expectedMessages)
{
Assert.That (messages.Count, Is.EqualTo (expectedMessages.Length), $"Unexpected number of {type}s ({messages.Count})\n\t{string.Join ("\n\t", messages)}");
for (int i = 0; i < expectedMessages.Length; i++) {
Assert.That (messages [i].Code, Is.EqualTo (expectedMessages [i].Code), $"Code for {type} #{i}");
Assert.That (messages [i].Message, Is.EqualTo (expectedMessages [i].Message), $"Message for {type} #{i}");
if (expectedMessages [i].FileName is not null)
Assert.That (messages [i].FileName, Is.EqualTo (expectedMessages [i].FileName), $"FileName for {type} #{i}");
if (expectedMessages [i].LineNumber is not null)
Assert.That (messages [i].LineNumber, Is.EqualTo (expectedMessages [i].LineNumber!.Value), $"LineNumber for {type} #{i}");
}
}
}

static class Color {
internal const char ESC_Char = '\u001b';
internal const string ESC = $"\u001b"; /* \033 - https://github.com/dotnet/csharplang/issues/7400 */
internal const string ST = ESC + "\\";
internal const string OSC = ESC + "]";

public const string DarkBlack = ESC + "[30m";
public const string DarkRed = ESC + "[31m";
public const string DarkGreen = ESC + "[32m";
public const string DarkYellow = ESC + "[33m";
public const string DarkBlue = ESC + "[34m";
public const string DarkMagenta = ESC + "[35m";
public const string DarkCyan = ESC + "[36m";
public const string DarkWhite = ESC + "[37m";

public const string Black = ESC + "[90m";
public const string Red = ESC + "[91m";
public const string Green = ESC + "[92m";
public const string Yellow = ESC + "[93m";
public const string Blue = ESC + "[94m";
public const string Magenta = ESC + "[95m";
public const string Cyan = ESC + "[96m";
public const string White = ESC + "[97m";
public const string Reset = ESC + "[0m";

public const string Bold = ESC + "[1m";
public const string Italicize = ESC + "[3m";
public const string Underline = ESC + "[4m";
public const string Strikethrough = ESC + "[9m";

public const string NotBold = ESC + "[22m";
public const string NotItalic = ESC + "[23m";
public const string NotUnderline = ESC + "[24m";
public const string NotStrikethrough = ESC + "[29m";
}
4 changes: 4 additions & 0 deletions tests/sharpie/Sharpie.Bind.Tests/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

global using System.Runtime.InteropServices;
Loading
Loading