From 79ec998f95406ec6a15f7638860ce0ce3f9d98ec Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 22 May 2026 08:59:47 +0200 Subject: [PATCH 01/79] [NSUrlSessionHandler] Reject unsupported auth methods to allow fallback to Basic. Fixes #25485. (#25493) When a server advertises multiple WWW-Authenticate challenges (e.g. Bearer before Basic), the handler would call PerformDefaultHandling for unrecognized methods like Bearer. This prevented the system from trying subsequent methods. Fix by rejecting unrecognized HTTP auth protection spaces, which allows the URL loading system to try the next advertised authentication method. Fixes https://github.com/dotnet/macios/issues/25485. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Foundation/NSUrlSessionHandler.cs | 18 +-- .../NSUrlSessionHandlerTest.cs | 106 ++++++++++++++++++ 2 files changed, 117 insertions(+), 7 deletions(-) diff --git a/src/Foundation/NSUrlSessionHandler.cs b/src/Foundation/NSUrlSessionHandler.cs index 2c0af8202220..fc35793204e2 100644 --- a/src/Foundation/NSUrlSessionHandler.cs +++ b/src/Foundation/NSUrlSessionHandler.cs @@ -1175,15 +1175,19 @@ static bool TryGetAuthenticationType (NSUrlProtectionSpace protectionSpace, [Not authenticationType = "basic"; } else if (protectionSpace.AuthenticationMethod == NSUrlProtectionSpace.AuthenticationMethodHTTPDigest) { authenticationType = "digest"; - } else if (protectionSpace.AuthenticationMethod == NSUrlProtectionSpace.AuthenticationMethodNegotiate || - protectionSpace.AuthenticationMethod == NSUrlProtectionSpace.AuthenticationMethodHTMLForm) { - // Want to reject this authentication type to allow the next authentication method in the request to - // be used. - authenticationType = RejectProtectionSpaceAuthType; - } else { - // ServerTrust, ClientCertificate or Default. + } else if (protectionSpace.AuthenticationMethod == NSUrlProtectionSpace.AuthenticationMethodServerTrust || + protectionSpace.AuthenticationMethod == NSUrlProtectionSpace.AuthenticationMethodClientCertificate) { + // ServerTrust and ClientCertificate are handled earlier in DidReceiveChallengeImpl, + // so we should not reach here for these types. Return false just in case. authenticationType = null; return false; + } else { + // For any other authentication method (Negotiate, HTMLForm, Bearer, etc.), + // reject this protection space to allow the next authentication method in the + // request to be tried. This is important when the server advertises multiple + // WWW-Authenticate challenges (e.g. Bearer before Basic) - rejecting unsupported + // methods allows fallback to one we can handle. + authenticationType = RejectProtectionSpaceAuthType; } return true; } diff --git a/tests/monotouch-test/System.Net.Http/NSUrlSessionHandlerTest.cs b/tests/monotouch-test/System.Net.Http/NSUrlSessionHandlerTest.cs index 86c0b0efb8b3..f8bf44a1035c 100644 --- a/tests/monotouch-test/System.Net.Http/NSUrlSessionHandlerTest.cs +++ b/tests/monotouch-test/System.Net.Http/NSUrlSessionHandlerTest.cs @@ -3,7 +3,11 @@ // using System; +using System.Net; using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading; using System.Threading.Tasks; using Foundation; using NUnit.Framework; @@ -216,5 +220,107 @@ void IgnoreIfExceptionDueToBackgroundServiceInUseByAnotherProcess (Exception? e) Assert.Ignore ("The background service is in use by another process."); } + + // https://github.com/dotnet/macios/issues/25485 + [Test] + public void BasicAuthWorksWhenBearerIsAdvertisedFirst () + { + if (!HttpListener.IsSupported) { + Assert.Inconclusive ("HttpListener is not supported"); + } + + const string username = "admin"; + const string password = "secret"; + var expectedBasicValue = Convert.ToBase64String (Encoding.UTF8.GetBytes ($"{username}:{password}")); + + var serverReady = new SemaphoreSlim (0, 1); + + var httpListener = StartListenerOnAvailablePort (out var listeningPort); + if (httpListener is null) { + Assert.Inconclusive ("Could not find an available port for the test server."); + return; + } + + var serverTask = Task.Run (async () => { + serverReady.Release (); + try { + while (httpListener.IsListening) { + var context = await httpListener.GetContextAsync ().ConfigureAwait (false); + var request = context.Request; + var response = context.Response; + + var authHeader = request.Headers ["Authorization"]; + if (authHeader is not null && authHeader == $"Basic {expectedBasicValue}") { + // Authenticated - return success + response.StatusCode = 200; + var body = Encoding.UTF8.GetBytes ("authenticated"); + response.ContentLength64 = body.Length; + response.OutputStream.Write (body, 0, body.Length); + } else { + // Return 401 with Bearer first, then Basic + response.StatusCode = 401; + response.AddHeader ("WWW-Authenticate", "Bearer realm=\"test\", charset=\"UTF-8\""); + response.AppendHeader ("WWW-Authenticate", "Basic realm=\"test\", charset=\"UTF-8\""); + } + response.Close (); + } + } catch (ObjectDisposedException) { + // listener was stopped + } catch (HttpListenerException) { + // listener was stopped + } + }); + + HttpStatusCode? statusCode = null; + string responseBody = null; + + try { + var done = TestRuntime.TryRunAsync (TimeSpan.FromSeconds (30), async () => { + await serverReady.WaitAsync ().ConfigureAwait (false); + + using var handler = new NSUrlSessionHandler (); + handler.Credentials = new NetworkCredential (username, password); + using var client = new HttpClient (handler); + using var request = new HttpRequestMessage (HttpMethod.Get, $"http://localhost:{listeningPort}/test"); + var response = await client.SendAsync (request).ConfigureAwait (false); + statusCode = response.StatusCode; + responseBody = await response.Content.ReadAsStringAsync ().ConfigureAwait (false); + }, out var ex); + + Assert.That (done, Is.True, "Request timed out"); + Assert.That (ex, Is.Null, $"Exception: {ex}"); + Assert.That (statusCode, Is.EqualTo (HttpStatusCode.OK), "Expected 200 OK after Basic auth negotiation"); + Assert.That (responseBody, Is.EqualTo ("authenticated"), "Response body"); + + if (serverTask.IsFaulted) + Assert.Fail ($"Server task failed: {serverTask.Exception}"); + } finally { + httpListener.Stop (); + httpListener.Close (); + } + } + + static HttpListener? StartListenerOnAvailablePort (out int listeningPort) + { + // IANA suggested range for dynamic or private ports + const int MinPort = 49215; + const int MaxPort = 65535; + + for (var port = MinPort; port < MaxPort; port++) { + var listener = new HttpListener (); + listener.Prefixes.Add ($"http://*:{port}/"); + try { + listener.Start (); + listeningPort = port; + return listener; + } catch { + // port in use, try next + listener.Close (); + } + } + + listeningPort = -1; + return null; + } } } From 417695160533766d7b979984d6664a67e258aec9 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 22 May 2026 09:45:25 +0200 Subject: [PATCH 02/79] [NSObject] Store the super handle in a ConditionalWeakTable. (#25454) Store the super handle in a ConditionalWeakTable. * Allocate native memory for it. * Cache that native memory in a ConditionalWeakTable. * Free it when the NSObject is freed (but not before). * Obsolete the SuperHandle property in .NET 11, and remove it in XAMCORE_5_0. This is less performant, but any performance issues will be alleviated by the fact that NSObject.GetSuper () will be called much less after #25376. The great advantage is that NSObjectData is now smaller, and fits in the tagged memory returned by `ObjectiveCMarshal.CreateReferenceTrackingHandle`, which will simplify memory management _a lot_ for CoreCLR. Contributes towards https://github.com/dotnet/macios/issues/25383. This is a continuation/simplification of #25405. --- runtime/xamarin/runtime.h | 3 +- src/Foundation/NSObject2.cs | 86 ++++++++++++++----- src/ObjCRuntime/Registrar.cs | 2 +- ...alyst-MonoVM-interpreter-preservedapis.txt | 16 ++-- .../MacCatalyst-MonoVM-interpreter-size.txt | 8 +- .../MacCatalyst-MonoVM-preservedapis.txt | 16 ++-- .../expected/MacCatalyst-MonoVM-size.txt | 8 +- ...atalyst-NativeAOT-TrimmableStatic-size.txt | 6 +- .../expected/MacCatalyst-NativeAOT-size.txt | 4 +- ...reCLR-Interpreter-TrimmableStatic-size.txt | 10 +-- .../MacOSX-CoreCLR-Interpreter-size.txt | 10 +-- .../MacOSX-NativeAOT-TrimmableStatic-size.txt | 4 +- .../expected/MacOSX-NativeAOT-size.txt | 4 +- .../TVOS-MonoVM-interpreter-preservedapis.txt | 16 ++-- .../expected/TVOS-MonoVM-interpreter-size.txt | 4 +- .../expected/TVOS-MonoVM-preservedapis.txt | 16 ++-- .../UnitTests/expected/TVOS-MonoVM-size.txt | 6 +- .../TVOS-NativeAOT-TrimmableStatic-size.txt | 4 +- .../expected/TVOS-NativeAOT-size.txt | 6 +- .../iOS-MonoVM-interpreter-preservedapis.txt | 16 ++-- .../expected/iOS-MonoVM-interpreter-size.txt | 6 +- .../expected/iOS-MonoVM-preservedapis.txt | 16 ++-- .../UnitTests/expected/iOS-MonoVM-size.txt | 8 +- .../iOS-NativeAOT-TrimmableStatic-size.txt | 6 +- .../UnitTests/expected/iOS-NativeAOT-size.txt | 6 +- 25 files changed, 175 insertions(+), 112 deletions(-) diff --git a/runtime/xamarin/runtime.h b/runtime/xamarin/runtime.h index 774713b78dd7..60c461f1b4af 100644 --- a/runtime/xamarin/runtime.h +++ b/runtime/xamarin/runtime.h @@ -367,10 +367,9 @@ extern xamarin_register_assemblies_callback xamarin_register_assemblies; // This has a managed equivalent in NSObject.cs struct NSObjectData { id handle; - struct objc_super* super; uint32_t /* NSObjectFlags */ flags; // if this structure ever changes, the encoding for this method will likely have to be updated in Registrar.RegistrarTypeUnsafe, currently it's: - // Signature = "^{NSObjectData=@^{objc_super}I}:", + // Signature = "^{NSObjectData=@I}:", }; #ifdef __cplusplus diff --git a/src/Foundation/NSObject2.cs b/src/Foundation/NSObject2.cs index 7f1a5adb7e8e..92657093752f 100644 --- a/src/Foundation/NSObject2.cs +++ b/src/Foundation/NSObject2.cs @@ -119,12 +119,9 @@ internal interface INSObjectFactory { #if !COREBUILD // Allocated in native memory, so that it can be accessed from native code without having to deal with the GC. - // Also put objc_super here, because it simplifies code. // This is mirrored in runtime.h and the definition needs to be in sync. struct NSObjectData { - // the layout here is important, the two first fields have to match the objc_super struct. public NativeHandle handle; - public NativeHandle classHandle; public NSObject.Flags flags; } @@ -137,34 +134,42 @@ struct NSObjectData { // * https://github.com/AustinWise/runtime/blob/2bd10ad43df967950657ae0ade1f899dc1b18a41/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.NativeAot.cs#L15 // * https://github.com/AustinWise/runtime/blob/2bd10ad43df967950657ae0ade1f899dc1b18a41/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.NativeAot.cs#L59-L71 // * https://github.com/AustinWise/runtime/blob/2bd10ad43df967950657ae0ade1f899dc1b18a41/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.NativeAot.cs#L181-L185 - unsafe class NSObjectDataHandle { - NSObjectData* data; + unsafe class NSObjectDataHandle : TrackedMemory { + public NSObjectData* Data { get => (NSObjectData*) Value; } + + public NSObjectDataHandle () : base ((nuint) sizeof (NSObjectData)) + { + } + } + + class TrackedMemory { GCHandle handle; - public NSObjectData* Data { get => data; } + public IntPtr Value { get; private set; } - public NSObjectDataHandle () + public unsafe TrackedMemory (nuint size) { - data = Runtime.AllocZeroed (); + Value = (IntPtr) NativeMemory.AllocZeroed (size); } - public void CreateHandle (NSObject obj) + public unsafe void CreateHandle (NSObject trackedObject) { - handle = GCHandle.Alloc (obj, GCHandleType.WeakTrackResurrection); + handle = GCHandle.Alloc (trackedObject, GCHandleType.WeakTrackResurrection); } - ~NSObjectDataHandle () + ~TrackedMemory () { var handleAllocated = handle.IsAllocated; - if (handleAllocated && handle.Target is not null) { // The NSObject instance isn't gone yet, we have to try again later. GC.ReRegisterForFinalize (this); return; } - NativeMemory.Free (data); - data = null; + unsafe { + NativeMemory.Free ((void*) Value); + } + Value = IntPtr.Zero; if (handleAllocated) handle.Free (); @@ -208,6 +213,10 @@ public partial class NSObject : INativeObject // having to attach those threads to to the managed runtime. IntPtr /* unsafe NSObjectData* */ __data; // Read directly from several places in the runtime +#pragma warning disable CS8618 // "Non-nullable field '...' must contain a non-null value when exiting constructor.": this field is always non-null, because NSObject.Initialize is called before anything else is done. + static ConditionalWeakTable super_map; +#pragma warning restore CS8618 + unsafe NativeHandle handle { get => GetData ()->handle; set => GetData ()->handle = value; @@ -388,17 +397,28 @@ internal static IntPtr CreateNSObject (IntPtr type_gchandle, IntPtr handle, Flag } } +#if !XAMCORE_5_0 unsafe NativeHandle GetSuper () { - var data = GetData (); - if (data->classHandle == NativeHandle.Zero) - data->classHandle = ClassHandle; - return (IntPtr) (&data->handle); + var memory = super_map.GetValue (this, (obj) => { + unsafe { + var memory = new TrackedMemory ((nuint) sizeof (objc_super)); + memory.CreateHandle (obj); + return memory; + } + }); + objc_super* sup = (objc_super*) memory.Value; + if (sup->ClassHandle == NativeHandle.Zero) + sup->ClassHandle = ClassHandle; + sup->Handle = handle; + return memory.Value; } +#endif // !XAMCORE_5_0 internal static NativeHandle Initialize () { data_table = new ConditionalWeakTable (); + super_map = new ConditionalWeakTable (); return class_ptr; } @@ -722,16 +742,35 @@ public NSObject DangerousAutorelease () return this; } +#if !XAMCORE_5_0 /// Handle used to represent the methods in the base class for this . /// An opaque pointer, represents an Objective-C objc_super object pointing to our base class. /// - /// This property is used to access members of a base class. - /// This is typically used when you call any of the Messaging - /// methods to invoke methods that were implemented in your base - /// class, instead of invoking the implementation in the current - /// class. + /// + /// This property is used to access members of a base class. + /// This is typically used when you call any of the Messaging + /// methods to invoke methods that were implemented in your base + /// class, instead of invoking the implementation in the current + /// class. + /// + /// + /// This property is obsolete; use the struct instead: + /// + /// + /// + /// /// [EditorBrowsable (EditorBrowsableState.Never)] +#if NET11_0_OR_GREATER + [Obsolete ("Use 'ObjCSuper' instead.")] +#endif public NativeHandle SuperHandle { get { if (handle == IntPtr.Zero) @@ -740,6 +779,7 @@ public NativeHandle SuperHandle { return GetSuper (); } } +#endif // !XAMCORE_5_0 /// Handle (pointer) to the unmanaged object representation. /// A pointer. diff --git a/src/ObjCRuntime/Registrar.cs b/src/ObjCRuntime/Registrar.cs index 173bf5645c4d..a90eed1e9ffe 100644 --- a/src/ObjCRuntime/Registrar.cs +++ b/src/ObjCRuntime/Registrar.cs @@ -2125,7 +2125,7 @@ void FlattenInterfaces (TType? [] ifaces) objcType.Add (new ObjCMethod (this, objcType, null) { Selector = "xamarinGetNSObjectData", Trampoline = Trampoline.GetNSObjectData, - Signature = "^{NSObjectData=@^{objc_super}I}:", + Signature = "^{NSObjectData=@I}:", IsStatic = false, }, ref exceptions); } diff --git a/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-interpreter-preservedapis.txt b/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-interpreter-preservedapis.txt index 6ec14abe043b..7342f8307492 100644 --- a/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-interpreter-preservedapis.txt +++ b/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-interpreter-preservedapis.txt @@ -195,12 +195,9 @@ Microsoft.MacCatalyst.dll:Foundation.NSObject/XamarinGCHandleFlags Foundation.NS Microsoft.MacCatalyst.dll:Foundation.NSObject/XamarinGCHandleFlags Foundation.NSObject/XamarinGCHandleFlags::InitialSet Microsoft.MacCatalyst.dll:Foundation.NSObject/XamarinGCHandleFlags Foundation.NSObject/XamarinGCHandleFlags::None Microsoft.MacCatalyst.dll:Foundation.NSObjectData -Microsoft.MacCatalyst.dll:Foundation.NSObjectData* Foundation.NSObjectDataHandle::data Microsoft.MacCatalyst.dll:Foundation.NSObjectData* Foundation.NSObjectDataHandle::Data() Microsoft.MacCatalyst.dll:Foundation.NSObjectDataHandle Microsoft.MacCatalyst.dll:Foundation.NSObjectDataHandle..ctor() -Microsoft.MacCatalyst.dll:Foundation.NSObjectDataHandle.CreateHandle(Foundation.NSObject) -Microsoft.MacCatalyst.dll:Foundation.NSObjectDataHandle.Finalize() Microsoft.MacCatalyst.dll:Foundation.NSObjectDataHandle.get_Data() Microsoft.MacCatalyst.dll:Foundation.NSObjectFlag Microsoft.MacCatalyst.dll:Foundation.NSObjectFlag Foundation.NSObjectFlag::Empty @@ -252,6 +249,12 @@ Microsoft.MacCatalyst.dll:Foundation.RegisterAttribute.get_IsStubClass() Microsoft.MacCatalyst.dll:Foundation.RegisterAttribute.get_IsWrapper() Microsoft.MacCatalyst.dll:Foundation.RegisterAttribute.get_Name() Microsoft.MacCatalyst.dll:Foundation.RegisterAttribute.get_SkipRegistration() +Microsoft.MacCatalyst.dll:Foundation.TrackedMemory +Microsoft.MacCatalyst.dll:Foundation.TrackedMemory..ctor(System.UIntPtr) +Microsoft.MacCatalyst.dll:Foundation.TrackedMemory.CreateHandle(Foundation.NSObject) +Microsoft.MacCatalyst.dll:Foundation.TrackedMemory.Finalize() +Microsoft.MacCatalyst.dll:Foundation.TrackedMemory.get_Value() +Microsoft.MacCatalyst.dll:Foundation.TrackedMemory.set_Value(System.IntPtr) Microsoft.MacCatalyst.dll:Foundation.You_Should_Not_Call_base_In_This_Method Microsoft.MacCatalyst.dll:Foundation.You_Should_Not_Call_base_In_This_Method..ctor() Microsoft.MacCatalyst.dll:ObjCRuntime.__Registrar__ @@ -495,7 +498,6 @@ Microsoft.MacCatalyst.dll:ObjCRuntime.NativeHandle Foundation.NSObject::class_pt Microsoft.MacCatalyst.dll:ObjCRuntime.NativeHandle Foundation.NSObject::ClassHandle() Microsoft.MacCatalyst.dll:ObjCRuntime.NativeHandle Foundation.NSObject::handle() Microsoft.MacCatalyst.dll:ObjCRuntime.NativeHandle Foundation.NSObject::Handle() -Microsoft.MacCatalyst.dll:ObjCRuntime.NativeHandle Foundation.NSObjectData::classHandle Microsoft.MacCatalyst.dll:ObjCRuntime.NativeHandle Foundation.NSObjectData::handle Microsoft.MacCatalyst.dll:ObjCRuntime.NativeHandle Foundation.NSString::class_ptr Microsoft.MacCatalyst.dll:ObjCRuntime.NativeHandle Foundation.NSString::ClassHandle() @@ -592,7 +594,6 @@ Microsoft.MacCatalyst.dll:ObjCRuntime.Runtime.g__Constru Microsoft.MacCatalyst.dll:ObjCRuntime.Runtime.g__ConstructNSObjectViaFactoryMethod|288_0`1(ObjCRuntime.NativeHandle) Microsoft.MacCatalyst.dll:ObjCRuntime.Runtime.AllocGCHandle(System.Object, System.Runtime.InteropServices.GCHandleType) Microsoft.MacCatalyst.dll:ObjCRuntime.Runtime.AllocGCHandle(System.Object) -Microsoft.MacCatalyst.dll:ObjCRuntime.Runtime.AllocZeroed`1() Microsoft.MacCatalyst.dll:ObjCRuntime.Runtime.AppendAdditionalInformation(System.Text.StringBuilder, System.IntPtr, System.RuntimeMethodHandle) Microsoft.MacCatalyst.dll:ObjCRuntime.Runtime.attempt_retain_nsobject(System.IntPtr, System.IntPtr*) Microsoft.MacCatalyst.dll:ObjCRuntime.Runtime.AttemptRetainNSObject(System.IntPtr) @@ -1334,6 +1335,8 @@ Microsoft.MacCatalyst.dll:System.IntPtr CoreFoundation.CFRange::len Microsoft.MacCatalyst.dll:System.IntPtr CoreFoundation.CFRange::loc Microsoft.MacCatalyst.dll:System.IntPtr Foundation.NSObject::__data Microsoft.MacCatalyst.dll:System.IntPtr Foundation.NSObject/NSObject_Disposer::class_ptr +Microsoft.MacCatalyst.dll:System.IntPtr Foundation.TrackedMemory::k__BackingField +Microsoft.MacCatalyst.dll:System.IntPtr Foundation.TrackedMemory::Value() Microsoft.MacCatalyst.dll:System.IntPtr ObjCRuntime.AdoptsAttribute::ProtocolHandle() Microsoft.MacCatalyst.dll:System.IntPtr ObjCRuntime.BlockCollector::block Microsoft.MacCatalyst.dll:System.IntPtr ObjCRuntime.Libraries/CoreFoundation::Handle @@ -1497,9 +1500,10 @@ Microsoft.MacCatalyst.dll:System.Reflection.MethodBase Registrar.Registrar/ObjCM Microsoft.MacCatalyst.dll:System.Reflection.PropertyInfo Registrar.Registrar/ObjCProperty::Property Microsoft.MacCatalyst.dll:System.Reflection.TypeFilter Registrar.SharedDynamic/<>c::<>9__0_0 Microsoft.MacCatalyst.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 Foundation.NSObject::data_table +Microsoft.MacCatalyst.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 Foundation.NSObject::super_map Microsoft.MacCatalyst.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 ObjCRuntime.Runtime::block_lifetime_table Microsoft.MacCatalyst.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 ObjCRuntime.Class::assembly_to_name -Microsoft.MacCatalyst.dll:System.Runtime.InteropServices.GCHandle Foundation.NSObjectDataHandle::handle +Microsoft.MacCatalyst.dll:System.Runtime.InteropServices.GCHandle Foundation.TrackedMemory::handle Microsoft.MacCatalyst.dll:System.Runtime.InteropServices.NFloat CoreGraphics.CGRect::height Microsoft.MacCatalyst.dll:System.Runtime.InteropServices.NFloat CoreGraphics.CGRect::width Microsoft.MacCatalyst.dll:System.Runtime.InteropServices.NFloat CoreGraphics.CGRect::x diff --git a/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-interpreter-size.txt b/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-interpreter-size.txt index 78ee37f1f790..23bdbd58a4b5 100644 --- a/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-interpreter-size.txt +++ b/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-interpreter-size.txt @@ -1,9 +1,9 @@ -AppBundleSize: 5,787,160 bytes (5,651.5 KB = 5.5 MB) +AppBundleSize: 5,789,578 bytes (5,653.9 KB = 5.5 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: Contents/_CodeSignature/CodeResources: 3,310 bytes (3.2 KB = 0.0 MB) -Contents/Info.plist: 1,101 bytes (1.1 KB = 0.0 MB) -Contents/MacOS/SizeTestApp: 4,563,536 bytes (4,456.6 KB = 4.4 MB) -Contents/MonoBundle/Microsoft.MacCatalyst.dll: 157,184 bytes (153.5 KB = 0.1 MB) +Contents/Info.plist: 1,119 bytes (1.1 KB = 0.0 MB) +Contents/MacOS/SizeTestApp: 4,565,424 bytes (4,458.4 KB = 4.4 MB) +Contents/MonoBundle/Microsoft.MacCatalyst.dll: 157,696 bytes (154.0 KB = 0.2 MB) Contents/MonoBundle/runtimeconfig.bin: 1,405 bytes (1.4 KB = 0.0 MB) Contents/MonoBundle/SizeTestApp.dll: 7,680 bytes (7.5 KB = 0.0 MB) Contents/MonoBundle/System.Private.CoreLib.aotdata.arm64: 41,224 bytes (40.3 KB = 0.0 MB) diff --git a/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-preservedapis.txt b/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-preservedapis.txt index 0043aa3bcc3b..d9b210cc8000 100644 --- a/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-preservedapis.txt +++ b/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-preservedapis.txt @@ -178,12 +178,9 @@ Microsoft.MacCatalyst.dll:Foundation.NSObject/XamarinGCHandleFlags Foundation.NS Microsoft.MacCatalyst.dll:Foundation.NSObject/XamarinGCHandleFlags Foundation.NSObject/XamarinGCHandleFlags::InitialSet Microsoft.MacCatalyst.dll:Foundation.NSObject/XamarinGCHandleFlags Foundation.NSObject/XamarinGCHandleFlags::None Microsoft.MacCatalyst.dll:Foundation.NSObjectData -Microsoft.MacCatalyst.dll:Foundation.NSObjectData* Foundation.NSObjectDataHandle::data Microsoft.MacCatalyst.dll:Foundation.NSObjectData* Foundation.NSObjectDataHandle::Data() Microsoft.MacCatalyst.dll:Foundation.NSObjectDataHandle Microsoft.MacCatalyst.dll:Foundation.NSObjectDataHandle..ctor() -Microsoft.MacCatalyst.dll:Foundation.NSObjectDataHandle.CreateHandle(Foundation.NSObject) -Microsoft.MacCatalyst.dll:Foundation.NSObjectDataHandle.Finalize() Microsoft.MacCatalyst.dll:Foundation.NSObjectDataHandle.get_Data() Microsoft.MacCatalyst.dll:Foundation.NSObjectFlag Microsoft.MacCatalyst.dll:Foundation.NSObjectFlag Foundation.NSObjectFlag::Empty @@ -194,6 +191,12 @@ Microsoft.MacCatalyst.dll:Foundation.RegisterAttribute Microsoft.MacCatalyst.dll:Foundation.RegisterAttribute..ctor(System.String, System.Boolean) Microsoft.MacCatalyst.dll:Foundation.RegisterAttribute..ctor(System.String) Microsoft.MacCatalyst.dll:Foundation.RegisterAttribute.get_IsWrapper() +Microsoft.MacCatalyst.dll:Foundation.TrackedMemory +Microsoft.MacCatalyst.dll:Foundation.TrackedMemory..ctor(System.UIntPtr) +Microsoft.MacCatalyst.dll:Foundation.TrackedMemory.CreateHandle(Foundation.NSObject) +Microsoft.MacCatalyst.dll:Foundation.TrackedMemory.Finalize() +Microsoft.MacCatalyst.dll:Foundation.TrackedMemory.get_Value() +Microsoft.MacCatalyst.dll:Foundation.TrackedMemory.set_Value(System.IntPtr) Microsoft.MacCatalyst.dll:Foundation.You_Should_Not_Call_base_In_This_Method Microsoft.MacCatalyst.dll:Foundation.You_Should_Not_Call_base_In_This_Method..ctor() Microsoft.MacCatalyst.dll:ObjCRuntime.__Registrar__ @@ -375,7 +378,6 @@ Microsoft.MacCatalyst.dll:ObjCRuntime.NativeHandle Foundation.NSObject::class_pt Microsoft.MacCatalyst.dll:ObjCRuntime.NativeHandle Foundation.NSObject::ClassHandle() Microsoft.MacCatalyst.dll:ObjCRuntime.NativeHandle Foundation.NSObject::handle() Microsoft.MacCatalyst.dll:ObjCRuntime.NativeHandle Foundation.NSObject::Handle() -Microsoft.MacCatalyst.dll:ObjCRuntime.NativeHandle Foundation.NSObjectData::classHandle Microsoft.MacCatalyst.dll:ObjCRuntime.NativeHandle Foundation.NSObjectData::handle Microsoft.MacCatalyst.dll:ObjCRuntime.NativeHandle ObjCRuntime.Class::handle Microsoft.MacCatalyst.dll:ObjCRuntime.NativeHandle ObjCRuntime.Class::Handle() @@ -450,7 +452,6 @@ Microsoft.MacCatalyst.dll:ObjCRuntime.Runtime.g__Constru Microsoft.MacCatalyst.dll:ObjCRuntime.Runtime.g__ConstructNSObjectViaFactoryMethod|288_0`1(ObjCRuntime.NativeHandle) Microsoft.MacCatalyst.dll:ObjCRuntime.Runtime.AllocGCHandle(System.Object, System.Runtime.InteropServices.GCHandleType) Microsoft.MacCatalyst.dll:ObjCRuntime.Runtime.AllocGCHandle(System.Object) -Microsoft.MacCatalyst.dll:ObjCRuntime.Runtime.AllocZeroed`1() Microsoft.MacCatalyst.dll:ObjCRuntime.Runtime.AppendAdditionalInformation(System.Text.StringBuilder, System.IntPtr, System.RuntimeMethodHandle) Microsoft.MacCatalyst.dll:ObjCRuntime.Runtime.attempt_retain_nsobject(System.IntPtr, System.IntPtr*) Microsoft.MacCatalyst.dll:ObjCRuntime.Runtime.AttemptRetainNSObject(System.IntPtr) @@ -721,6 +722,8 @@ Microsoft.MacCatalyst.dll:System.IntPtr CoreFoundation.CFRange::len Microsoft.MacCatalyst.dll:System.IntPtr CoreFoundation.CFRange::loc Microsoft.MacCatalyst.dll:System.IntPtr Foundation.NSObject::__data Microsoft.MacCatalyst.dll:System.IntPtr Foundation.NSObject/NSObject_Disposer::class_ptr +Microsoft.MacCatalyst.dll:System.IntPtr Foundation.TrackedMemory::k__BackingField +Microsoft.MacCatalyst.dll:System.IntPtr Foundation.TrackedMemory::Value() Microsoft.MacCatalyst.dll:System.IntPtr ObjCRuntime.BlockCollector::block Microsoft.MacCatalyst.dll:System.IntPtr ObjCRuntime.Libraries/CoreFoundation::Handle Microsoft.MacCatalyst.dll:System.IntPtr ObjCRuntime.NativeHandle::handle @@ -852,9 +855,10 @@ Microsoft.MacCatalyst.dll:System.Object ObjCRuntime.Class::verification_lock Microsoft.MacCatalyst.dll:System.Object ObjCRuntime.Runtime::lock_obj Microsoft.MacCatalyst.dll:System.Reflection.Assembly Foundation.NSObject::PlatformAssembly Microsoft.MacCatalyst.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 Foundation.NSObject::data_table +Microsoft.MacCatalyst.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 Foundation.NSObject::super_map Microsoft.MacCatalyst.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 ObjCRuntime.Runtime::block_lifetime_table Microsoft.MacCatalyst.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 ObjCRuntime.Class::assembly_to_name -Microsoft.MacCatalyst.dll:System.Runtime.InteropServices.GCHandle Foundation.NSObjectDataHandle::handle +Microsoft.MacCatalyst.dll:System.Runtime.InteropServices.GCHandle Foundation.TrackedMemory::handle Microsoft.MacCatalyst.dll:System.Runtime.InteropServices.NFloat CoreGraphics.CGRect::height Microsoft.MacCatalyst.dll:System.Runtime.InteropServices.NFloat CoreGraphics.CGRect::width Microsoft.MacCatalyst.dll:System.Runtime.InteropServices.NFloat CoreGraphics.CGRect::x diff --git a/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-size.txt b/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-size.txt index 67e1e583c978..48f7aafc2dad 100644 --- a/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-size.txt +++ b/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-size.txt @@ -1,10 +1,10 @@ -AppBundleSize: 16,323,580 bytes (15,941.0 KB = 15.6 MB) +AppBundleSize: 16,325,774 bytes (15,943.1 KB = 15.6 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: Contents/_CodeSignature/CodeResources: 4,134 bytes (4.0 KB = 0.0 MB) -Contents/Info.plist: 1,101 bytes (1.1 KB = 0.0 MB) -Contents/MacOS/SizeTestApp: 13,812,384 bytes (13,488.7 KB = 13.2 MB) +Contents/Info.plist: 1,119 bytes (1.1 KB = 0.0 MB) +Contents/MacOS/SizeTestApp: 13,814,640 bytes (13,490.9 KB = 13.2 MB) Contents/MonoBundle/aot-instances.aotdata.arm64: 1,045,032 bytes (1,020.5 KB = 1.0 MB) -Contents/MonoBundle/Microsoft.MacCatalyst.aotdata.arm64: 36,368 bytes (35.5 KB = 0.0 MB) +Contents/MonoBundle/Microsoft.MacCatalyst.aotdata.arm64: 36,288 bytes (35.4 KB = 0.0 MB) Contents/MonoBundle/Microsoft.MacCatalyst.dll: 50,688 bytes (49.5 KB = 0.0 MB) Contents/MonoBundle/runtimeconfig.bin: 1,481 bytes (1.4 KB = 0.0 MB) Contents/MonoBundle/SizeTestApp.aotdata.arm64: 1,552 bytes (1.5 KB = 0.0 MB) diff --git a/tests/dotnet/UnitTests/expected/MacCatalyst-NativeAOT-TrimmableStatic-size.txt b/tests/dotnet/UnitTests/expected/MacCatalyst-NativeAOT-TrimmableStatic-size.txt index 59ab57bf44ef..ac4c45f624c1 100644 --- a/tests/dotnet/UnitTests/expected/MacCatalyst-NativeAOT-TrimmableStatic-size.txt +++ b/tests/dotnet/UnitTests/expected/MacCatalyst-NativeAOT-TrimmableStatic-size.txt @@ -1,7 +1,7 @@ -AppBundleSize: 8,792,419 bytes (8,586.3 KB = 8.4 MB) +AppBundleSize: 8,808,853 bytes (8,602.4 KB = 8.4 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: Contents/_CodeSignature/CodeResources: 2,358 bytes (2.3 KB = 0.0 MB) -Contents/Info.plist: 1,101 bytes (1.1 KB = 0.0 MB) -Contents/MacOS/SizeTestApp: 8,787,056 bytes (8,581.1 KB = 8.4 MB) +Contents/Info.plist: 1,119 bytes (1.1 KB = 0.0 MB) +Contents/MacOS/SizeTestApp: 8,803,472 bytes (8,597.1 KB = 8.4 MB) Contents/MonoBundle/runtimeconfig.bin: 1,896 bytes (1.9 KB = 0.0 MB) Contents/PkgInfo: 8 bytes (0.0 KB = 0.0 MB) diff --git a/tests/dotnet/UnitTests/expected/MacCatalyst-NativeAOT-size.txt b/tests/dotnet/UnitTests/expected/MacCatalyst-NativeAOT-size.txt index 8a8b69daa88a..f35ae9613d96 100644 --- a/tests/dotnet/UnitTests/expected/MacCatalyst-NativeAOT-size.txt +++ b/tests/dotnet/UnitTests/expected/MacCatalyst-NativeAOT-size.txt @@ -1,7 +1,7 @@ -AppBundleSize: 2,781,131 bytes (2,715.9 KB = 2.7 MB) +AppBundleSize: 2,781,149 bytes (2,716.0 KB = 2.7 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: Contents/_CodeSignature/CodeResources: 2,358 bytes (2.3 KB = 0.0 MB) -Contents/Info.plist: 1,101 bytes (1.1 KB = 0.0 MB) +Contents/Info.plist: 1,119 bytes (1.1 KB = 0.0 MB) Contents/MacOS/SizeTestApp: 2,775,856 bytes (2,710.8 KB = 2.6 MB) Contents/MonoBundle/runtimeconfig.bin: 1,808 bytes (1.8 KB = 0.0 MB) Contents/PkgInfo: 8 bytes (0.0 KB = 0.0 MB) diff --git a/tests/dotnet/UnitTests/expected/MacOSX-CoreCLR-Interpreter-TrimmableStatic-size.txt b/tests/dotnet/UnitTests/expected/MacOSX-CoreCLR-Interpreter-TrimmableStatic-size.txt index 067f24eb504d..726ac17a14c8 100644 --- a/tests/dotnet/UnitTests/expected/MacOSX-CoreCLR-Interpreter-TrimmableStatic-size.txt +++ b/tests/dotnet/UnitTests/expected/MacOSX-CoreCLR-Interpreter-TrimmableStatic-size.txt @@ -1,12 +1,12 @@ -AppBundleSize: 260,130,342 bytes (254,033.5 KB = 248.1 MB) +AppBundleSize: 260,131,912 bytes (254,035.1 KB = 248.1 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: Contents/_CodeSignature/CodeResources: 67,868 bytes (66.3 KB = 0.1 MB) -Contents/Info.plist: 732 bytes (0.7 KB = 0.0 MB) -Contents/MacOS/SizeTestApp: 7,431,264 bytes (7,257.1 KB = 7.1 MB) +Contents/Info.plist: 750 bytes (0.7 KB = 0.0 MB) +Contents/MacOS/SizeTestApp: 7,431,792 bytes (7,257.6 KB = 7.1 MB) Contents/MonoBundle/.xamarin/osx-arm64/_Microsoft.macOS.TypeMap.dll: 4,847,616 bytes (4,734.0 KB = 4.6 MB) Contents/MonoBundle/.xamarin/osx-arm64/_SizeTestApp.TypeMap.dll: 3,072 bytes (3.0 KB = 0.0 MB) Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.CSharp.dll: 893,192 bytes (872.3 KB = 0.9 MB) -Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.macOS.dll: 38,523,904 bytes (37,621.0 KB = 36.7 MB) +Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.macOS.dll: 38,524,416 bytes (37,621.5 KB = 36.7 MB) Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.VisualBasic.Core.dll: 1,335,096 bytes (1,303.8 KB = 1.3 MB) Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.VisualBasic.dll: 17,680 bytes (17.3 KB = 0.0 MB) Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.Win32.Primitives.dll: 16,144 bytes (15.8 KB = 0.0 MB) @@ -182,7 +182,7 @@ Contents/MonoBundle/.xamarin/osx-arm64/WindowsBase.dll: 16,688 bytes (16.3 KB = Contents/MonoBundle/.xamarin/osx-x64/_Microsoft.macOS.TypeMap.dll: 4,847,616 bytes (4,734.0 KB = 4.6 MB) Contents/MonoBundle/.xamarin/osx-x64/_SizeTestApp.TypeMap.dll: 3,072 bytes (3.0 KB = 0.0 MB) Contents/MonoBundle/.xamarin/osx-x64/Microsoft.CSharp.dll: 796,432 bytes (777.8 KB = 0.8 MB) -Contents/MonoBundle/.xamarin/osx-x64/Microsoft.macOS.dll: 38,523,904 bytes (37,621.0 KB = 36.7 MB) +Contents/MonoBundle/.xamarin/osx-x64/Microsoft.macOS.dll: 38,524,416 bytes (37,621.5 KB = 36.7 MB) Contents/MonoBundle/.xamarin/osx-x64/Microsoft.VisualBasic.Core.dll: 1,166,648 bytes (1,139.3 KB = 1.1 MB) Contents/MonoBundle/.xamarin/osx-x64/Microsoft.VisualBasic.dll: 17,680 bytes (17.3 KB = 0.0 MB) Contents/MonoBundle/.xamarin/osx-x64/Microsoft.Win32.Primitives.dll: 16,176 bytes (15.8 KB = 0.0 MB) diff --git a/tests/dotnet/UnitTests/expected/MacOSX-CoreCLR-Interpreter-size.txt b/tests/dotnet/UnitTests/expected/MacOSX-CoreCLR-Interpreter-size.txt index 83d57f54803e..c7f0715b7541 100644 --- a/tests/dotnet/UnitTests/expected/MacOSX-CoreCLR-Interpreter-size.txt +++ b/tests/dotnet/UnitTests/expected/MacOSX-CoreCLR-Interpreter-size.txt @@ -1,10 +1,10 @@ -AppBundleSize: 248,292,016 bytes (242,472.7 KB = 236.8 MB) +AppBundleSize: 248,293,554 bytes (242,474.2 KB = 236.8 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: Contents/_CodeSignature/CodeResources: 67,160 bytes (65.6 KB = 0.1 MB) -Contents/Info.plist: 732 bytes (0.7 KB = 0.0 MB) -Contents/MacOS/SizeTestApp: 8,087,552 bytes (7,898.0 KB = 7.7 MB) +Contents/Info.plist: 750 bytes (0.7 KB = 0.0 MB) +Contents/MacOS/SizeTestApp: 8,088,048 bytes (7,898.5 KB = 7.7 MB) Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.CSharp.dll: 893,192 bytes (872.3 KB = 0.9 MB) -Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.macOS.dll: 37,127,168 bytes (36,257.0 KB = 35.4 MB) +Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.macOS.dll: 37,127,680 bytes (36,257.5 KB = 35.4 MB) Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.VisualBasic.Core.dll: 1,335,096 bytes (1,303.8 KB = 1.3 MB) Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.VisualBasic.dll: 17,680 bytes (17.3 KB = 0.0 MB) Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.Win32.Primitives.dll: 16,144 bytes (15.8 KB = 0.0 MB) @@ -178,7 +178,7 @@ Contents/MonoBundle/.xamarin/osx-arm64/System.Xml.XPath.dll: 16,144 bytes (15.8 Contents/MonoBundle/.xamarin/osx-arm64/System.Xml.XPath.XDocument.dll: 17,672 bytes (17.3 KB = 0.0 MB) Contents/MonoBundle/.xamarin/osx-arm64/WindowsBase.dll: 16,688 bytes (16.3 KB = 0.0 MB) Contents/MonoBundle/.xamarin/osx-x64/Microsoft.CSharp.dll: 796,432 bytes (777.8 KB = 0.8 MB) -Contents/MonoBundle/.xamarin/osx-x64/Microsoft.macOS.dll: 37,127,168 bytes (36,257.0 KB = 35.4 MB) +Contents/MonoBundle/.xamarin/osx-x64/Microsoft.macOS.dll: 37,127,680 bytes (36,257.5 KB = 35.4 MB) Contents/MonoBundle/.xamarin/osx-x64/Microsoft.VisualBasic.Core.dll: 1,166,648 bytes (1,139.3 KB = 1.1 MB) Contents/MonoBundle/.xamarin/osx-x64/Microsoft.VisualBasic.dll: 17,680 bytes (17.3 KB = 0.0 MB) Contents/MonoBundle/.xamarin/osx-x64/Microsoft.Win32.Primitives.dll: 16,176 bytes (15.8 KB = 0.0 MB) diff --git a/tests/dotnet/UnitTests/expected/MacOSX-NativeAOT-TrimmableStatic-size.txt b/tests/dotnet/UnitTests/expected/MacOSX-NativeAOT-TrimmableStatic-size.txt index 9a45e4dcfb44..03086f1a3700 100644 --- a/tests/dotnet/UnitTests/expected/MacOSX-NativeAOT-TrimmableStatic-size.txt +++ b/tests/dotnet/UnitTests/expected/MacOSX-NativeAOT-TrimmableStatic-size.txt @@ -1,7 +1,7 @@ -AppBundleSize: 21,531,678 bytes (21,027.0 KB = 20.5 MB) +AppBundleSize: 21,531,696 bytes (21,027.0 KB = 20.5 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: Contents/_CodeSignature/CodeResources: 3,489 bytes (3.4 KB = 0.0 MB) -Contents/Info.plist: 732 bytes (0.7 KB = 0.0 MB) +Contents/Info.plist: 750 bytes (0.7 KB = 0.0 MB) Contents/MacOS/SizeTestApp: 18,538,016 bytes (18,103.5 KB = 17.7 MB) Contents/MonoBundle/libSystem.Globalization.Native.dylib: 252,176 bytes (246.3 KB = 0.2 MB) Contents/MonoBundle/libSystem.IO.Compression.Native.dylib: 2,005,440 bytes (1,958.4 KB = 1.9 MB) diff --git a/tests/dotnet/UnitTests/expected/MacOSX-NativeAOT-size.txt b/tests/dotnet/UnitTests/expected/MacOSX-NativeAOT-size.txt index 40ce560ec153..73e9feab2def 100644 --- a/tests/dotnet/UnitTests/expected/MacOSX-NativeAOT-size.txt +++ b/tests/dotnet/UnitTests/expected/MacOSX-NativeAOT-size.txt @@ -1,7 +1,7 @@ -AppBundleSize: 8,835,196 bytes (8,628.1 KB = 8.4 MB) +AppBundleSize: 8,835,214 bytes (8,628.1 KB = 8.4 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: Contents/_CodeSignature/CodeResources: 3,489 bytes (3.4 KB = 0.0 MB) -Contents/Info.plist: 732 bytes (0.7 KB = 0.0 MB) +Contents/Info.plist: 750 bytes (0.7 KB = 0.0 MB) Contents/MacOS/SizeTestApp: 5,841,616 bytes (5,704.7 KB = 5.6 MB) Contents/MonoBundle/libSystem.Globalization.Native.dylib: 252,176 bytes (246.3 KB = 0.2 MB) Contents/MonoBundle/libSystem.IO.Compression.Native.dylib: 2,005,440 bytes (1,958.4 KB = 1.9 MB) diff --git a/tests/dotnet/UnitTests/expected/TVOS-MonoVM-interpreter-preservedapis.txt b/tests/dotnet/UnitTests/expected/TVOS-MonoVM-interpreter-preservedapis.txt index 9dc7e411bbb3..5dc7ee998d0d 100644 --- a/tests/dotnet/UnitTests/expected/TVOS-MonoVM-interpreter-preservedapis.txt +++ b/tests/dotnet/UnitTests/expected/TVOS-MonoVM-interpreter-preservedapis.txt @@ -187,12 +187,9 @@ Microsoft.tvOS.dll:Foundation.NSObject/XamarinGCHandleFlags Foundation.NSObject/ Microsoft.tvOS.dll:Foundation.NSObject/XamarinGCHandleFlags Foundation.NSObject/XamarinGCHandleFlags::InitialSet Microsoft.tvOS.dll:Foundation.NSObject/XamarinGCHandleFlags Foundation.NSObject/XamarinGCHandleFlags::None Microsoft.tvOS.dll:Foundation.NSObjectData -Microsoft.tvOS.dll:Foundation.NSObjectData* Foundation.NSObjectDataHandle::data Microsoft.tvOS.dll:Foundation.NSObjectData* Foundation.NSObjectDataHandle::Data() Microsoft.tvOS.dll:Foundation.NSObjectDataHandle Microsoft.tvOS.dll:Foundation.NSObjectDataHandle..ctor() -Microsoft.tvOS.dll:Foundation.NSObjectDataHandle.CreateHandle(Foundation.NSObject) -Microsoft.tvOS.dll:Foundation.NSObjectDataHandle.Finalize() Microsoft.tvOS.dll:Foundation.NSObjectDataHandle.get_Data() Microsoft.tvOS.dll:Foundation.NSObjectFlag Microsoft.tvOS.dll:Foundation.NSObjectFlag Foundation.NSObjectFlag::Empty @@ -244,6 +241,12 @@ Microsoft.tvOS.dll:Foundation.RegisterAttribute.get_IsStubClass() Microsoft.tvOS.dll:Foundation.RegisterAttribute.get_IsWrapper() Microsoft.tvOS.dll:Foundation.RegisterAttribute.get_Name() Microsoft.tvOS.dll:Foundation.RegisterAttribute.get_SkipRegistration() +Microsoft.tvOS.dll:Foundation.TrackedMemory +Microsoft.tvOS.dll:Foundation.TrackedMemory..ctor(System.UIntPtr) +Microsoft.tvOS.dll:Foundation.TrackedMemory.CreateHandle(Foundation.NSObject) +Microsoft.tvOS.dll:Foundation.TrackedMemory.Finalize() +Microsoft.tvOS.dll:Foundation.TrackedMemory.get_Value() +Microsoft.tvOS.dll:Foundation.TrackedMemory.set_Value(System.IntPtr) Microsoft.tvOS.dll:Foundation.You_Should_Not_Call_base_In_This_Method Microsoft.tvOS.dll:Foundation.You_Should_Not_Call_base_In_This_Method..ctor() Microsoft.tvOS.dll:ObjCRuntime.__Registrar__ @@ -490,7 +493,6 @@ Microsoft.tvOS.dll:ObjCRuntime.NativeHandle Foundation.NSObject::class_ptr Microsoft.tvOS.dll:ObjCRuntime.NativeHandle Foundation.NSObject::ClassHandle() Microsoft.tvOS.dll:ObjCRuntime.NativeHandle Foundation.NSObject::handle() Microsoft.tvOS.dll:ObjCRuntime.NativeHandle Foundation.NSObject::Handle() -Microsoft.tvOS.dll:ObjCRuntime.NativeHandle Foundation.NSObjectData::classHandle Microsoft.tvOS.dll:ObjCRuntime.NativeHandle Foundation.NSObjectData::handle Microsoft.tvOS.dll:ObjCRuntime.NativeHandle Foundation.NSString::class_ptr Microsoft.tvOS.dll:ObjCRuntime.NativeHandle Foundation.NSString::ClassHandle() @@ -588,7 +590,6 @@ Microsoft.tvOS.dll:ObjCRuntime.Runtime.g__ConstructINati Microsoft.tvOS.dll:ObjCRuntime.Runtime.g__ConstructNSObjectViaFactoryMethod|288_0`1(ObjCRuntime.NativeHandle) Microsoft.tvOS.dll:ObjCRuntime.Runtime.AllocGCHandle(System.Object, System.Runtime.InteropServices.GCHandleType) Microsoft.tvOS.dll:ObjCRuntime.Runtime.AllocGCHandle(System.Object) -Microsoft.tvOS.dll:ObjCRuntime.Runtime.AllocZeroed`1() Microsoft.tvOS.dll:ObjCRuntime.Runtime.AppendAdditionalInformation(System.Text.StringBuilder, System.IntPtr, System.RuntimeMethodHandle) Microsoft.tvOS.dll:ObjCRuntime.Runtime.attempt_retain_nsobject(System.IntPtr, System.IntPtr*) Microsoft.tvOS.dll:ObjCRuntime.Runtime.AttemptRetainNSObject(System.IntPtr) @@ -1324,6 +1325,8 @@ Microsoft.tvOS.dll:System.IntPtr CoreFoundation.CFRange::len Microsoft.tvOS.dll:System.IntPtr CoreFoundation.CFRange::loc Microsoft.tvOS.dll:System.IntPtr Foundation.NSObject::__data Microsoft.tvOS.dll:System.IntPtr Foundation.NSObject/NSObject_Disposer::class_ptr +Microsoft.tvOS.dll:System.IntPtr Foundation.TrackedMemory::k__BackingField +Microsoft.tvOS.dll:System.IntPtr Foundation.TrackedMemory::Value() Microsoft.tvOS.dll:System.IntPtr ObjCRuntime.AdoptsAttribute::ProtocolHandle() Microsoft.tvOS.dll:System.IntPtr ObjCRuntime.BlockCollector::block Microsoft.tvOS.dll:System.IntPtr ObjCRuntime.Libraries/CoreFoundation::Handle @@ -1485,9 +1488,10 @@ Microsoft.tvOS.dll:System.Reflection.MethodBase Registrar.Registrar/ObjCMethod:: Microsoft.tvOS.dll:System.Reflection.PropertyInfo Registrar.Registrar/ObjCProperty::Property Microsoft.tvOS.dll:System.Reflection.TypeFilter Registrar.SharedDynamic/<>c::<>9__0_0 Microsoft.tvOS.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 Foundation.NSObject::data_table +Microsoft.tvOS.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 Foundation.NSObject::super_map Microsoft.tvOS.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 ObjCRuntime.Runtime::block_lifetime_table Microsoft.tvOS.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 ObjCRuntime.Class::assembly_to_name -Microsoft.tvOS.dll:System.Runtime.InteropServices.GCHandle Foundation.NSObjectDataHandle::handle +Microsoft.tvOS.dll:System.Runtime.InteropServices.GCHandle Foundation.TrackedMemory::handle Microsoft.tvOS.dll:System.Runtime.InteropServices.NFloat CoreGraphics.CGRect::height Microsoft.tvOS.dll:System.Runtime.InteropServices.NFloat CoreGraphics.CGRect::width Microsoft.tvOS.dll:System.Runtime.InteropServices.NFloat CoreGraphics.CGRect::x diff --git a/tests/dotnet/UnitTests/expected/TVOS-MonoVM-interpreter-size.txt b/tests/dotnet/UnitTests/expected/TVOS-MonoVM-interpreter-size.txt index 50aaf0f31b2f..aa1d0fe08b6c 100644 --- a/tests/dotnet/UnitTests/expected/TVOS-MonoVM-interpreter-size.txt +++ b/tests/dotnet/UnitTests/expected/TVOS-MonoVM-interpreter-size.txt @@ -1,8 +1,8 @@ -AppBundleSize: 3,616,420 bytes (3,531.7 KB = 3.4 MB) +AppBundleSize: 3,616,438 bytes (3,531.7 KB = 3.4 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: _CodeSignature/CodeResources: 3,999 bytes (3.9 KB = 0.0 MB) archived-expanded-entitlements.xcent: 384 bytes (0.4 KB = 0.0 MB) -Info.plist: 1,120 bytes (1.1 KB = 0.0 MB) +Info.plist: 1,138 bytes (1.1 KB = 0.0 MB) Microsoft.tvOS.dll: 154,624 bytes (151.0 KB = 0.1 MB) PkgInfo: 8 bytes (0.0 KB = 0.0 MB) runtimeconfig.bin: 1,405 bytes (1.4 KB = 0.0 MB) diff --git a/tests/dotnet/UnitTests/expected/TVOS-MonoVM-preservedapis.txt b/tests/dotnet/UnitTests/expected/TVOS-MonoVM-preservedapis.txt index 897cc8c3871e..fef21b016f75 100644 --- a/tests/dotnet/UnitTests/expected/TVOS-MonoVM-preservedapis.txt +++ b/tests/dotnet/UnitTests/expected/TVOS-MonoVM-preservedapis.txt @@ -170,12 +170,9 @@ Microsoft.tvOS.dll:Foundation.NSObject/XamarinGCHandleFlags Foundation.NSObject/ Microsoft.tvOS.dll:Foundation.NSObject/XamarinGCHandleFlags Foundation.NSObject/XamarinGCHandleFlags::InitialSet Microsoft.tvOS.dll:Foundation.NSObject/XamarinGCHandleFlags Foundation.NSObject/XamarinGCHandleFlags::None Microsoft.tvOS.dll:Foundation.NSObjectData -Microsoft.tvOS.dll:Foundation.NSObjectData* Foundation.NSObjectDataHandle::data Microsoft.tvOS.dll:Foundation.NSObjectData* Foundation.NSObjectDataHandle::Data() Microsoft.tvOS.dll:Foundation.NSObjectDataHandle Microsoft.tvOS.dll:Foundation.NSObjectDataHandle..ctor() -Microsoft.tvOS.dll:Foundation.NSObjectDataHandle.CreateHandle(Foundation.NSObject) -Microsoft.tvOS.dll:Foundation.NSObjectDataHandle.Finalize() Microsoft.tvOS.dll:Foundation.NSObjectDataHandle.get_Data() Microsoft.tvOS.dll:Foundation.NSObjectFlag Microsoft.tvOS.dll:Foundation.NSObjectFlag Foundation.NSObjectFlag::Empty @@ -186,6 +183,12 @@ Microsoft.tvOS.dll:Foundation.RegisterAttribute Microsoft.tvOS.dll:Foundation.RegisterAttribute..ctor(System.String, System.Boolean) Microsoft.tvOS.dll:Foundation.RegisterAttribute..ctor(System.String) Microsoft.tvOS.dll:Foundation.RegisterAttribute.get_IsWrapper() +Microsoft.tvOS.dll:Foundation.TrackedMemory +Microsoft.tvOS.dll:Foundation.TrackedMemory..ctor(System.UIntPtr) +Microsoft.tvOS.dll:Foundation.TrackedMemory.CreateHandle(Foundation.NSObject) +Microsoft.tvOS.dll:Foundation.TrackedMemory.Finalize() +Microsoft.tvOS.dll:Foundation.TrackedMemory.get_Value() +Microsoft.tvOS.dll:Foundation.TrackedMemory.set_Value(System.IntPtr) Microsoft.tvOS.dll:Foundation.You_Should_Not_Call_base_In_This_Method Microsoft.tvOS.dll:Foundation.You_Should_Not_Call_base_In_This_Method..ctor() Microsoft.tvOS.dll:ObjCRuntime.__Registrar__ @@ -370,7 +373,6 @@ Microsoft.tvOS.dll:ObjCRuntime.NativeHandle Foundation.NSObject::class_ptr Microsoft.tvOS.dll:ObjCRuntime.NativeHandle Foundation.NSObject::ClassHandle() Microsoft.tvOS.dll:ObjCRuntime.NativeHandle Foundation.NSObject::handle() Microsoft.tvOS.dll:ObjCRuntime.NativeHandle Foundation.NSObject::Handle() -Microsoft.tvOS.dll:ObjCRuntime.NativeHandle Foundation.NSObjectData::classHandle Microsoft.tvOS.dll:ObjCRuntime.NativeHandle Foundation.NSObjectData::handle Microsoft.tvOS.dll:ObjCRuntime.NativeHandle ObjCRuntime.Class::handle Microsoft.tvOS.dll:ObjCRuntime.NativeHandle ObjCRuntime.Class::Handle() @@ -446,7 +448,6 @@ Microsoft.tvOS.dll:ObjCRuntime.Runtime.g__ConstructINati Microsoft.tvOS.dll:ObjCRuntime.Runtime.g__ConstructNSObjectViaFactoryMethod|288_0`1(ObjCRuntime.NativeHandle) Microsoft.tvOS.dll:ObjCRuntime.Runtime.AllocGCHandle(System.Object, System.Runtime.InteropServices.GCHandleType) Microsoft.tvOS.dll:ObjCRuntime.Runtime.AllocGCHandle(System.Object) -Microsoft.tvOS.dll:ObjCRuntime.Runtime.AllocZeroed`1() Microsoft.tvOS.dll:ObjCRuntime.Runtime.AppendAdditionalInformation(System.Text.StringBuilder, System.IntPtr, System.RuntimeMethodHandle) Microsoft.tvOS.dll:ObjCRuntime.Runtime.attempt_retain_nsobject(System.IntPtr, System.IntPtr*) Microsoft.tvOS.dll:ObjCRuntime.Runtime.AttemptRetainNSObject(System.IntPtr) @@ -716,6 +717,8 @@ Microsoft.tvOS.dll:System.IntPtr CoreFoundation.CFRange::len Microsoft.tvOS.dll:System.IntPtr CoreFoundation.CFRange::loc Microsoft.tvOS.dll:System.IntPtr Foundation.NSObject::__data Microsoft.tvOS.dll:System.IntPtr Foundation.NSObject/NSObject_Disposer::class_ptr +Microsoft.tvOS.dll:System.IntPtr Foundation.TrackedMemory::k__BackingField +Microsoft.tvOS.dll:System.IntPtr Foundation.TrackedMemory::Value() Microsoft.tvOS.dll:System.IntPtr ObjCRuntime.BlockCollector::block Microsoft.tvOS.dll:System.IntPtr ObjCRuntime.Libraries/CoreFoundation::Handle Microsoft.tvOS.dll:System.IntPtr ObjCRuntime.NativeHandle::handle @@ -845,9 +848,10 @@ Microsoft.tvOS.dll:System.Object Foundation.NSObject/NSObject_Disposer::lock_obj Microsoft.tvOS.dll:System.Object ObjCRuntime.Runtime::lock_obj Microsoft.tvOS.dll:System.Reflection.Assembly Foundation.NSObject::PlatformAssembly Microsoft.tvOS.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 Foundation.NSObject::data_table +Microsoft.tvOS.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 Foundation.NSObject::super_map Microsoft.tvOS.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 ObjCRuntime.Runtime::block_lifetime_table Microsoft.tvOS.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 ObjCRuntime.Class::assembly_to_name -Microsoft.tvOS.dll:System.Runtime.InteropServices.GCHandle Foundation.NSObjectDataHandle::handle +Microsoft.tvOS.dll:System.Runtime.InteropServices.GCHandle Foundation.TrackedMemory::handle Microsoft.tvOS.dll:System.Runtime.InteropServices.NFloat CoreGraphics.CGRect::height Microsoft.tvOS.dll:System.Runtime.InteropServices.NFloat CoreGraphics.CGRect::width Microsoft.tvOS.dll:System.Runtime.InteropServices.NFloat CoreGraphics.CGRect::x diff --git a/tests/dotnet/UnitTests/expected/TVOS-MonoVM-size.txt b/tests/dotnet/UnitTests/expected/TVOS-MonoVM-size.txt index 756f7510197e..35b977aeb175 100644 --- a/tests/dotnet/UnitTests/expected/TVOS-MonoVM-size.txt +++ b/tests/dotnet/UnitTests/expected/TVOS-MonoVM-size.txt @@ -1,10 +1,10 @@ -AppBundleSize: 9,364,234 bytes (9,144.8 KB = 8.9 MB) +AppBundleSize: 9,364,148 bytes (9,144.7 KB = 8.9 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: _CodeSignature/CodeResources: 5,233 bytes (5.1 KB = 0.0 MB) aot-instances.aotdata.arm64: 827,592 bytes (808.2 KB = 0.8 MB) archived-expanded-entitlements.xcent: 384 bytes (0.4 KB = 0.0 MB) -Info.plist: 1,120 bytes (1.1 KB = 0.0 MB) -Microsoft.tvOS.aotdata.arm64: 22,984 bytes (22.4 KB = 0.0 MB) +Info.plist: 1,138 bytes (1.1 KB = 0.0 MB) +Microsoft.tvOS.aotdata.arm64: 22,880 bytes (22.3 KB = 0.0 MB) Microsoft.tvOS.dll: 48,640 bytes (47.5 KB = 0.0 MB) PkgInfo: 8 bytes (0.0 KB = 0.0 MB) runtimeconfig.bin: 1,481 bytes (1.4 KB = 0.0 MB) diff --git a/tests/dotnet/UnitTests/expected/TVOS-NativeAOT-TrimmableStatic-size.txt b/tests/dotnet/UnitTests/expected/TVOS-NativeAOT-TrimmableStatic-size.txt index ce409263e277..756b1289a4de 100644 --- a/tests/dotnet/UnitTests/expected/TVOS-NativeAOT-TrimmableStatic-size.txt +++ b/tests/dotnet/UnitTests/expected/TVOS-NativeAOT-TrimmableStatic-size.txt @@ -1,8 +1,8 @@ -AppBundleSize: 7,922,022 bytes (7,736.3 KB = 7.6 MB) +AppBundleSize: 7,922,040 bytes (7,736.4 KB = 7.6 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: _CodeSignature/CodeResources: 2,589 bytes (2.5 KB = 0.0 MB) archived-expanded-entitlements.xcent: 384 bytes (0.4 KB = 0.0 MB) -Info.plist: 1,120 bytes (1.1 KB = 0.0 MB) +Info.plist: 1,138 bytes (1.1 KB = 0.0 MB) PkgInfo: 8 bytes (0.0 KB = 0.0 MB) runtimeconfig.bin: 1,889 bytes (1.8 KB = 0.0 MB) SizeTestApp: 7,916,032 bytes (7,730.5 KB = 7.5 MB) diff --git a/tests/dotnet/UnitTests/expected/TVOS-NativeAOT-size.txt b/tests/dotnet/UnitTests/expected/TVOS-NativeAOT-size.txt index 7ede3e0bb7e8..15f36c7f55eb 100644 --- a/tests/dotnet/UnitTests/expected/TVOS-NativeAOT-size.txt +++ b/tests/dotnet/UnitTests/expected/TVOS-NativeAOT-size.txt @@ -1,8 +1,8 @@ -AppBundleSize: 2,783,125 bytes (2,717.9 KB = 2.7 MB) +AppBundleSize: 2,783,159 bytes (2,717.9 KB = 2.7 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: _CodeSignature/CodeResources: 2,589 bytes (2.5 KB = 0.0 MB) archived-expanded-entitlements.xcent: 384 bytes (0.4 KB = 0.0 MB) -Info.plist: 1,120 bytes (1.1 KB = 0.0 MB) +Info.plist: 1,138 bytes (1.1 KB = 0.0 MB) PkgInfo: 8 bytes (0.0 KB = 0.0 MB) runtimeconfig.bin: 1,808 bytes (1.8 KB = 0.0 MB) -SizeTestApp: 2,777,216 bytes (2,712.1 KB = 2.6 MB) +SizeTestApp: 2,777,232 bytes (2,712.1 KB = 2.6 MB) diff --git a/tests/dotnet/UnitTests/expected/iOS-MonoVM-interpreter-preservedapis.txt b/tests/dotnet/UnitTests/expected/iOS-MonoVM-interpreter-preservedapis.txt index c2062123e2fc..0178f1e5c2e3 100644 --- a/tests/dotnet/UnitTests/expected/iOS-MonoVM-interpreter-preservedapis.txt +++ b/tests/dotnet/UnitTests/expected/iOS-MonoVM-interpreter-preservedapis.txt @@ -187,12 +187,9 @@ Microsoft.iOS.dll:Foundation.NSObject/XamarinGCHandleFlags Foundation.NSObject/X Microsoft.iOS.dll:Foundation.NSObject/XamarinGCHandleFlags Foundation.NSObject/XamarinGCHandleFlags::InitialSet Microsoft.iOS.dll:Foundation.NSObject/XamarinGCHandleFlags Foundation.NSObject/XamarinGCHandleFlags::None Microsoft.iOS.dll:Foundation.NSObjectData -Microsoft.iOS.dll:Foundation.NSObjectData* Foundation.NSObjectDataHandle::data Microsoft.iOS.dll:Foundation.NSObjectData* Foundation.NSObjectDataHandle::Data() Microsoft.iOS.dll:Foundation.NSObjectDataHandle Microsoft.iOS.dll:Foundation.NSObjectDataHandle..ctor() -Microsoft.iOS.dll:Foundation.NSObjectDataHandle.CreateHandle(Foundation.NSObject) -Microsoft.iOS.dll:Foundation.NSObjectDataHandle.Finalize() Microsoft.iOS.dll:Foundation.NSObjectDataHandle.get_Data() Microsoft.iOS.dll:Foundation.NSObjectFlag Microsoft.iOS.dll:Foundation.NSObjectFlag Foundation.NSObjectFlag::Empty @@ -244,6 +241,12 @@ Microsoft.iOS.dll:Foundation.RegisterAttribute.get_IsStubClass() Microsoft.iOS.dll:Foundation.RegisterAttribute.get_IsWrapper() Microsoft.iOS.dll:Foundation.RegisterAttribute.get_Name() Microsoft.iOS.dll:Foundation.RegisterAttribute.get_SkipRegistration() +Microsoft.iOS.dll:Foundation.TrackedMemory +Microsoft.iOS.dll:Foundation.TrackedMemory..ctor(System.UIntPtr) +Microsoft.iOS.dll:Foundation.TrackedMemory.CreateHandle(Foundation.NSObject) +Microsoft.iOS.dll:Foundation.TrackedMemory.Finalize() +Microsoft.iOS.dll:Foundation.TrackedMemory.get_Value() +Microsoft.iOS.dll:Foundation.TrackedMemory.set_Value(System.IntPtr) Microsoft.iOS.dll:Foundation.You_Should_Not_Call_base_In_This_Method Microsoft.iOS.dll:Foundation.You_Should_Not_Call_base_In_This_Method..ctor() Microsoft.iOS.dll:ObjCRuntime.__Registrar__ @@ -490,7 +493,6 @@ Microsoft.iOS.dll:ObjCRuntime.NativeHandle Foundation.NSObject::class_ptr Microsoft.iOS.dll:ObjCRuntime.NativeHandle Foundation.NSObject::ClassHandle() Microsoft.iOS.dll:ObjCRuntime.NativeHandle Foundation.NSObject::handle() Microsoft.iOS.dll:ObjCRuntime.NativeHandle Foundation.NSObject::Handle() -Microsoft.iOS.dll:ObjCRuntime.NativeHandle Foundation.NSObjectData::classHandle Microsoft.iOS.dll:ObjCRuntime.NativeHandle Foundation.NSObjectData::handle Microsoft.iOS.dll:ObjCRuntime.NativeHandle Foundation.NSString::class_ptr Microsoft.iOS.dll:ObjCRuntime.NativeHandle Foundation.NSString::ClassHandle() @@ -588,7 +590,6 @@ Microsoft.iOS.dll:ObjCRuntime.Runtime.g__ConstructINativ Microsoft.iOS.dll:ObjCRuntime.Runtime.g__ConstructNSObjectViaFactoryMethod|288_0`1(ObjCRuntime.NativeHandle) Microsoft.iOS.dll:ObjCRuntime.Runtime.AllocGCHandle(System.Object, System.Runtime.InteropServices.GCHandleType) Microsoft.iOS.dll:ObjCRuntime.Runtime.AllocGCHandle(System.Object) -Microsoft.iOS.dll:ObjCRuntime.Runtime.AllocZeroed`1() Microsoft.iOS.dll:ObjCRuntime.Runtime.AppendAdditionalInformation(System.Text.StringBuilder, System.IntPtr, System.RuntimeMethodHandle) Microsoft.iOS.dll:ObjCRuntime.Runtime.attempt_retain_nsobject(System.IntPtr, System.IntPtr*) Microsoft.iOS.dll:ObjCRuntime.Runtime.AttemptRetainNSObject(System.IntPtr) @@ -1324,6 +1325,8 @@ Microsoft.iOS.dll:System.IntPtr CoreFoundation.CFRange::len Microsoft.iOS.dll:System.IntPtr CoreFoundation.CFRange::loc Microsoft.iOS.dll:System.IntPtr Foundation.NSObject::__data Microsoft.iOS.dll:System.IntPtr Foundation.NSObject/NSObject_Disposer::class_ptr +Microsoft.iOS.dll:System.IntPtr Foundation.TrackedMemory::k__BackingField +Microsoft.iOS.dll:System.IntPtr Foundation.TrackedMemory::Value() Microsoft.iOS.dll:System.IntPtr ObjCRuntime.AdoptsAttribute::ProtocolHandle() Microsoft.iOS.dll:System.IntPtr ObjCRuntime.BlockCollector::block Microsoft.iOS.dll:System.IntPtr ObjCRuntime.Libraries/CoreFoundation::Handle @@ -1485,9 +1488,10 @@ Microsoft.iOS.dll:System.Reflection.MethodBase Registrar.Registrar/ObjCMethod::M Microsoft.iOS.dll:System.Reflection.PropertyInfo Registrar.Registrar/ObjCProperty::Property Microsoft.iOS.dll:System.Reflection.TypeFilter Registrar.SharedDynamic/<>c::<>9__0_0 Microsoft.iOS.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 Foundation.NSObject::data_table +Microsoft.iOS.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 Foundation.NSObject::super_map Microsoft.iOS.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 ObjCRuntime.Runtime::block_lifetime_table Microsoft.iOS.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 ObjCRuntime.Class::assembly_to_name -Microsoft.iOS.dll:System.Runtime.InteropServices.GCHandle Foundation.NSObjectDataHandle::handle +Microsoft.iOS.dll:System.Runtime.InteropServices.GCHandle Foundation.TrackedMemory::handle Microsoft.iOS.dll:System.Runtime.InteropServices.NFloat CoreGraphics.CGRect::height Microsoft.iOS.dll:System.Runtime.InteropServices.NFloat CoreGraphics.CGRect::width Microsoft.iOS.dll:System.Runtime.InteropServices.NFloat CoreGraphics.CGRect::x diff --git a/tests/dotnet/UnitTests/expected/iOS-MonoVM-interpreter-size.txt b/tests/dotnet/UnitTests/expected/iOS-MonoVM-interpreter-size.txt index 0c9c253439b5..ebe56d99fd5b 100644 --- a/tests/dotnet/UnitTests/expected/iOS-MonoVM-interpreter-size.txt +++ b/tests/dotnet/UnitTests/expected/iOS-MonoVM-interpreter-size.txt @@ -1,12 +1,12 @@ -AppBundleSize: 3,602,873 bytes (3,518.4 KB = 3.4 MB) +AppBundleSize: 3,616,747 bytes (3,532.0 KB = 3.4 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: _CodeSignature/CodeResources: 3,997 bytes (3.9 KB = 0.0 MB) archived-expanded-entitlements.xcent: 384 bytes (0.4 KB = 0.0 MB) -Info.plist: 1,143 bytes (1.1 KB = 0.0 MB) +Info.plist: 1,161 bytes (1.1 KB = 0.0 MB) Microsoft.iOS.dll: 154,624 bytes (151.0 KB = 0.1 MB) PkgInfo: 8 bytes (0.0 KB = 0.0 MB) runtimeconfig.bin: 1,405 bytes (1.4 KB = 0.0 MB) -SizeTestApp: 2,390,848 bytes (2,334.8 KB = 2.3 MB) +SizeTestApp: 2,404,704 bytes (2,348.3 KB = 2.3 MB) SizeTestApp.dll: 7,680 bytes (7.5 KB = 0.0 MB) System.Private.CoreLib.aotdata.arm64: 41,312 bytes (40.3 KB = 0.0 MB) System.Private.CoreLib.dll: 988,160 bytes (965.0 KB = 0.9 MB) diff --git a/tests/dotnet/UnitTests/expected/iOS-MonoVM-preservedapis.txt b/tests/dotnet/UnitTests/expected/iOS-MonoVM-preservedapis.txt index 0b5ab2d71783..5971b208e5d3 100644 --- a/tests/dotnet/UnitTests/expected/iOS-MonoVM-preservedapis.txt +++ b/tests/dotnet/UnitTests/expected/iOS-MonoVM-preservedapis.txt @@ -170,12 +170,9 @@ Microsoft.iOS.dll:Foundation.NSObject/XamarinGCHandleFlags Foundation.NSObject/X Microsoft.iOS.dll:Foundation.NSObject/XamarinGCHandleFlags Foundation.NSObject/XamarinGCHandleFlags::InitialSet Microsoft.iOS.dll:Foundation.NSObject/XamarinGCHandleFlags Foundation.NSObject/XamarinGCHandleFlags::None Microsoft.iOS.dll:Foundation.NSObjectData -Microsoft.iOS.dll:Foundation.NSObjectData* Foundation.NSObjectDataHandle::data Microsoft.iOS.dll:Foundation.NSObjectData* Foundation.NSObjectDataHandle::Data() Microsoft.iOS.dll:Foundation.NSObjectDataHandle Microsoft.iOS.dll:Foundation.NSObjectDataHandle..ctor() -Microsoft.iOS.dll:Foundation.NSObjectDataHandle.CreateHandle(Foundation.NSObject) -Microsoft.iOS.dll:Foundation.NSObjectDataHandle.Finalize() Microsoft.iOS.dll:Foundation.NSObjectDataHandle.get_Data() Microsoft.iOS.dll:Foundation.NSObjectFlag Microsoft.iOS.dll:Foundation.NSObjectFlag Foundation.NSObjectFlag::Empty @@ -186,6 +183,12 @@ Microsoft.iOS.dll:Foundation.RegisterAttribute Microsoft.iOS.dll:Foundation.RegisterAttribute..ctor(System.String, System.Boolean) Microsoft.iOS.dll:Foundation.RegisterAttribute..ctor(System.String) Microsoft.iOS.dll:Foundation.RegisterAttribute.get_IsWrapper() +Microsoft.iOS.dll:Foundation.TrackedMemory +Microsoft.iOS.dll:Foundation.TrackedMemory..ctor(System.UIntPtr) +Microsoft.iOS.dll:Foundation.TrackedMemory.CreateHandle(Foundation.NSObject) +Microsoft.iOS.dll:Foundation.TrackedMemory.Finalize() +Microsoft.iOS.dll:Foundation.TrackedMemory.get_Value() +Microsoft.iOS.dll:Foundation.TrackedMemory.set_Value(System.IntPtr) Microsoft.iOS.dll:Foundation.You_Should_Not_Call_base_In_This_Method Microsoft.iOS.dll:Foundation.You_Should_Not_Call_base_In_This_Method..ctor() Microsoft.iOS.dll:ObjCRuntime.__Registrar__ @@ -370,7 +373,6 @@ Microsoft.iOS.dll:ObjCRuntime.NativeHandle Foundation.NSObject::class_ptr Microsoft.iOS.dll:ObjCRuntime.NativeHandle Foundation.NSObject::ClassHandle() Microsoft.iOS.dll:ObjCRuntime.NativeHandle Foundation.NSObject::handle() Microsoft.iOS.dll:ObjCRuntime.NativeHandle Foundation.NSObject::Handle() -Microsoft.iOS.dll:ObjCRuntime.NativeHandle Foundation.NSObjectData::classHandle Microsoft.iOS.dll:ObjCRuntime.NativeHandle Foundation.NSObjectData::handle Microsoft.iOS.dll:ObjCRuntime.NativeHandle ObjCRuntime.Class::handle Microsoft.iOS.dll:ObjCRuntime.NativeHandle ObjCRuntime.Class::Handle() @@ -446,7 +448,6 @@ Microsoft.iOS.dll:ObjCRuntime.Runtime.g__ConstructINativ Microsoft.iOS.dll:ObjCRuntime.Runtime.g__ConstructNSObjectViaFactoryMethod|288_0`1(ObjCRuntime.NativeHandle) Microsoft.iOS.dll:ObjCRuntime.Runtime.AllocGCHandle(System.Object, System.Runtime.InteropServices.GCHandleType) Microsoft.iOS.dll:ObjCRuntime.Runtime.AllocGCHandle(System.Object) -Microsoft.iOS.dll:ObjCRuntime.Runtime.AllocZeroed`1() Microsoft.iOS.dll:ObjCRuntime.Runtime.AppendAdditionalInformation(System.Text.StringBuilder, System.IntPtr, System.RuntimeMethodHandle) Microsoft.iOS.dll:ObjCRuntime.Runtime.attempt_retain_nsobject(System.IntPtr, System.IntPtr*) Microsoft.iOS.dll:ObjCRuntime.Runtime.AttemptRetainNSObject(System.IntPtr) @@ -716,6 +717,8 @@ Microsoft.iOS.dll:System.IntPtr CoreFoundation.CFRange::len Microsoft.iOS.dll:System.IntPtr CoreFoundation.CFRange::loc Microsoft.iOS.dll:System.IntPtr Foundation.NSObject::__data Microsoft.iOS.dll:System.IntPtr Foundation.NSObject/NSObject_Disposer::class_ptr +Microsoft.iOS.dll:System.IntPtr Foundation.TrackedMemory::k__BackingField +Microsoft.iOS.dll:System.IntPtr Foundation.TrackedMemory::Value() Microsoft.iOS.dll:System.IntPtr ObjCRuntime.BlockCollector::block Microsoft.iOS.dll:System.IntPtr ObjCRuntime.Libraries/CoreFoundation::Handle Microsoft.iOS.dll:System.IntPtr ObjCRuntime.NativeHandle::handle @@ -845,9 +848,10 @@ Microsoft.iOS.dll:System.Object Foundation.NSObject/NSObject_Disposer::lock_obj Microsoft.iOS.dll:System.Object ObjCRuntime.Runtime::lock_obj Microsoft.iOS.dll:System.Reflection.Assembly Foundation.NSObject::PlatformAssembly Microsoft.iOS.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 Foundation.NSObject::data_table +Microsoft.iOS.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 Foundation.NSObject::super_map Microsoft.iOS.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 ObjCRuntime.Runtime::block_lifetime_table Microsoft.iOS.dll:System.Runtime.CompilerServices.ConditionalWeakTable`2 ObjCRuntime.Class::assembly_to_name -Microsoft.iOS.dll:System.Runtime.InteropServices.GCHandle Foundation.NSObjectDataHandle::handle +Microsoft.iOS.dll:System.Runtime.InteropServices.GCHandle Foundation.TrackedMemory::handle Microsoft.iOS.dll:System.Runtime.InteropServices.NFloat CoreGraphics.CGRect::height Microsoft.iOS.dll:System.Runtime.InteropServices.NFloat CoreGraphics.CGRect::width Microsoft.iOS.dll:System.Runtime.InteropServices.NFloat CoreGraphics.CGRect::x diff --git a/tests/dotnet/UnitTests/expected/iOS-MonoVM-size.txt b/tests/dotnet/UnitTests/expected/iOS-MonoVM-size.txt index 12ad8c361f6a..4b084761bcb9 100644 --- a/tests/dotnet/UnitTests/expected/iOS-MonoVM-size.txt +++ b/tests/dotnet/UnitTests/expected/iOS-MonoVM-size.txt @@ -1,14 +1,14 @@ -AppBundleSize: 9,338,573 bytes (9,119.7 KB = 8.9 MB) +AppBundleSize: 9,380,687 bytes (9,160.8 KB = 8.9 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: _CodeSignature/CodeResources: 5,229 bytes (5.1 KB = 0.0 MB) aot-instances.aotdata.arm64: 827,592 bytes (808.2 KB = 0.8 MB) archived-expanded-entitlements.xcent: 384 bytes (0.4 KB = 0.0 MB) -Info.plist: 1,143 bytes (1.1 KB = 0.0 MB) -Microsoft.iOS.aotdata.arm64: 23,336 bytes (22.8 KB = 0.0 MB) +Info.plist: 1,161 bytes (1.1 KB = 0.0 MB) +Microsoft.iOS.aotdata.arm64: 23,224 bytes (22.7 KB = 0.0 MB) Microsoft.iOS.dll: 48,640 bytes (47.5 KB = 0.0 MB) PkgInfo: 8 bytes (0.0 KB = 0.0 MB) runtimeconfig.bin: 1,481 bytes (1.4 KB = 0.0 MB) -SizeTestApp: 7,235,120 bytes (7,065.5 KB = 6.9 MB) +SizeTestApp: 7,277,328 bytes (7,106.8 KB = 6.9 MB) SizeTestApp.aotdata.arm64: 1,464 bytes (1.4 KB = 0.0 MB) SizeTestApp.dll: 7,680 bytes (7.5 KB = 0.0 MB) System.Private.CoreLib.aotdata.arm64: 640,656 bytes (625.6 KB = 0.6 MB) diff --git a/tests/dotnet/UnitTests/expected/iOS-NativeAOT-TrimmableStatic-size.txt b/tests/dotnet/UnitTests/expected/iOS-NativeAOT-TrimmableStatic-size.txt index c3b634481f8f..5c14be37f20a 100644 --- a/tests/dotnet/UnitTests/expected/iOS-NativeAOT-TrimmableStatic-size.txt +++ b/tests/dotnet/UnitTests/expected/iOS-NativeAOT-TrimmableStatic-size.txt @@ -1,8 +1,8 @@ -AppBundleSize: 9,072,044 bytes (8,859.4 KB = 8.7 MB) +AppBundleSize: 9,124,974 bytes (8,911.1 KB = 8.7 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: _CodeSignature/CodeResources: 2,589 bytes (2.5 KB = 0.0 MB) archived-expanded-entitlements.xcent: 384 bytes (0.4 KB = 0.0 MB) -Info.plist: 1,143 bytes (1.1 KB = 0.0 MB) +Info.plist: 1,161 bytes (1.1 KB = 0.0 MB) PkgInfo: 8 bytes (0.0 KB = 0.0 MB) runtimeconfig.bin: 1,888 bytes (1.8 KB = 0.0 MB) -SizeTestApp: 9,066,032 bytes (8,853.5 KB = 8.6 MB) +SizeTestApp: 9,118,944 bytes (8,905.2 KB = 8.7 MB) diff --git a/tests/dotnet/UnitTests/expected/iOS-NativeAOT-size.txt b/tests/dotnet/UnitTests/expected/iOS-NativeAOT-size.txt index 12ee378aba96..9977114c7ece 100644 --- a/tests/dotnet/UnitTests/expected/iOS-NativeAOT-size.txt +++ b/tests/dotnet/UnitTests/expected/iOS-NativeAOT-size.txt @@ -1,8 +1,8 @@ -AppBundleSize: 2,783,868 bytes (2,718.6 KB = 2.7 MB) +AppBundleSize: 2,800,030 bytes (2,734.4 KB = 2.7 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: _CodeSignature/CodeResources: 2,589 bytes (2.5 KB = 0.0 MB) archived-expanded-entitlements.xcent: 384 bytes (0.4 KB = 0.0 MB) -Info.plist: 1,143 bytes (1.1 KB = 0.0 MB) +Info.plist: 1,161 bytes (1.1 KB = 0.0 MB) PkgInfo: 8 bytes (0.0 KB = 0.0 MB) runtimeconfig.bin: 1,808 bytes (1.8 KB = 0.0 MB) -SizeTestApp: 2,777,936 bytes (2,712.8 KB = 2.6 MB) +SizeTestApp: 2,794,080 bytes (2,728.6 KB = 2.7 MB) From 7717e7b6275597b0b3a028e0e915b9b043af5adc Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 22 May 2026 11:21:56 +0200 Subject: [PATCH 03/79] [mtouch] Make mtouch work without Xcode, since we don't really need Xcode for what mtouch currently does. (#25430) mtouch only needs to know the Xcode version to do what it currently does (create the partial static registrar code during our build), so just pass that instead of the path to Xcode. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tools/common/Application.cs | 8 ++++++++ tools/common/Driver.cs | 12 +++++++++++- tools/common/FileCopier.cs | 2 +- tools/common/PathUtils.cs | 2 +- tools/common/Target.cs | 2 +- tools/mtouch/Makefile | 16 +++++++++++----- tools/mtouch/mtouch.cs | 2 -- 7 files changed, 33 insertions(+), 11 deletions(-) diff --git a/tools/common/Application.cs b/tools/common/Application.cs index 6ee57f4c9b12..56f008d19b66 100644 --- a/tools/common/Application.cs +++ b/tools/common/Application.cs @@ -265,6 +265,14 @@ public bool PackageManagedDebugSymbols { public Version GetMacCatalystiOSVersion (Version macOSVersion) { +#if LEGACY_TOOLS + if (macOSVersion.Major >= 26 && Driver.SdkRoot is null) { + // this shouldn't happen for normal builds, nor for customers, so just show an internal 99 warning. + ErrorHelper.Warning (99, Errors.MX0099, $"No Xcode configured, assuming the macOS version {macOSVersion} is identical to the Mac Catalyst/iOS version."); + return macOSVersion; + } +#endif + if (!MacCatalystSupport.TryGetiOSVersion (Driver.GetFrameworkDirectory (this), macOSVersion, out var value, out var knownMacOSVersions)) throw ErrorHelper.CreateError (184, Errors.MX0184 /* Could not map the macOS version {0} to a corresponding Mac Catalyst version. Valid macOS versions are: {1} */, macOSVersion.ToString (), string.Join (", ", knownMacOSVersions.OrderBy (v => v))); diff --git a/tools/common/Driver.cs b/tools/common/Driver.cs index 50bd8dbd3cce..5ff3ad4d8fe9 100644 --- a/tools/common/Driver.cs +++ b/tools/common/Driver.cs @@ -69,6 +69,11 @@ static void ParseOptions (Application app, Mono.Options.OptionSet options, strin options.Add ("rid=", "The runtime identifier we're building for", v => { app.RuntimeIdentifier = v; }); + options.Add ("xcode-version=", "The Xcode version we're building with", v => { + if (!Version.TryParse (v, out var xcodeVersion)) + throw ErrorHelper.CreateError (26, Errors.MX0026, $"xcode-version:{v}", "Expected a valid version string."); + Driver.XcodeVersion = xcodeVersion; + }); try { app.RootAssemblies.AddRange (options.Parse (args)); @@ -230,7 +235,12 @@ public static string? XcodeProductVersion { static Version? xcode_version; public static Version XcodeVersion { get { - return xcode_version!; + if (xcode_version is null) + throw ErrorHelper.CreateError (99, Errors.MX0099, "The Xcode version has not been configured. Pass --xcode-version or configure an Xcode installation."); + return xcode_version; + } + set { + xcode_version = value; } } diff --git a/tools/common/FileCopier.cs b/tools/common/FileCopier.cs index 98a8e1a27f5d..06d1176453bc 100644 --- a/tools/common/FileCopier.cs +++ b/tools/common/FileCopier.cs @@ -394,7 +394,7 @@ static bool IsUptodate (IEnumerable sources, IEnumerable targets return true; } - [DllImport ("/usr/lib/libSystem.dylib", SetLastError = true, EntryPoint = "strerror")] + [DllImport ("libc", SetLastError = true, EntryPoint = "strerror")] static extern IntPtr _strerror (int errno); internal static string strerror (int errno) diff --git a/tools/common/PathUtils.cs b/tools/common/PathUtils.cs index 19dcc31df8fe..c1675692caef 100644 --- a/tools/common/PathUtils.cs +++ b/tools/common/PathUtils.cs @@ -38,7 +38,7 @@ static char ToOrdinalIgnoreCase (char c) return path; } - [DllImport ("/usr/lib/libc.dylib")] + [DllImport ("libc")] static extern IntPtr realpath (string path, IntPtr buffer); #if NET diff --git a/tools/common/Target.cs b/tools/common/Target.cs index 021457e59cba..5ba0f42feb8a 100644 --- a/tools/common/Target.cs +++ b/tools/common/Target.cs @@ -64,7 +64,7 @@ public void ExtractNativeLinkInfo (List exceptions) } #endif // !LEGACY_TOOLS - [DllImport (Constants.libSystemLibrary, SetLastError = true)] + [DllImport ("libc", SetLastError = true)] static extern string realpath (string path, IntPtr zero); public static string GetRealPath (string path, bool warnIfNoSuchPathExists = true) diff --git a/tools/mtouch/Makefile b/tools/mtouch/Makefile index 0ad70dd239a7..d48cc4bd1eaf 100644 --- a/tools/mtouch/Makefile +++ b/tools/mtouch/Makefile @@ -35,18 +35,21 @@ $(abspath Constants.cs): Constants.cs.in Makefile $(TOP)/Make.config.inc # define RunRegistrar .libs/Microsoft.$(9).registrar.$(10)%m .libs/Microsoft.$(9).registrar.$(10)%h: $(TOP)/src/build/dotnet/$(1)/$(3)/Microsoft.$(9).dll $(LOCAL_MTOUCH) | .libs - $$(Q_GEN) $$(LOCAL_MTOUCH_COMMAND) $$(MTOUCH_VERBOSITY) --runregistrar:$$(abspath $$(basename $$@).m) --sdkroot $$(XCODE_DEVELOPER_ROOT) --sdk $(4) $$< --target-framework .NETCoreApp,Version=$(subst net,,$(DOTNET_TFM)),Profile=$(1) --abi $(2) --reference:$(DOTNET_BCL_DIR)/System.Runtime.dll --reference:$(DOTNET_BCL_DIR)/System.Runtime.InteropServices.dll --rid $(10) + $$(Q_GEN) $$(LOCAL_MTOUCH_COMMAND) $$(MTOUCH_VERBOSITY) --runregistrar:$$(abspath $$(basename $$@).m) --xcode-version $$(XCODE_VERSION) --sdk $(4) $$< --target-framework .NETCoreApp,Version=$(subst net,,$(DOTNET_TFM)),Profile=$(1) --abi $(2) --reference:$(DOTNET_BCL_DIR)/System.Runtime.dll --reference:$(DOTNET_BCL_DIR)/System.Runtime.InteropServices.dll --rid $(10) $$(Q) touch $$(basename $$@).m $$(basename $$@).h .libs/Microsoft.$(9).registrar.$(10).a: .libs/Microsoft.$(9).registrar.$(10).m .libs/Microsoft.$(9).registrar.$(10).h | .libs $$(Q_CC) $$(CLANG) -DDEBUG -g -gdwarf-2 $(6) -stdlib=libc++ -std=c++14 -x objective-c++ -o $$@ -c $$< -Wall -Wno-unguarded-availability-new -I$(TOP)/runtime .libs/Microsoft.$(9).registrar.coreclr.$(10)%m .libs/Microsoft.$(9).registrar.coreclr.$(10)%h: $(TOP)/src/build/dotnet/$(1)/$(3)/Microsoft.$(9).dll $(LOCAL_MTOUCH) | .libs - $$(Q_GEN) $$(LOCAL_MTOUCH_COMMAND) $$(MTOUCH_VERBOSITY) --runregistrar:$$(abspath $$(basename $$@).m) --sdkroot $$(XCODE_DEVELOPER_ROOT) --sdk $(4) $$< --target-framework .NETCoreApp,Version=$(subst net,,$(DOTNET_TFM)),Profile=$(1) --abi $(2) --reference:$(DOTNET_BCL_DIR)/System.Runtime.dll --reference:$(DOTNET_BCL_DIR)/System.Runtime.InteropServices.dll --rid $(10) --xamarin-runtime CoreCLR + $$(Q_GEN) $$(LOCAL_MTOUCH_COMMAND) $$(MTOUCH_VERBOSITY) --runregistrar:$$(abspath $$(basename $$@).m) --xcode-version $$(XCODE_VERSION) --sdk $(4) $$< --target-framework .NETCoreApp,Version=$(subst net,,$(DOTNET_TFM)),Profile=$(1) --abi $(2) --reference:$(DOTNET_BCL_DIR)/System.Runtime.dll --reference:$(DOTNET_BCL_DIR)/System.Runtime.InteropServices.dll --rid $(10) --xamarin-runtime CoreCLR $$(Q) touch $$(basename $$@).m $$(basename $$@).h .libs/Microsoft.$(9).registrar.coreclr.$(10).a: .libs/Microsoft.$(9).registrar.coreclr.$(10).m .libs/Microsoft.$(9).registrar.$(10).h | .libs $$(Q_CC) $$(CLANG) -DDEBUG -g -gdwarf-2 $(6) -stdlib=libc++ -std=c++14 -x objective-c++ -o $$@ -c $$< -Wall -Wno-unguarded-availability-new -I$(TOP)/runtime + +no-xcode-build:: .libs/Microsoft.$(9).registrar.$(10).m .libs/Microsoft.$(9).registrar.$(10).h +no-xcode-build:: .libs/Microsoft.$(9).registrar.coreclr.$(10).m .libs/Microsoft.$(9).registrar.$(10).h endef $(eval $(call RunRegistrar,ios,x86_64,64,$(IOS_SDK_VERSION),iOS,$(iossimulator-x64_CFLAGS),,,iOS,iossimulator-x64)) $(eval $(call RunRegistrar,ios,arm64,64,$(IOS_SDK_VERSION),iOS,$(iossimulator-arm64_CFLAGS),,,iOS,iossimulator-arm64)) @@ -86,12 +89,15 @@ endef $(foreach platform,$(DOTNET_PLATFORMS_MTOUCH),$(foreach rid,$(DOTNET_$(platform)_RUNTIME_IDENTIFIERS),$(eval $(call InstallRegistrar,$(platform),$(rid))))) -ifndef NO_XCODE +ifdef NO_XCODE +dotnet:: no-xcode-build +else dotnet: $(TARGETS_DOTNET) -install-local:: $(TARGETS_DOTNET) -all-local:: $(TARGETS_DOTNET) endif +install-local:: dotnet +all-local:: dotnet + clean-local:: rm -Rf bin obj diff --git a/tools/mtouch/mtouch.cs b/tools/mtouch/mtouch.cs index 21349adc0aaf..42ae6901f1a0 100644 --- a/tools/mtouch/mtouch.cs +++ b/tools/mtouch/mtouch.cs @@ -22,8 +22,6 @@ static int Main2 (string [] args) var os = new OptionSet (); ParseOptions (app, os, args); - ValidateXcode (app, false, false); - app.InitializeCommon (); app.RunRegistrar (); From c56223b22f6b93da9212c06b572e46b8b9dfa076 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 12:18:20 +0000 Subject: [PATCH 04/79] [main] Update dependencies from dotnet/macios (#25499) This pull request updates the following dependencies ## From https://github.com/dotnet/macios - **Subscription**: [c0371266-dd6f-4959-822b-decc72d2d668](https://maestro.dot.net/subscriptions?search=c0371266-dd6f-4959-822b-decc72d2d668) - **Build**: [20260521.4](https://dev.azure.com/devdiv/DevDiv/_build/results?buildId=14158846) ([315351](https://maestro.dot.net/channel/3884/github:dotnet:macios/build/315351)) - **Date Produced**: May 21, 2026 2:11:24 PM UTC - **Commit**: [5a29bbfbaac5941a8b229c91f1b606081b49ea10](https://github.com/dotnet/macios/commit/5a29bbfbaac5941a8b229c91f1b606081b49ea10) - **Branch**: [release/9.0.1xx](https://github.com/dotnet/macios/tree/release/9.0.1xx) - **Dependency Updates**: - From [26.5.9002 to 26.5.9003][1] - Microsoft.iOS.Sdk.net9.0_26.5 - Microsoft.MacCatalyst.Sdk.net9.0_26.5 - Microsoft.macOS.Sdk.net9.0_26.5 - Microsoft.tvOS.Sdk.net9.0_26.5 [1]: https://github.com/dotnet/macios/compare/7075bb3cf9...5a29bbfbaa --- NuGet.config | 3 +-- eng/Version.Details.props | 8 ++++---- eng/Version.Details.xml | 16 ++++++++-------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/NuGet.config b/NuGet.config index 03a2d9eabbfd..31b55049d0f8 100644 --- a/NuGet.config +++ b/NuGet.config @@ -13,8 +13,7 @@ - - + diff --git a/eng/Version.Details.props b/eng/Version.Details.props index b44948c83ce7..2580fca25dba 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -19,16 +19,16 @@ This file should be imported by eng/Versions.props 26.0.11017 18.5.9227 - 26.5.9002 + 26.5.9003 26.0.11017 18.5.9227 - 26.5.9002 + 26.5.9003 26.0.11017 15.5.9227 - 26.5.9002 + 26.5.9003 26.0.11017 18.5.9227 - 26.5.9002 + 26.5.9003 11.0.0-prerelease.26264.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index c23401f2a72e..21e29be673c0 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -60,21 +60,21 @@ 797d30720e5e629d23eb146935da94cb1b61047e - + https://github.com/dotnet/macios - 7075bb3cf977326a4e0a01108b748478ce08091e + 5a29bbfbaac5941a8b229c91f1b606081b49ea10 - + https://github.com/dotnet/macios - 7075bb3cf977326a4e0a01108b748478ce08091e + 5a29bbfbaac5941a8b229c91f1b606081b49ea10 - + https://github.com/dotnet/macios - 7075bb3cf977326a4e0a01108b748478ce08091e + 5a29bbfbaac5941a8b229c91f1b606081b49ea10 - + https://github.com/dotnet/macios - 7075bb3cf977326a4e0a01108b748478ce08091e + 5a29bbfbaac5941a8b229c91f1b606081b49ea10 From 97204e747fcc6d5a9ccc55957b90b330aa92bf44 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 22 May 2026 16:11:37 +0200 Subject: [PATCH 05/79] Fix 'dotnet checkout' typos in docs/guides/HowToBranch.md (#25500) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Lines 23-24 of `docs/guides/HowToBranch.md` incorrectly use `dotnet checkout` instead of `git checkout`. The `dotnet` CLI has no `checkout` subcommand, so anyone following the guide literally would get an error. Later commands in the same document correctly use `git`, confirming this was a typo. ## Changes - Line 23: `$ dotnet checkout net10.0` → `$ git checkout net10.0` - Line 24: `$ dotnet checkout -b release/10.0.1xx-preview42` → `$ git checkout -b release/10.0.1xx-preview42` Markdown-only change — no build or test impact. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/guides/HowToBranch.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/HowToBranch.md b/docs/guides/HowToBranch.md index ec95a74c03aa..46048712427c 100644 --- a/docs/guides/HowToBranch.md +++ b/docs/guides/HowToBranch.md @@ -20,8 +20,8 @@ sequence of events would be: 3. `dotnet/macios` branches `release/10.0.1xx-preview42` from `net10.0`: ```shell - $ dotnet checkout net10.0 - $ dotnet checkout -b release/10.0.1xx-preview42 + $ git checkout net10.0 + $ git checkout -b release/10.0.1xx-preview42 ``` Note that release candidates will use values such as `rc.1`, `rc.2`, etc. From 3c6edceb8270b34f44f7f21b93bcfd7430a5f27e Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 22 May 2026 17:08:42 +0200 Subject: [PATCH 06/79] [tests] Upgrade remaining tests to NUnit v4 Assert.That syntax. (#25501) Migrate all remaining classic assertions (AreEqual, IsTrue, IsNull, etc.) to Assert.That constraint syntax. --- Directory.Build.props | 2 + tests/BundledResources/ResourcesTest.cs | 6 +- tests/EmbeddedResources/ResourcesTest.cs | 14 +- tests/bindings-test/ProtocolTest.cs | 90 +++--- tests/bindings-test/RegistrarBindingTest.cs | 36 +-- tests/bindings-test/RuntimeTest.cs | 16 +- tests/bindings-test2/BindingTest.cs | 4 +- tests/common/Assert.cs | 2 +- tests/common/AssertHelpers.cs | 2 +- tests/common/BundlerTool.cs | 2 +- tests/common/ProductTests.cs | 4 +- tests/common/TestRuntime.cs | 4 +- .../Touch.Client/Runner/TestResultElement.cs | 2 +- .../Touch.Client/Runner/TouchRunner.cs | 12 +- .../Touch.Client/dotnet/shared.csproj | 4 +- tests/common/shared-dotnet.csproj | 2 +- tests/framework-test/FrameworkTests.cs | 8 +- tests/fsharp/FSharpTests.fs | 2 +- tests/interdependent-binding-projects/Main.cs | 2 +- tests/test-libraries/testgenerator.cs | 274 +++++++++--------- tests/xcframework-test/XCFrameworkTests.cs | 4 +- tests/xtro-sharpie/UnitTests/UnitTests.csproj | 6 +- tests/xtro-sharpie/UnitTests/Xtro.cs | 2 +- 23 files changed, 252 insertions(+), 248 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index d4a1086d9d07..f054d696a5d3 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -22,6 +22,8 @@ 6.1.0 4.7.0 4.4.0 + 3.12.0 + 3.6.0 latest diff --git a/tests/BundledResources/ResourcesTest.cs b/tests/BundledResources/ResourcesTest.cs index e22cdc7ec85e..573102ee7133 100644 --- a/tests/BundledResources/ResourcesTest.cs +++ b/tests/BundledResources/ResourcesTest.cs @@ -22,9 +22,9 @@ public void Bundled () // files are extracted (by MonoDevelop) so we can see them in the file system // that's true for simulator or devices and whatever the linker settings are var dir = NSBundle.MainBundle.ResourcePath!; - Assert.True (File.Exists (Path.Combine (dir, "basn3p08.png")), "file-basn3p08.png"); - Assert.True (File.Exists (Path.Combine (dir, "basn3p08_with_loc.png")), "file-basn3p08_with_loc.png"); - Assert.True (File.Exists (Path.Combine (dir, "xamvideotest.mp4")), "xamvideotest.mp4"); + Assert.That (File.Exists (Path.Combine (dir, "basn3p08.png")), Is.True, "file-basn3p08.png"); + Assert.That (File.Exists (Path.Combine (dir, "basn3p08_with_loc.png")), Is.True, "file-basn3p08_with_loc.png"); + Assert.That (File.Exists (Path.Combine (dir, "xamvideotest.mp4")), Is.True, "xamvideotest.mp4"); // resources are removed by the linker or an extra step (e.g. "link sdk" or "don't link") but that // extra step is done only on device (to keep the simulator builds as fast as possible) diff --git a/tests/EmbeddedResources/ResourcesTest.cs b/tests/EmbeddedResources/ResourcesTest.cs index 04f35a6856e8..e8c83f5d9b08 100644 --- a/tests/EmbeddedResources/ResourcesTest.cs +++ b/tests/EmbeddedResources/ResourcesTest.cs @@ -23,13 +23,13 @@ public class ResourcesTest { public void Embedded () { var manager = new ResourceManager ("EmbeddedResources.Welcome", typeof (ResourcesTest).Assembly); - Assert.AreEqual ("Welcome", manager.GetString ("String1", new CultureInfo ("en")), "en"); - Assert.AreEqual ("G'day", manager.GetString ("String1", new CultureInfo ("en-AU")), "en-AU"); - Assert.AreEqual ("Willkommen", manager.GetString ("String1", new CultureInfo ("de")), "de"); - Assert.AreEqual ("Willkommen", manager.GetString ("String1", new CultureInfo ("de-DE")), "de-DE"); - Assert.AreEqual ("Bienvenido", manager.GetString ("String1", new CultureInfo ("es")), "es"); - Assert.AreEqual ("Bienvenido", manager.GetString ("String1", new CultureInfo ("es-AR")), "es-AR"); - Assert.AreEqual ("Bienvenido", manager.GetString ("String1", new CultureInfo ("es-ES")), "es-ES"); + Assert.That (manager.GetString ("String1", new CultureInfo ("en")), Is.EqualTo ("Welcome"), "en"); + Assert.That (manager.GetString ("String1", new CultureInfo ("en-AU")), Is.EqualTo ("G'day"), "en-AU"); + Assert.That (manager.GetString ("String1", new CultureInfo ("de")), Is.EqualTo ("Willkommen"), "de"); + Assert.That (manager.GetString ("String1", new CultureInfo ("de-DE")), Is.EqualTo ("Willkommen"), "de-DE"); + Assert.That (manager.GetString ("String1", new CultureInfo ("es")), Is.EqualTo ("Bienvenido"), "es"); + Assert.That (manager.GetString ("String1", new CultureInfo ("es-AR")), Is.EqualTo ("Bienvenido"), "es-AR"); + Assert.That (manager.GetString ("String1", new CultureInfo ("es-ES")), Is.EqualTo ("Bienvenido"), "es-ES"); } } } diff --git a/tests/bindings-test/ProtocolTest.cs b/tests/bindings-test/ProtocolTest.cs index b9d2ae0495e9..f94355ccc85f 100644 --- a/tests/bindings-test/ProtocolTest.cs +++ b/tests/bindings-test/ProtocolTest.cs @@ -46,23 +46,23 @@ public void Constructors () using var dateNow = (NSDate) DateTime.Now; using (var obj = IConstructorProtocol.CreateInstance ("Hello world")!) { - Assert.AreEqual ("Hello world", obj.StringValue, "A StringValue"); - Assert.IsNull (obj.DateValue, "A DateValue"); + Assert.That (obj.StringValue, Is.EqualTo ("Hello world"), "A StringValue"); + Assert.That (obj.DateValue, Is.Null, "A DateValue"); } using (var obj = IConstructorProtocol.CreateInstance (dateNow)!) { - Assert.IsNull (obj.StringValue, "B StringValue"); - Assert.AreEqual (dateNow, obj.DateValue, "B DateValue"); + Assert.That (obj.StringValue, Is.Null, "B StringValue"); + Assert.That (obj.DateValue, Is.EqualTo (dateNow), "B DateValue"); } using (var obj = IConstructorProtocol.CreateInstance ("Hello Subclassed")!) { - Assert.AreEqual ("Hello Subclassed", obj.StringValue, "C1 StringValue"); - Assert.IsNull (obj.DateValue, "C1 DateValue"); + Assert.That (obj.StringValue, Is.EqualTo ("Hello Subclassed"), "C1 StringValue"); + Assert.That (obj.DateValue, Is.Null, "C1 DateValue"); } using (var obj = IConstructorProtocol.CreateInstance (dateNow)!) { - Assert.IsNull (obj.StringValue, "C2 StringValue"); - Assert.AreEqual (dateNow, obj.DateValue, "C2 DateValue"); + Assert.That (obj.StringValue, Is.Null, "C2 StringValue"); + Assert.That (obj.DateValue, Is.EqualTo (dateNow), "C2 DateValue"); } if (global::XamarinTests.ObjCRuntime.Registrar.IsDynamicRegistrar) { @@ -71,8 +71,8 @@ public void Constructors () }, "D1 Exception"); } else { using (var obj = IConstructorProtocol.CreateInstance ("Hello Subclassed 2")!) { - Assert.AreEqual ("Managed interceptor! Hello Subclassed 2", obj.StringValue, "D1 StringValue"); - Assert.IsNull (obj.DateValue, "D1 DateValue"); + Assert.That (obj.StringValue, Is.EqualTo ("Managed interceptor! Hello Subclassed 2"), "D1 StringValue"); + Assert.That (obj.DateValue, Is.Null, "D1 DateValue"); } } @@ -82,8 +82,8 @@ public void Constructors () }, "D2 Exception"); } else { using (var obj = IConstructorProtocol.CreateInstance (dateNow)!) { - Assert.IsNull (obj.StringValue, "D2 StringValue"); - Assert.AreEqual (dateNow.AddSeconds (42), obj.DateValue, "D2 DateValue"); + Assert.That (obj.StringValue, Is.Null, "D2 StringValue"); + Assert.That (obj.DateValue, Is.EqualTo (dateNow.AddSeconds (42)), "D2 DateValue"); } } } @@ -118,27 +118,27 @@ public void OnlyProtocol () // the interface must be created var IP1 = bindingAssembly.GetType ("Bindings.Test.Protocol.IP1")!; - Assert.IsNotNull (IP1, "IP1"); + Assert.That (IP1, Is.Not.Null, "IP1"); // with a [Protocol] attribute var IP1Attributes = IP1.GetCustomAttributes (typeof (ProtocolAttribute), false); if (HasProtocolAttributes) { - Assert.AreEqual (1, IP1Attributes.Length, "[Protocol] IP1"); + Assert.That (IP1Attributes.Length, Is.EqualTo (1), "[Protocol] IP1"); var IP1Protocol = (ProtocolAttribute) IP1Attributes [0]; - Assert.AreEqual ("P1", IP1Protocol.Name, "Name"); + Assert.That (IP1Protocol.Name, Is.EqualTo ("P1"), "Name"); // and a wrapper type var wrapperType = bindingAssembly.GetType ("Bindings.Test.Protocol.P1Wrapper"); - Assert.IsNotNull (wrapperType, "P1_Wrapper"); - Assert.AreEqual (wrapperType, IP1Protocol.WrapperType, "WrapperType"); + Assert.That (wrapperType, Is.Not.Null, "P1_Wrapper"); + Assert.That (IP1Protocol.WrapperType, Is.EqualTo (wrapperType), "WrapperType"); } else { - Assert.AreEqual (0, IP1Attributes.Length, "[Protocol] IP1"); + Assert.That (IP1Attributes.Length, Is.EqualTo (0), "[Protocol] IP1"); // and a wrapper type var wrapperType = bindingAssembly.GetType ("Bindings.Test.Protocol.P1Wrapper"); - Assert.IsNotNull (wrapperType, "P1_Wrapper"); + Assert.That (wrapperType, Is.Not.Null, "P1_Wrapper"); } // but not the model - Assert.IsNull (bindingAssembly.GetType ("Bindings.Test.Protocol.P1"), "P1"); + Assert.That (bindingAssembly.GetType ("Bindings.Test.Protocol.P1"), Is.Null, "P1"); } [Test] @@ -150,32 +150,32 @@ public void ProtocolWithBaseType () // the interface must be created var IP2 = bindingAssembly.GetType ("Bindings.Test.Protocol.IP2")!; - Assert.IsNotNull (IP2, "IP2"); + Assert.That (IP2, Is.Not.Null, "IP2"); // with a [Protocol] attribute var IP2Attributes = IP2.GetCustomAttributes (typeof (ProtocolAttribute), false); if (HasProtocolAttributes) { - Assert.AreEqual (1, IP2Attributes.Length, "[Protocol] IP2"); + Assert.That (IP2Attributes.Length, Is.EqualTo (1), "[Protocol] IP2"); var IP2Protocol = (ProtocolAttribute) IP2Attributes [0]; - Assert.AreEqual ("P2", IP2Protocol.Name, "Name"); + Assert.That (IP2Protocol.Name, Is.EqualTo ("P2"), "Name"); // and a wrapper type var wrapperType = bindingAssembly.GetType ("Bindings.Test.Protocol.P2Wrapper"); - Assert.IsNotNull (wrapperType, "P2_Wrapper"); - Assert.AreEqual (wrapperType, IP2Protocol.WrapperType, "WrapperType"); + Assert.That (wrapperType, Is.Not.Null, "P2_Wrapper"); + Assert.That (IP2Protocol.WrapperType, Is.EqualTo (wrapperType), "WrapperType"); } else { - Assert.AreEqual (0, IP2Attributes.Length, "[Protocol] IP2"); + Assert.That (IP2Attributes.Length, Is.EqualTo (0), "[Protocol] IP2"); // and a wrapper type var wrapperType = bindingAssembly.GetType ("Bindings.Test.Protocol.P2Wrapper"); - Assert.IsNotNull (wrapperType, "P2_Wrapper"); + Assert.That (wrapperType, Is.Not.Null, "P2_Wrapper"); } // and a model-like class var model = bindingAssembly.GetType ("Bindings.Test.Protocol.P2")!; - Assert.IsNotNull (model, "P2"); + Assert.That (model, Is.Not.Null, "P2"); // but without the [Model] attribute - Assert.False (model.IsDefined (typeof (ModelAttribute), false), "model"); + Assert.That (model.IsDefined (typeof (ModelAttribute), false), Is.False, "model"); } [Test] @@ -187,32 +187,32 @@ public void ProtocolWithBaseTypeAndModel () // the interface must be created var IP3 = bindingAssembly.GetType ("Bindings.Test.Protocol.IP3")!; - Assert.IsNotNull (IP3, "IP3"); + Assert.That (IP3, Is.Not.Null, "IP3"); // with a [Protocol] attribute var IP3Attributes = IP3.GetCustomAttributes (typeof (ProtocolAttribute), false); if (HasProtocolAttributes) { - Assert.AreEqual (1, IP3Attributes.Length, "[Protocol] IP3"); + Assert.That (IP3Attributes.Length, Is.EqualTo (1), "[Protocol] IP3"); var IP3Protocol = (ProtocolAttribute) IP3Attributes [0]; - Assert.AreEqual ("P3", IP3Protocol.Name, "Name"); + Assert.That (IP3Protocol.Name, Is.EqualTo ("P3"), "Name"); // and a wrapper type var wrapperType = bindingAssembly.GetType ("Bindings.Test.Protocol.P3Wrapper"); - Assert.IsNotNull (wrapperType, "P3_Wrapper"); - Assert.AreEqual (wrapperType, IP3Protocol.WrapperType, "WrapperType"); + Assert.That (wrapperType, Is.Not.Null, "P3_Wrapper"); + Assert.That (IP3Protocol.WrapperType, Is.EqualTo (wrapperType), "WrapperType"); } else { - Assert.AreEqual (0, IP3Attributes.Length, "[Protocol] IP3"); + Assert.That (IP3Attributes.Length, Is.EqualTo (0), "[Protocol] IP3"); // and a wrapper type var wrapperType = bindingAssembly.GetType ("Bindings.Test.Protocol.P3Wrapper"); - Assert.IsNotNull (wrapperType, "P3_Wrapper"); + Assert.That (wrapperType, Is.Not.Null, "P3_Wrapper"); } // and a model class var model = bindingAssembly.GetType ("Bindings.Test.Protocol.P3")!; - Assert.IsNotNull (model, "P3"); + Assert.That (model, Is.Not.Null, "P3"); // with a [Model] attribute - Assert.True (model.IsDefined (typeof (ModelAttribute), false), "model"); + Assert.That (model.IsDefined (typeof (ModelAttribute), false), Is.True, "model"); } class MembersImplementation : NSObject, Bindings.Test.Protocol.IMemberAttributes { @@ -241,14 +241,14 @@ void CleanupSignatures (objc_method_description [] methods) public void ProtocolMembers () { IntPtr protocol = objc_getProtocol ("MemberAttributes"); - Assert.AreNotEqual (IntPtr.Zero, protocol, "a"); + Assert.That (protocol, Is.Not.EqualTo (IntPtr.Zero), "a"); objc_method_description [] methods; // Required instance methods methods = protocol_copyMethodDescriptionList (protocol, true, true); CleanupSignatures (methods); - Assert.AreEqual (4, methods.Length, "Required Instance Methods: Count"); + Assert.That (methods.Length, Is.EqualTo (4), "Required Instance Methods: Count"); AssertContains (methods, new objc_method_description ("requiredInstanceMethod", "v@:"), "Required Instance Methods: requiredInstanceMethod"); AssertContains (methods, new objc_method_description ("requiredInstanceProperty", "@@:"), "Required Instance Methods: requiredInstanceProperty"); AssertContains (methods, new objc_method_description ("setRequiredInstanceProperty:", "v@:@"), "Required Instance Methods: setRequiredInstanceProperty"); @@ -257,7 +257,7 @@ public void ProtocolMembers () // Required static methods methods = protocol_copyMethodDescriptionList (protocol, true, false); CleanupSignatures (methods); - Assert.AreEqual (3, methods.Length, "Required Static Methods: Count"); + Assert.That (methods.Length, Is.EqualTo (3), "Required Static Methods: Count"); AssertContains (methods, new objc_method_description ("requiredStaticMethod", "v@:"), "Required Static Methods: requiredStaticMethod"); AssertContains (methods, new objc_method_description ("setRequiredStaticProperty:", "v@:@"), "Required Static Methods: setRequiredStaticProperty:"); AssertContains (methods, new objc_method_description ("requiredStaticProperty", "@@:"), "Required Static Methods: requiredStaticProperty"); @@ -265,7 +265,7 @@ public void ProtocolMembers () // Optional instance methods methods = protocol_copyMethodDescriptionList (protocol, false, true); CleanupSignatures (methods); - Assert.AreEqual (19, methods.Length, "Optional Instance Methods: Count"); + Assert.That (methods.Length, Is.EqualTo (19), "Optional Instance Methods: Count"); AssertContains (methods, new objc_method_description ("variadicMethod:", "v@:^v"), "Optional Instance Methods: variadicMethod:"); AssertContains (methods, new objc_method_description ("methodWithReturnType", "@@:"), "Optional Instance Methods: methodWithReturnType"); AssertContains (methods, new objc_method_description ("methodWithParameter:", "v@:i"), "Optional Instance Methods: methodWithParameter:"); @@ -289,7 +289,7 @@ public void ProtocolMembers () // Optional static methods methods = protocol_copyMethodDescriptionList (protocol, false, false); CleanupSignatures (methods); - Assert.AreEqual (3, methods.Length, "Optional Static Methods: Count"); + Assert.That (methods.Length, Is.EqualTo (3), "Optional Static Methods: Count"); AssertContains (methods, new objc_method_description ("optionalStaticMethod", "v@:"), "Optional Static Methods: optionalStaticMethod"); AssertContains (methods, new objc_method_description ("optionalStaticProperty", "@@:"), "Optional Static Methods: optionalStaticProperty"); AssertContains (methods, new objc_method_description ("setOptionalStaticProperty:", "v@:@"), "Optional Static Methods: setOptionalStaticProperty:"); @@ -301,9 +301,9 @@ public void ProtocolMembers () // see file objc4-647/runtime/objc-runtime-old.mm in Apple's open source code), // so we need to verify differently for the dynamic registrar. if (XamarinTests.ObjCRuntime.Registrar.IsStaticRegistrar) { - Assert.AreEqual (9, properties.Length, "Properties: Count"); + Assert.That (properties.Length, Is.EqualTo (9), "Properties: Count"); } else { - Assert.AreEqual (2, properties.Length, "Properties: Count"); + Assert.That (properties.Length, Is.EqualTo (2), "Properties: Count"); } AssertContains (properties, new objc_property ("requiredInstanceProperty", "T@\"NSString\",N", new objc_property_attribute [] { diff --git a/tests/bindings-test/RegistrarBindingTest.cs b/tests/bindings-test/RegistrarBindingTest.cs index 93bba2d9c9e2..fb3cf6821c83 100644 --- a/tests/bindings-test/RegistrarBindingTest.cs +++ b/tests/bindings-test/RegistrarBindingTest.cs @@ -57,7 +57,7 @@ public static void OptionalStaticCallback (Action completionHandler) public Action RequiredReturnValue () { return new Action ((v) => { - Assert.AreEqual (42, v, "RequiredReturnValue"); + Assert.That (v, Is.EqualTo (42), "RequiredReturnValue"); }); } @@ -65,7 +65,7 @@ public Action RequiredReturnValue () public Action OptionalReturnValue () { return new Action ((v) => { - Assert.AreEqual (42, v, "RequiredReturnValue"); + Assert.That (v, Is.EqualTo (42), "RequiredReturnValue"); }); } @@ -73,7 +73,7 @@ public Action OptionalReturnValue () public static Action RequiredStaticReturnValue () { return new Action ((v) => { - Assert.AreEqual (42, v, "RequiredReturnValue"); + Assert.That (v, Is.EqualTo (42), "RequiredReturnValue"); }); } @@ -81,7 +81,7 @@ public static Action RequiredStaticReturnValue () public static Action OptionalStaticReturnValue () { return new Action ((v) => { - Assert.AreEqual (42, v, "RequiredReturnValue"); + Assert.That (v, Is.EqualTo (42), "RequiredReturnValue"); }); } } @@ -99,10 +99,10 @@ public void DerivedClassBlockCallback () ObjCBlockTester.CallOptionalStaticCallback (); DerivedBlockCallbackClass.Answer = 2; - Assert.IsFalse (obj.InvokeNullableCallbackNatively (null), "NullableCallback A rv"); + Assert.That (obj.InvokeNullableCallbackNatively (null), Is.False, "NullableCallback A rv"); int nullableResult = -1; - Assert.IsTrue (obj.InvokeNullableCallbackNatively ((v) => nullableResult = v), "NullableCallback B rv"); - Assert.AreEqual (24, nullableResult, "NullableCallback result"); + Assert.That (obj.InvokeNullableCallbackNatively ((v) => nullableResult = v), Is.True, "NullableCallback B rv"); + Assert.That (nullableResult, Is.EqualTo (24), "NullableCallback result"); } } @@ -141,7 +141,7 @@ public static void OptionalStaticCallback (Action completionHandler) public override Action RequiredReturnValue () { return new Action ((v) => { - Assert.AreEqual (Answer, v, "RequiredReturnValue"); + Assert.That (v, Is.EqualTo (Answer), "RequiredReturnValue"); }); } @@ -150,7 +150,7 @@ public Action OptionalReturnValue () { return new Action ((v) => { Console.WriteLine ("OptionalReturnValue"); - Assert.AreEqual (Answer, v, "RequiredReturnValue"); + Assert.That (v, Is.EqualTo (Answer), "RequiredReturnValue"); }); } @@ -158,7 +158,7 @@ public Action OptionalReturnValue () public static Action RequiredStaticReturnValue () { return new Action ((v) => { - Assert.AreEqual (Answer, v, "RequiredReturnValue"); + Assert.That (v, Is.EqualTo (Answer), "RequiredReturnValue"); }); } @@ -167,7 +167,7 @@ public static Action OptionalStaticReturnValue () { return new Action ((v) => { Console.WriteLine ("OptionalStaticReturnValue"); - Assert.AreEqual (Answer, v, "RequiredReturnValue"); + Assert.That (v, Is.EqualTo (Answer), "RequiredReturnValue"); }); } } @@ -201,7 +201,7 @@ public static void OptionalRequiredCallback (Action completionHandler) Action IObjCProtocolBlockTest.RequiredReturnValue () { return new Action ((v) => { - Assert.AreEqual (42, v, "RequiredReturnValue"); + Assert.That (v, Is.EqualTo (42), "RequiredReturnValue"); }); } @@ -209,7 +209,7 @@ Action IObjCProtocolBlockTest.RequiredReturnValue () public Action OptionalReturnValue () { return new Action ((v) => { - Assert.AreEqual (42, v, "RequiredReturnValue"); + Assert.That (v, Is.EqualTo (42), "RequiredReturnValue"); }); } @@ -217,7 +217,7 @@ public Action OptionalReturnValue () public static Action RequiredStaticReturnValue () { return new Action ((v) => { - Assert.AreEqual (42, v, "RequiredReturnValue"); + Assert.That (v, Is.EqualTo (42), "RequiredReturnValue"); }); } @@ -225,7 +225,7 @@ public static Action RequiredStaticReturnValue () public static Action OptionalStaticReturnValue () { return new Action ((v) => { - Assert.AreEqual (42, v, "RequiredReturnValue"); + Assert.That (v, Is.EqualTo (42), "RequiredReturnValue"); }); } } @@ -311,7 +311,7 @@ public void ProtocolWithBlockProperties (bool required, bool instance) } } ObjCBlockTester.CallProtocolWithBlockProperties (pb, required, instance); - Assert.IsTrue (callbackCalled, "Callback"); + Assert.That (callbackCalled, Is.True, "Callback"); } } @@ -338,7 +338,7 @@ public void ProtocolWithNativeBlockProperties (bool required, bool instance) PropertyBlock.MyOptionalStaticProperty! (); } } - Assert.AreEqual (calledCounter + 1, ObjCBlockTester.CalledBlockCount, "Blocks called"); + Assert.That (ObjCBlockTester.CalledBlockCount, Is.EqualTo (calledCounter + 1), "Blocks called"); } } [Test] @@ -372,7 +372,7 @@ public void LinkedAway (bool required, bool instance) if (re.Code == 8009) { Assert.That (re.Message, Does.StartWith ("Unable to locate the block to delegate conversion method for the method Xamarin.BindingTests.RegistrarBindingTest+FakePropertyBlock.set_"), re.Message, "Message"); } else { - Assert.AreEqual ("The runtime function get_block_wrapper_creator has been linked away.", re.Message, "Message"); + Assert.That (re.Message, Is.EqualTo ("The runtime function get_block_wrapper_creator has been linked away."), "Message"); } } } diff --git a/tests/bindings-test/RuntimeTest.cs b/tests/bindings-test/RuntimeTest.cs index aea647585f60..f445ec7bca9e 100644 --- a/tests/bindings-test/RuntimeTest.cs +++ b/tests/bindings-test/RuntimeTest.cs @@ -9,7 +9,7 @@ public class RuntimeTest { [Test] public void GlobalStringTest () { - Assert.AreEqual ("There's nothing cruvus here!", (string) Globals.GlobalString, "Global string"); + Assert.That ((string) Globals.GlobalString, Is.EqualTo ("There's nothing cruvus here!"), "Global string"); } [Test] @@ -95,7 +95,7 @@ public void SwiftTest () { TestRuntime.AssertXcodeVersion (13, 0); using var obj = new SwiftTestClass (); - Assert.AreEqual ("Hello from Swift", obj.SayHello (), "Hello"); + Assert.That (obj.SayHello (), Is.EqualTo ("Hello from Swift"), "Hello"); } [Test] @@ -105,18 +105,18 @@ public void SwiftTypeEncodings () using var obj = new SwiftTestClass (); - Assert.AreEqual ("42", obj.DoSomething ("42"), "DoSomething"); + Assert.That (obj.DoSomething ("42"), Is.EqualTo ("42"), "DoSomething"); string? asyncResult = null; obj.DoSomethingAsync ("dolphins", (v) => asyncResult = v); var done = TestRuntime.RunAsync (TimeSpan.FromSeconds (5), () => asyncResult is not null); - Assert.AreEqual ("dolphins", asyncResult, "DoSomethingAsync"); - Assert.IsTrue (done, "Done"); + Assert.That (asyncResult, Is.EqualTo ("dolphins"), "DoSomethingAsync"); + Assert.That (done, Is.True, "Done"); obj.DoSomethingComplexAsync ("fish", IntPtr.Zero, (v) => asyncResult = v); done = TestRuntime.RunAsync (TimeSpan.FromSeconds (5), () => asyncResult is not null); - Assert.AreEqual ("fish", asyncResult, "DoSomethingComplexAsync"); - Assert.IsTrue (done, "Done 2"); + Assert.That (asyncResult, Is.EqualTo ("fish"), "DoSomethingComplexAsync"); + Assert.That (done, Is.True, "Done 2"); } [Test] @@ -124,7 +124,7 @@ public void SwiftTestClass2 () { TestRuntime.AssertXcodeVersion (13, 0); using var obj = new SwiftTestClass2 (); - Assert.AreEqual ("Hello from Swift 2", obj.SayHello2 (), "Hello"); + Assert.That (obj.SayHello2 (), Is.EqualTo ("Hello from Swift 2"), "Hello"); } [Test] diff --git a/tests/bindings-test2/BindingTest.cs b/tests/bindings-test2/BindingTest.cs index 7da68e8bbfc5..803e3bf91624 100644 --- a/tests/bindings-test2/BindingTest.cs +++ b/tests/bindings-test2/BindingTest.cs @@ -9,8 +9,8 @@ public class BindingTest { [Test] public void Test () { - Assert.AreEqual (42, CFunctions.getIntOfChocolate (), "chocolate"); - Assert.AreEqual (42, Bindings.Test.CFunctions.theUltimateAnswer (), "theUltimateAnswer"); + Assert.That (CFunctions.getIntOfChocolate (), Is.EqualTo (42), "chocolate"); + Assert.That (Bindings.Test.CFunctions.theUltimateAnswer (), Is.EqualTo (42), "theUltimateAnswer"); } } } diff --git a/tests/common/Assert.cs b/tests/common/Assert.cs index 5f1b7bb0f344..e16866ee8362 100644 --- a/tests/common/Assert.cs +++ b/tests/common/Assert.cs @@ -92,7 +92,7 @@ protected virtual void SetUp () public static void Assert (string msg, bool condition) { - NUnit.Framework.Assert.True (condition, msg); + Assert.That (condition, Is.True, msg); } public static void AssertEquals (object a, object b) diff --git a/tests/common/AssertHelpers.cs b/tests/common/AssertHelpers.cs index cb4aba3fe4ca..4975c7d26110 100644 --- a/tests/common/AssertHelpers.cs +++ b/tests/common/AssertHelpers.cs @@ -11,7 +11,7 @@ public static void Throws (Action action, string expectedExceptionMessage, st action (); throw new AssertionException (string.Format ("Expected {0}, but no exception was thrown. {1}.", typeof (T).FullName, message)); } catch (T ex) { - Assert.AreEqual (expectedExceptionMessage, ex.Message, message); + Assert.That (ex.Message, Is.EqualTo (expectedExceptionMessage), message); } } diff --git a/tests/common/BundlerTool.cs b/tests/common/BundlerTool.cs index 7a5754620653..3f45c28b1bf7 100644 --- a/tests/common/BundlerTool.cs +++ b/tests/common/BundlerTool.cs @@ -321,7 +321,7 @@ public virtual void AssertExecute (string message = null) public void AssertExecuteFailure (string message = null) { - Assert.AreEqual (1, Execute (), message); + Assert.That (Execute (), Is.EqualTo (1), message); } public abstract void CreateTemporaryApp (Profile profile, string appName = "testApp", string code = null, IList extraArgs = null, string extraCode = null, string usings = null); diff --git a/tests/common/ProductTests.cs b/tests/common/ProductTests.cs index ec1d1bc53569..8d4a4d3d1063 100644 --- a/tests/common/ProductTests.cs +++ b/tests/common/ProductTests.cs @@ -71,7 +71,7 @@ public void MinOSVersion (Profile profile, MachO.LoadCommands load_command, Mach Version lc_min_version; var mincmd = lc as MinCommand; if (mincmd is not null) { - Assert.AreEqual (load_command, mincmd.Command, "Unexpected min load command"); + Assert.That (mincmd.Command, Is.EqualTo (load_command), "Unexpected min load command"); lc_min_version = mincmd.Version; } else { // starting from iOS SDK 12 the LC_BUILD_VERSION is used instead @@ -159,7 +159,7 @@ public void MinOSVersion (Profile profile, MachO.LoadCommands load_command, Mach failed.Add ($"No minOS version found in {machoFile}."); } } - CollectionAssert.IsEmpty (failed, "Failures"); + Assert.That (failed, Is.Empty, "Failures"); } } diff --git a/tests/common/TestRuntime.cs b/tests/common/TestRuntime.cs index 68ab6699c66b..296c348ac2cf 100644 --- a/tests/common/TestRuntime.cs +++ b/tests/common/TestRuntime.cs @@ -210,7 +210,7 @@ public static void AssertXcodeVersion (int major, int minor, int build = 0) if (CheckXcodeVersion (major, minor, build)) return; - NUnit.Framework.Assert.Ignore ("Requires the platform version shipped with Xcode {0}.{1}", major, minor); + NUnit.Framework.Assert.Ignore ($"Requires the platform version shipped with Xcode {major}.{minor}"); } public static void AssertDevice (string message = "This test only runs on device.") @@ -1867,7 +1867,7 @@ public static void AssertNoNonNUnitException (Exception ex, string message) case InconclusiveException: throw new InconclusiveException (ex.Message, ex); case ResultStateException: throw ex; default: - Assert.IsNull (ex, message); + Assert.That (ex, Is.Null, message); break; } } diff --git a/tests/common/Touch.Unit/Touch.Client/Runner/TestResultElement.cs b/tests/common/Touch.Unit/Touch.Client/Runner/TestResultElement.cs index 880741dcc9a2..c9f1ef8af194 100644 --- a/tests/common/Touch.Unit/Touch.Client/Runner/TestResultElement.cs +++ b/tests/common/Touch.Unit/Touch.Client/Runner/TestResultElement.cs @@ -32,7 +32,7 @@ namespace MonoTouch.NUnit.UI { class TestResultElement : StyledMultilineElement { public TestResultElement (TestResult result) : - base (result.Message ?? "Unknown error", result.StackTrace, UITableViewCellStyle.Subtitle) + base (result.Message ?? "Unknown error", result.StackTrace ?? "", UITableViewCellStyle.Subtitle) { } } diff --git a/tests/common/Touch.Unit/Touch.Client/Runner/TouchRunner.cs b/tests/common/Touch.Unit/Touch.Client/Runner/TouchRunner.cs index a2a873e133e8..1c23046fb905 100644 --- a/tests/common/Touch.Unit/Touch.Client/Runner/TouchRunner.cs +++ b/tests/common/Touch.Unit/Touch.Client/Runner/TouchRunner.cs @@ -568,8 +568,8 @@ public virtual void TestFinished (ITestResult r) #endif string? stacktrace = result.StackTrace; - if (!String.IsNullOrEmpty (result.StackTrace)) { - string [] lines = stacktrace.Split (new char [] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + if (!String.IsNullOrEmpty (stacktrace)) { + string [] lines = stacktrace!.Split (new char [] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string line in lines) writer.WriteLine ("\t\t{0}", line); } @@ -689,9 +689,11 @@ public void Run (Test test) var tsr = new TestSuiteResult (suite); foreach (var runner in runners) { - var rv = (TestResult) (find_result (runner.Result) ?? runner.Result); - if (rv is not null) - tsr.AddResult (rv); + var runnerResult = runner.Result is not null ? (find_result (runner.Result) ?? runner.Result) : null; + if (runnerResult is null) + continue; + var rv = (TestResult) runnerResult; + tsr.AddResult (rv); } Result = tsr; #else diff --git a/tests/common/Touch.Unit/Touch.Client/dotnet/shared.csproj b/tests/common/Touch.Unit/Touch.Client/dotnet/shared.csproj index ada75cd79283..97edb9f41e74 100644 --- a/tests/common/Touch.Unit/Touch.Client/dotnet/shared.csproj +++ b/tests/common/Touch.Unit/Touch.Client/dotnet/shared.csproj @@ -55,10 +55,10 @@ - 3.12.0 + $(NUnitLitePackageVersion) - 3.6.0 + $(NUnitV2ResultWriterPackageVersion) 6.12.0.148 diff --git a/tests/common/shared-dotnet.csproj b/tests/common/shared-dotnet.csproj index 06243b83489f..8c3e83197315 100644 --- a/tests/common/shared-dotnet.csproj +++ b/tests/common/shared-dotnet.csproj @@ -107,7 +107,7 @@ - + diff --git a/tests/framework-test/FrameworkTests.cs b/tests/framework-test/FrameworkTests.cs index da279d818e21..6092954415c4 100644 --- a/tests/framework-test/FrameworkTests.cs +++ b/tests/framework-test/FrameworkTests.cs @@ -19,10 +19,10 @@ public class FrameworkTests { [Test] public void CFunction () { - Assert.AreEqual (42, CFunctions.theUltimateAnswer (), "a"); + Assert.That (CFunctions.theUltimateAnswer (), Is.EqualTo (42), "a"); #if !__MACOS__ - Assert.AreEqual (42, CFunctions.object_theUltimateAnswer (), "object"); - Assert.AreEqual (42, CFunctions.ar_theUltimateAnswer (), "ar"); + Assert.That (CFunctions.object_theUltimateAnswer (), Is.EqualTo (42), "object"); + Assert.That (CFunctions.ar_theUltimateAnswer (), Is.EqualTo (42), "ar"); #endif } @@ -30,7 +30,7 @@ public void CFunction () public void ObjCClass () { using (var obj = new FrameworkTest ()) { - Assert.AreEqual (42, obj.Func (), "a"); + Assert.That (obj.Func (), Is.EqualTo (42), "a"); } } } diff --git a/tests/fsharp/FSharpTests.fs b/tests/fsharp/FSharpTests.fs index ed4c75bb2031..5e05fcec9dc8 100644 --- a/tests/fsharp/FSharpTests.fs +++ b/tests/fsharp/FSharpTests.fs @@ -24,4 +24,4 @@ type FSharpTest () = let e = 5555 let pr = sprintf "%d %d %d %d %d" a b c d e - Assert.AreEqual ("1111 2222 3333 4444 5555", pr) + Assert.That (pr, Is.EqualTo ("1111 2222 3333 4444 5555")) diff --git a/tests/interdependent-binding-projects/Main.cs b/tests/interdependent-binding-projects/Main.cs index 2c08bd2dc725..892a12493f39 100644 --- a/tests/interdependent-binding-projects/Main.cs +++ b/tests/interdependent-binding-projects/Main.cs @@ -15,6 +15,6 @@ static partial void AddTestAssembliesImpl (HashSet assemblies) public class LoaderTest { public void TestAssemblyCount () { - Assert.AreEqual (3, TestLoader.GetTestAssemblies ().Count (), "Test assembly count"); + Assert.That (TestLoader.GetTestAssemblies ().Count (), Is.EqualTo (3), "Test assembly count"); } } diff --git a/tests/test-libraries/testgenerator.cs b/tests/test-libraries/testgenerator.cs index b50e7b191a8c..c118503a9a17 100644 --- a/tests/test-libraries/testgenerator.cs +++ b/tests/test-libraries/testgenerator.cs @@ -953,7 +953,7 @@ public partial class RegistrarTestGenerated {"); w.AppendLine ("\t\t\tusing (var tc = new ObjCRegistrarTest ()) {"); w.AppendLine ($"\t\t\t\tvar s = tc.PS{s};"); for (int i = 0; i < s.Length; i++) - w.AppendLine ($"\t\t\t\tAssert.AreEqual (0, s.x{i}, \"pre-#{i}\");"); + w.AppendLine ($"\t\t\t\tAssert.That (s.x{i}, Is.EqualTo (0), \"pre-#{i}\");"); w.Append ($"\t\t\t\tvar k = new S{s} () {{ "); for (int i = 0; i < s.Length; i++) w.Append ($"x{i} = ").Append (GetValue (s [i], i)).Append (", "); @@ -962,7 +962,7 @@ public partial class RegistrarTestGenerated {"); w.AppendLine ($"\t\t\t\ttc.PS{s} = k;"); w.AppendLine ($"\t\t\t\ts = tc.PS{s};"); for (int i = 0; i < s.Length; i++) - w.AppendLine ($"\t\t\t\tAssert.AreEqual (k.x{i}, s.x{i}, \"post-#{i}\");"); + w.AppendLine ($"\t\t\t\tAssert.That (s.x{i}, Is.EqualTo (k.x{i}), \"post-#{i}\");"); w.AppendLine (); w.Append ($"\t\t\t\tvar v = new S{s} () {{ "); @@ -975,7 +975,7 @@ public partial class RegistrarTestGenerated {"); w.AppendLine ($"\t\t\t\ttc.SetProperty{s} (v);"); w.AppendLine ($"\t\t\t\ts = tc.PS{s};"); for (int i = 0; i < s.Length; i++) - w.AppendLine ($"\t\t\t\tAssert.AreEqual (v.x{i}, s.x{i}, \"set-#{i}\");"); + w.AppendLine ($"\t\t\t\tAssert.That (s.x{i}, Is.EqualTo (v.x{i}), \"set-#{i}\");"); w.AppendLine ("\t\t\t}"); w.AppendLine ("\t\t}"); @@ -1066,32 +1066,32 @@ public partial class RegistrarTestGenerated {"); w.AppendLine ("\t\t{"); WriteAsserts (w, v); w.AppendLine ($"\t\t\tusing (var obj = new ObjCRegistrarTest ()) {{"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Number, \"initial null property\");"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}NumberNullable, \"initial nullable null property\");"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.Get{v.Managed}NumberNullable (), \"initial null method\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}Number, Is.Null, \"initial null property\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}NumberNullable, Is.Null, \"initial nullable null property\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}NumberNullable (), Is.Null, \"initial null method\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\t{v.Managed}? value = default ({v.Managed});"); w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}NumberNonNullable (value.Value);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.P{v.Managed}NumberNullable, \"nullable property after setting default value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.P{v.Managed}NumberNonNullable, \"non-nullable property after setting default value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.Get{v.Managed}NumberNullable (), \"nullable get method after setting default value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.Get{v.Managed}NumberNonNullable (), \"non-nullable get method after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}NumberNullable, Is.EqualTo (value), \"nullable property after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}NumberNonNullable, Is.EqualTo (value.Value), \"non-nullable property after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}NumberNullable (), Is.EqualTo (value), \"nullable get method after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}NumberNonNullable (), Is.EqualTo (value.Value), \"non-nullable get method after setting default value\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}NumberNonNullable (value.Value);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.P{v.Managed}NumberNullable, \"nullable property after setting custom value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.P{v.Managed}NumberNonNullable, \"non-nullable property after setting custom value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.Get{v.Managed}NumberNullable (), \"nullable get method after setting custom value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.Get{v.Managed}NumberNonNullable (), \"non-nullable get method after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}NumberNullable, Is.EqualTo (value), \"nullable property after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}NumberNonNullable, Is.EqualTo (value.Value), \"non-nullable property after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}NumberNullable (), Is.EqualTo (value), \"nullable get method after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}NumberNonNullable (), Is.EqualTo (value.Value), \"non-nullable get method after setting custom value\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = null;"); w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}NumberNullable (value);"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Number, \"null property after setting null value\");"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}NumberNullable, \"nullable null property after setting null value\");"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.Get{v.Managed}NumberNullable (), \"null method after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}Number, Is.Null, \"null property after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}NumberNullable, Is.Null, \"nullable null property after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}NumberNullable (), Is.Null, \"null method after setting null value\");"); w.AppendLine ($"\t\t\t}}"); w.AppendLine ("\t\t}"); w.AppendLine (); @@ -1102,43 +1102,43 @@ public partial class RegistrarTestGenerated {"); w.AppendLine ("\t\t{"); WriteAsserts (w, v); w.AppendLine ($"\t\t\tusing (var obj = new BindAsTestClassGenerated ()) {{"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Number, \"initial null\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Number, Is.Null, \"initial null\");"); w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}NumberNullable:\"), IntPtr.Zero);"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Number, \"null after setting null\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Number, Is.Null, \"null after setting null\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Number = {v.ManagedNewExpression};"); w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}NumberNullable:\"), IntPtr.Zero);"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Number, \"null after re-setting null\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Number, Is.Null, \"null after re-setting null\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvar value = {v.ManagedNewExpression};"); w.AppendLine ($"\t\t\t\tusing (var input = new NSNumber ({v.ToNSNumberCastExpression}value))"); w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}NumberNullable:\"), input.Handle);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.{v.Managed}Number, \"after setting A\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Number, Is.EqualTo (value), \"after setting A\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Number = null;"); w.AppendLine ($"\t\t\t\tusing (var input = new NSNumber ({v.ToNSNumberCastExpression}value))"); w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}NumberNonNullable:\"), input.Handle);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.{v.Managed}Number.Value, \"after setting B\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Number.Value, Is.EqualTo (value), \"after setting B\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Number = null;"); w.AppendLine ($"\t\t\t\tvar number = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}NumberNullable\")));"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (number, \"null from getter A\");"); + w.AppendLine ($"\t\t\t\tAssert.That (number, Is.Null, \"null from getter A\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Number = value;"); w.AppendLine ($"\t\t\t\tnumber = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}NumberNullable\")));"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, {v.FromNSNumberCastExpression}number{v.Map}, \"getter B\");"); + w.AppendLine ($"\t\t\t\tAssert.That ({v.FromNSNumberCastExpression}number{v.Map}, Is.EqualTo (value), \"getter B\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Number = value;"); w.AppendLine ($"\t\t\t\tnumber = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}NumberNonNullable\")));"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, {v.FromNSNumberCastExpression}number{v.Map}, \"getter C\");"); + w.AppendLine ($"\t\t\t\tAssert.That ({v.FromNSNumberCastExpression}number{v.Map}, Is.EqualTo (value), \"getter C\");"); w.AppendLine ($"\t\t\t}}"); w.AppendLine ("\t\t}"); @@ -1148,28 +1148,28 @@ public partial class RegistrarTestGenerated {"); w.AppendLine ("\t\t{"); WriteAsserts (w, v); w.AppendLine ($"\t\t\tusing (var obj = new ObjCRegistrarTest ()) {{"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Array, \"initial null property\");"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.Get{v.Managed}Array (), \"initial null method\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}Array, Is.Null, \"initial null property\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}Array (), Is.Null, \"initial null method\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\t{v.Managed}[] value = null;"); w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}Array (value);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.P{v.Managed}Array, \"nullable property after setting default value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.Get{v.Managed}Array (), \"nullable get method after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}Array, Is.EqualTo (value), \"nullable property after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}Array (), Is.EqualTo (value), \"nullable get method after setting default value\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}Array (value);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (1, obj.P{v.Managed}Array.Length, \"nullable property after setting custom value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (1, obj.Get{v.Managed}Array ().Length, \"nullable get method after setting custom value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.P{v.Managed}Array [0], \"nullable property after setting custom value element\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.Get{v.Managed}Array () [0], \"nullable get method after setting custom value element\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}Array.Length, Is.EqualTo (1), \"nullable property after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}Array ().Length, Is.EqualTo (1), \"nullable get method after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}Array [0], Is.EqualTo (value [0]), \"nullable property after setting custom value element\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}Array () [0], Is.EqualTo (value [0]), \"nullable get method after setting custom value element\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = null;"); w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}Array (value);"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Array, \"null property after setting null value\");"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.Get{v.Managed}Array (), \"null method after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}Array, Is.Null, \"null property after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}Array (), Is.Null, \"null method after setting null value\");"); w.AppendLine ($"\t\t\t}}"); w.AppendLine ("\t\t}"); w.AppendLine (); @@ -1180,40 +1180,40 @@ public partial class RegistrarTestGenerated {"); w.AppendLine ("\t\t{"); WriteAsserts (w, v); w.AppendLine ($"\t\t\tusing (var obj = new BindAsTestClassGenerated ()) {{"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Array, \"initial null\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Array, Is.Null, \"initial null\");"); w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), IntPtr.Zero);"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Array, \"null after setting null\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Array, Is.Null, \"null after setting null\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Array = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), IntPtr.Zero);"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Array, \"null after re-setting null\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Array, Is.Null, \"null after re-setting null\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvar value = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); w.AppendLine ($"\t\t\t\tusing (var input = NSArray.FromNSObjects<{v.Managed}> ((v) => new NSNumber ({v.ToNSNumberCastExpression}v), value))"); w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), input.Handle);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Length, obj.{v.Managed}Array.Length, \"after setting A\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.{v.Managed}Array [0], \"after setting A element\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Array.Length, Is.EqualTo (value.Length), \"after setting A\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Array [0], Is.EqualTo (value [0]), \"after setting A element\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Array = null;"); w.AppendLine ($"\t\t\t\tusing (var input = NSArray.FromNSObjects<{v.Managed}> ((v) => new NSNumber ({v.ToNSNumberCastExpression}v), value))"); w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), input.Handle);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Length, obj.{v.Managed}Array.Length, \"after setting B\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.{v.Managed}Array [0], \"after setting B element\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Array.Length, Is.EqualTo (value.Length), \"after setting B\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Array [0], Is.EqualTo (value [0]), \"after setting B element\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Array = null;"); w.AppendLine ($"\t\t\t\tvar array = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}Array\")));"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (array, \"null from getter A\");"); + w.AppendLine ($"\t\t\t\tAssert.That (array, Is.Null, \"null from getter A\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Array = value;"); w.AppendLine ($"\t\t\t\tarray = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}Array\")));"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual ((nuint) value.Length, array.Count, \"getter B\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], {v.FromNSNumberCastExpression}array.GetItem (0){v.Map}, \"getter B element\");"); + w.AppendLine ($"\t\t\t\tAssert.That (array.Count, Is.EqualTo ((nuint) value.Length), \"getter B\");"); + w.AppendLine ($"\t\t\t\tAssert.That ({v.FromNSNumberCastExpression}array.GetItem (0){v.Map}, Is.EqualTo (value [0]), \"getter B element\");"); w.AppendLine (); w.AppendLine ($"\t\t\t}}"); @@ -1236,32 +1236,32 @@ public partial class RegistrarTestGenerated {"); w.AppendLine ("\t\t{"); WriteAsserts (w, v); w.AppendLine ($"\t\t\tusing (var obj = new ObjCRegistrarTest ()) {{"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Value, \"initial null property\");"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}ValueNullable, \"initial nullable null property\");"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.Get{v.Managed}ValueNullable (), \"initial null method\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}Value, Is.Null, \"initial null property\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}ValueNullable, Is.Null, \"initial nullable null property\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}ValueNullable (), Is.Null, \"initial null method\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\t{v.Managed}? value = default ({v.Managed});"); w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}ValueNonNullable (value.Value);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.P{v.Managed}ValueNullable, \"nullable property after setting default value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.P{v.Managed}ValueNonNullable, \"non-nullable property after setting default value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.Get{v.Managed}ValueNullable (), \"nullable get method after setting default value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.Get{v.Managed}ValueNonNullable (), \"non-nullable get method after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}ValueNullable, Is.EqualTo (value), \"nullable property after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}ValueNonNullable, Is.EqualTo (value.Value), \"non-nullable property after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}ValueNullable (), Is.EqualTo (value), \"nullable get method after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}ValueNonNullable (), Is.EqualTo (value.Value), \"non-nullable get method after setting default value\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}ValueNonNullable (value.Value);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.P{v.Managed}ValueNullable, \"nullable property after setting custom value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.P{v.Managed}ValueNonNullable, \"non-nullable property after setting custom value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.Get{v.Managed}ValueNullable (), \"nullable get method after setting custom value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.Get{v.Managed}ValueNonNullable (), \"non-nullable get method after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}ValueNullable, Is.EqualTo (value), \"nullable property after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}ValueNonNullable, Is.EqualTo (value.Value), \"non-nullable property after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}ValueNullable (), Is.EqualTo (value), \"nullable get method after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}ValueNonNullable (), Is.EqualTo (value.Value), \"non-nullable get method after setting custom value\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = null;"); w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}ValueNullable (value);"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Value, \"null property after setting null value\");"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}ValueNullable, \"nullable null property after setting null value\");"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.Get{v.Managed}ValueNullable (), \"null method after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}Value, Is.Null, \"null property after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}ValueNullable, Is.Null, \"nullable null property after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}ValueNullable (), Is.Null, \"null method after setting null value\");"); w.AppendLine ($"\t\t\t}}"); w.AppendLine ("\t\t}"); w.AppendLine (); @@ -1272,43 +1272,43 @@ public partial class RegistrarTestGenerated {"); w.AppendLine ("\t\t{"); WriteAsserts (w, v); w.AppendLine ($"\t\t\tusing (var obj = new BindAsTestClassGenerated ()) {{"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Value, \"initial null\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Value, Is.Null, \"initial null\");"); w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}ValueNullable:\"), IntPtr.Zero);"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Value, \"null after setting null\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Value, Is.Null, \"null after setting null\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Value = {v.ManagedNewExpression};"); w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}ValueNullable:\"), IntPtr.Zero);"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Value, \"null after re-setting null\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Value, Is.Null, \"null after re-setting null\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvar value = {v.ManagedNewExpression};"); w.AppendLine ($"\t\t\t\tusing (var input = NSValue.{v.MapFrom} (value))"); w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}ValueNullable:\"), input.Handle);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.{v.Managed}Value, \"after setting A\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Value, Is.EqualTo (value), \"after setting A\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Value = null;"); w.AppendLine ($"\t\t\t\tusing (var input = NSValue.{v.MapFrom} (value))"); w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}ValueNonNullable:\"), input.Handle);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.{v.Managed}Value, \"after setting B\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Value, Is.EqualTo (value), \"after setting B\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Value = null;"); w.AppendLine ($"\t\t\t\tvar Value = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}ValueNullable\")));"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (Value, \"null from getter A\");"); + w.AppendLine ($"\t\t\t\tAssert.That (Value, Is.Null, \"null from getter A\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Value = value;"); w.AppendLine ($"\t\t\t\tValue = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}ValueNullable\")));"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, Value{v.Map}, \"getter B\");"); + w.AppendLine ($"\t\t\t\tAssert.That (Value{v.Map}, Is.EqualTo (value), \"getter B\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Value = value;"); w.AppendLine ($"\t\t\t\tValue = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}ValueNonNullable\")));"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, Value{v.Map}, \"getter C\");"); + w.AppendLine ($"\t\t\t\tAssert.That (Value{v.Map}, Is.EqualTo (value), \"getter C\");"); w.AppendLine ($"\t\t\t}}"); w.AppendLine ("\t\t}"); @@ -1318,28 +1318,28 @@ public partial class RegistrarTestGenerated {"); w.AppendLine ("\t\t{"); WriteAsserts (w, v); w.AppendLine ($"\t\t\tusing (var obj = new ObjCRegistrarTest ()) {{"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Array, \"initial null property\");"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.Get{v.Managed}Array (), \"initial null method\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}Array, Is.Null, \"initial null property\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}Array (), Is.Null, \"initial null method\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\t{v.Managed}[] value = null;"); w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}Array (value);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.P{v.Managed}Array, \"nullable property after setting default value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.Get{v.Managed}Array (), \"nullable get method after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}Array, Is.EqualTo (value), \"nullable property after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}Array (), Is.EqualTo (value), \"nullable get method after setting default value\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}Array (value);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (1, obj.P{v.Managed}Array.Length, \"nullable property after setting custom value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (1, obj.Get{v.Managed}Array ().Length, \"nullable get method after setting custom value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.P{v.Managed}Array [0], \"nullable property after setting custom value element\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.Get{v.Managed}Array () [0], \"nullable get method after setting custom value element\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}Array.Length, Is.EqualTo (1), \"nullable property after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}Array ().Length, Is.EqualTo (1), \"nullable get method after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}Array [0], Is.EqualTo (value [0]), \"nullable property after setting custom value element\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}Array () [0], Is.EqualTo (value [0]), \"nullable get method after setting custom value element\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = null;"); w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}Array (value);"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Array, \"null property after setting null value\");"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.Get{v.Managed}Array (), \"null method after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.P{v.Managed}Array, Is.Null, \"null property after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.Get{v.Managed}Array (), Is.Null, \"null method after setting null value\");"); w.AppendLine ($"\t\t\t}}"); w.AppendLine ("\t\t}"); w.AppendLine (); @@ -1350,40 +1350,40 @@ public partial class RegistrarTestGenerated {"); w.AppendLine ("\t\t{"); WriteAsserts (w, v); w.AppendLine ($"\t\t\tusing (var obj = new BindAsTestClassGenerated ()) {{"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Array, \"initial null\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Array, Is.Null, \"initial null\");"); w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), IntPtr.Zero);"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Array, \"null after setting null\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Array, Is.Null, \"null after setting null\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Array = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), IntPtr.Zero);"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Array, \"null after re-setting null\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Array, Is.Null, \"null after re-setting null\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvar value = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); w.AppendLine ($"\t\t\t\tusing (var input = NSArray.FromNSObjects<{v.Managed}> ((v) => NSValue.{v.MapFrom} (v), value))"); w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), input.Handle);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Length, obj.{v.Managed}Array.Length, \"after setting A\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.{v.Managed}Array [0], \"after setting A element\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Array.Length, Is.EqualTo (value.Length), \"after setting A\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Array [0], Is.EqualTo (value [0]), \"after setting A element\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Array = null;"); w.AppendLine ($"\t\t\t\tusing (var input = NSArray.FromNSObjects<{v.Managed}> ((v) => NSValue.{v.MapFrom} (v), value))"); w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), input.Handle);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Length, obj.{v.Managed}Array.Length, \"after setting B\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.{v.Managed}Array [0], \"after setting B element\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Array.Length, Is.EqualTo (value.Length), \"after setting B\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.{v.Managed}Array [0], Is.EqualTo (value [0]), \"after setting B element\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Array = null;"); w.AppendLine ($"\t\t\t\tvar array = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}Array\")));"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (array, \"null from getter A\");"); + w.AppendLine ($"\t\t\t\tAssert.That (array, Is.Null, \"null from getter A\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Array = value;"); w.AppendLine ($"\t\t\t\tarray = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}Array\")));"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual ((nuint) value.Length, array.Count, \"getter B\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], array.GetItem (0){v.Map}, \"getter B element\");"); + w.AppendLine ($"\t\t\t\tAssert.That (array.Count, Is.EqualTo ((nuint) value.Length), \"getter B\");"); + w.AppendLine ($"\t\t\t\tAssert.That (array.GetItem (0){v.Map}, Is.EqualTo (value [0]), \"getter B element\");"); w.AppendLine ($"\t\t\t}}"); w.AppendLine ("\t\t}"); w.AppendLine (); @@ -1406,28 +1406,28 @@ public partial class RegistrarTestGenerated {"); w.AppendLine ($"\t\t\tusing (var obj = new ObjCRegistrarTest ()) {{"); w.AppendLine ($"\t\t\t\tAssert.Throws (() => {{ Console.WriteLine (obj.PSmart{v.Managed}Property); }}, \"initial zero property\");"); w.AppendLine ($"\t\t\t\tAssert.Throws (() => {{ Console.WriteLine (obj.GetSmart{v.Managed}Value ()); }}, \"initial zero method\");"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.GetSmartNullable{v.Managed}Value (), \"initial null method\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.GetSmartNullable{v.Managed}Value (), Is.Null, \"initial null method\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\t{v.Managed}? value = default ({v.Managed});"); w.AppendLine ($"\t\t\t\tobj.SetSmartNullable{v.Managed}Value (value);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.PSmart{v.Managed}Property, \"zero property after setting default value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.GetSmart{v.Managed}Value (), \"non-nullable property after setting default value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.GetSmartNullable{v.Managed}Value (), \"nullable get method after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.PSmart{v.Managed}Property, Is.EqualTo (value.Value), \"zero property after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.GetSmart{v.Managed}Value (), Is.EqualTo (value.Value), \"non-nullable property after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.GetSmartNullable{v.Managed}Value (), Is.EqualTo (value), \"nullable get method after setting default value\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); w.AppendLine ($"\t\t\t\tobj.SetSmart{v.Managed}Value (value.Value);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.PSmart{v.Managed}Property, \"non-nullable property after setting custom value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.GetSmartNullable{v.Managed}Value (), \"nullable get method after setting custom value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.GetSmart{v.Managed}Value (), \"non-nullable get method after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.PSmart{v.Managed}Property, Is.EqualTo (value.Value), \"non-nullable property after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.GetSmartNullable{v.Managed}Value (), Is.EqualTo (value), \"nullable get method after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.GetSmart{v.Managed}Value (), Is.EqualTo (value.Value), \"non-nullable get method after setting custom value\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = null;"); w.AppendLine ($"\t\t\t\tobj.SetSmartNullable{v.Managed}Value (value);"); w.AppendLine ($"\t\t\t\tAssert.Throws (() => {{ Console.WriteLine (obj.PSmart{v.Managed}Property); }}, \"null property after setting null value\");"); w.AppendLine ($"\t\t\t\tAssert.Throws (() => {{ Console.WriteLine (obj.GetSmart{v.Managed}Value ()); }}, \"non-nullable method after setting null value\");"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.GetSmartNullable{v.Managed}Value (), \"null method after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.GetSmartNullable{v.Managed}Value (), Is.Null, \"null method after setting null value\");"); w.AppendLine ($"\t\t\t}}"); w.AppendLine ("\t\t}"); w.AppendLine (); @@ -1451,30 +1451,30 @@ public partial class RegistrarTestGenerated {"); w.AppendLine ($"\t\t\t\tvar value = {v.ManagedNewExpression};"); w.AppendLine ($"\t\t\t\tusing (var input = value.GetConstant ())"); w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"setSmartNullable{v.Managed}Value:\"), input.Handle);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.PSmart{v.Managed}Property, \"after setting A\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.PSmart{v.Managed}Property, Is.EqualTo (value), \"after setting A\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.PSmart{v.Managed}Property = 0;"); w.AppendLine ($"\t\t\t\tusing (var input = value.GetConstant ())"); w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"setSmart{v.Managed}Value:\"), input.Handle);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.PSmart{v.Managed}Property, \"after setting B\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.PSmart{v.Managed}Property, Is.EqualTo (value), \"after setting B\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.PSmart{v.Managed}Property = 0;"); w.AppendLine ($"\t\t\t\tvar Value = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"getSmartNullable{v.Managed}Value\")));"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (default ({v.Managed}).GetConstant ().ToString (), Value.ToString (), \"zero from getter A\");"); + w.AppendLine ($"\t\t\t\tAssert.That (Value.ToString (), Is.EqualTo (default ({v.Managed}).GetConstant ().ToString ()), \"zero from getter A\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); w.AppendLine ($"\t\t\t\tobj.PSmart{v.Managed}Property = value;"); w.AppendLine ($"\t\t\t\tValue = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"getSmartNullable{v.Managed}Value\")));"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, {v.Managed}Extensions.GetValue (Value), \"getter B\");"); + w.AppendLine ($"\t\t\t\tAssert.That ({v.Managed}Extensions.GetValue (Value), Is.EqualTo (value), \"getter B\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); w.AppendLine ($"\t\t\t\tobj.PSmart{v.Managed}Property = value;"); w.AppendLine ($"\t\t\t\tValue = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"getSmart{v.Managed}Value\")));"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, {v.Managed}Extensions.GetValue (Value), \"getter C\");"); + w.AppendLine ($"\t\t\t\tAssert.That ({v.Managed}Extensions.GetValue (Value), Is.EqualTo (value), \"getter C\");"); w.AppendLine ($"\t\t\t}}"); w.AppendLine ("\t\t}"); @@ -1484,28 +1484,28 @@ public partial class RegistrarTestGenerated {"); w.AppendLine ("\t\t{"); WriteAsserts (w, v); w.AppendLine ($"\t\t\tusing (var obj = new ObjCRegistrarTest ()) {{"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.PSmart{v.Managed}Properties, \"initial null property\");"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.GetSmart{v.Managed}Values (), \"initial null method\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.PSmart{v.Managed}Properties, Is.Null, \"initial null property\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.GetSmart{v.Managed}Values (), Is.Null, \"initial null method\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\t{v.Managed}[] value = null;"); w.AppendLine ($"\t\t\t\tobj.SetSmart{v.Managed}Values (value);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.PSmart{v.Managed}Properties, \"nullable property after setting default value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.GetSmart{v.Managed}Values (), \"nullable get method after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.PSmart{v.Managed}Properties, Is.EqualTo (value), \"nullable property after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.GetSmart{v.Managed}Values (), Is.EqualTo (value), \"nullable get method after setting default value\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); w.AppendLine ($"\t\t\t\tobj.SetSmart{v.Managed}Values (value);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (1, obj.PSmart{v.Managed}Properties.Length, \"nullable property after setting custom value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (1, obj.GetSmart{v.Managed}Values ().Length, \"nullable get method after setting custom value\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.PSmart{v.Managed}Properties [0], \"nullable property after setting custom value element\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.GetSmart{v.Managed}Values () [0], \"nullable get method after setting custom value element\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.PSmart{v.Managed}Properties.Length, Is.EqualTo (1), \"nullable property after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.GetSmart{v.Managed}Values ().Length, Is.EqualTo (1), \"nullable get method after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.PSmart{v.Managed}Properties [0], Is.EqualTo (value [0]), \"nullable property after setting custom value element\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.GetSmart{v.Managed}Values () [0], Is.EqualTo (value [0]), \"nullable get method after setting custom value element\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = null;"); w.AppendLine ($"\t\t\t\tobj.SetSmart{v.Managed}Values (value);"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.PSmart{v.Managed}Properties, \"null property after setting null value\");"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.GetSmart{v.Managed}Values (), \"null method after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.PSmart{v.Managed}Properties, Is.Null, \"null property after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.GetSmart{v.Managed}Values (), Is.Null, \"null method after setting null value\");"); w.AppendLine ($"\t\t\t}}"); w.AppendLine ("\t\t}"); w.AppendLine (); @@ -1516,40 +1516,40 @@ public partial class RegistrarTestGenerated {"); w.AppendLine ("\t\t{"); WriteAsserts (w, v); w.AppendLine ($"\t\t\tusing (var obj = new BindAsTestClassGenerated ()) {{"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.PSmart{v.Managed}Properties, \"initial null\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.PSmart{v.Managed}Properties, Is.Null, \"initial null\");"); w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"setSmart{v.Managed}Values:\"), IntPtr.Zero);"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.PSmart{v.Managed}Properties, \"null after setting null\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.PSmart{v.Managed}Properties, Is.Null, \"null after setting null\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.PSmart{v.Managed}Properties = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"setSmart{v.Managed}Values:\"), IntPtr.Zero);"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.PSmart{v.Managed}Properties, \"null after re-setting null\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.PSmart{v.Managed}Properties, Is.Null, \"null after re-setting null\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvar value = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); w.AppendLine ($"\t\t\t\tusing (var input = NSArray.FromNSObjects<{v.Managed}> ((v) => v.GetConstant (), value))"); w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"setSmart{v.Managed}Values:\"), input.Handle);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Length, obj.PSmart{v.Managed}Properties.Length, \"after setting A\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.PSmart{v.Managed}Properties [0], \"after setting A element\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.PSmart{v.Managed}Properties.Length, Is.EqualTo (value.Length), \"after setting A\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.PSmart{v.Managed}Properties [0], Is.EqualTo (value [0]), \"after setting A element\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.PSmart{v.Managed}Properties = null;"); w.AppendLine ($"\t\t\t\tusing (var input = NSArray.FromNSObjects<{v.Managed}> ((v) => v.GetConstant (), value))"); w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"setSmart{v.Managed}Values:\"), input.Handle);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Length, obj.PSmart{v.Managed}Properties.Length, \"after setting B\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.PSmart{v.Managed}Properties [0], \"after setting B element\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.PSmart{v.Managed}Properties.Length, Is.EqualTo (value.Length), \"after setting B\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.PSmart{v.Managed}Properties [0], Is.EqualTo (value [0]), \"after setting B element\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.PSmart{v.Managed}Properties = null;"); w.AppendLine ($"\t\t\t\tvar array = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"getSmart{v.Managed}Values\")));"); - w.AppendLine ($"\t\t\t\tAssert.IsNull (array, \"null from getter A\");"); + w.AppendLine ($"\t\t\t\tAssert.That (array, Is.Null, \"null from getter A\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); w.AppendLine ($"\t\t\t\tobj.PSmart{v.Managed}Properties = value;"); w.AppendLine ($"\t\t\t\tarray = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"getSmart{v.Managed}Values\")));"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual ((nuint) value.Length, array.Count, \"getter B\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], {v.Managed}Extensions.GetValue (array.GetItem (0)), \"getter B element\");"); + w.AppendLine ($"\t\t\t\tAssert.That (array.Count, Is.EqualTo ((nuint) value.Length), \"getter B\");"); + w.AppendLine ($"\t\t\t\tAssert.That ({v.Managed}Extensions.GetValue (array.GetItem (0)), Is.EqualTo (value [0]), \"getter B element\");"); w.AppendLine ($"\t\t\t}}"); w.AppendLine ("\t\t}"); w.AppendLine (); @@ -1720,7 +1720,7 @@ public partial class TrampolineTestGenerated {"); w.AppendLine ($"\t\t\t\t\trv = S{s}_objc_msgSend (obj.Handle, new Selector (\"Test_{s}Struct\").Handle);"); w.AppendLine ($"\t\t\t\t}}"); } - w.AppendLine ($"\t\t\t\tAssert.AreEqual (({GenerateNewExpression (s, 1)}).ToString (), rv.ToString (), \"a\");"); + w.AppendLine ($"\t\t\t\tAssert.That (rv.ToString (), Is.EqualTo (({GenerateNewExpression (s, 1)}).ToString ()), \"a\");"); w.AppendLine (); WriteStretConditions (w, s, out never); @@ -1732,7 +1732,7 @@ public partial class TrampolineTestGenerated {"); w.AppendLine ($"\t\t\t\t\trv = S{s}_objc_msgSend (class_ptr, new Selector (\"Test_Static{s}Struct\").Handle);"); w.AppendLine ($"\t\t\t\t}}"); } - w.AppendLine ($"\t\t\t\tAssert.AreEqual (({GenerateNewExpression (s, 2)}).ToString (), rv.ToString (), \"a\");"); + w.AppendLine ($"\t\t\t\tAssert.That (rv.ToString (), Is.EqualTo (({GenerateNewExpression (s, 2)}).ToString ()), \"a\");"); w.AppendLine (); WriteStretConditions (w, s, out never); @@ -1744,7 +1744,7 @@ public partial class TrampolineTestGenerated {"); w.AppendLine ($"\t\t\t\t\trv = S{s}_objc_msgSend (obj.Handle, new Selector (\"Test_{s}StructProperty\").Handle);"); w.AppendLine ($"\t\t\t\t}}"); } - w.AppendLine ($"\t\t\t\tAssert.AreEqual (({GenerateNewExpression (s, 3)}).ToString (), rv.ToString (), \"a\");"); + w.AppendLine ($"\t\t\t\tAssert.That (rv.ToString (), Is.EqualTo (({GenerateNewExpression (s, 3)}).ToString ()), \"a\");"); w.AppendLine (); WriteStretConditions (w, s, out never); @@ -1756,7 +1756,7 @@ public partial class TrampolineTestGenerated {"); w.AppendLine ($"\t\t\t\t\trv = S{s}_objc_msgSend (class_ptr, new Selector (\"Test_Static{s}StructProperty\").Handle);"); w.AppendLine ($"\t\t\t\t}}"); } - w.AppendLine ($"\t\t\t\tAssert.AreEqual (({GenerateNewExpression (s, 4)}).ToString (), rv.ToString (), \"a\");"); + w.AppendLine ($"\t\t\t\tAssert.That (rv.ToString (), Is.EqualTo (({GenerateNewExpression (s, 4)}).ToString ()), \"a\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\trvd = 0;"); @@ -1769,8 +1769,8 @@ public partial class TrampolineTestGenerated {"); w.AppendLine ($"\t\t\t\t\trv = S{s}_objc_msgSend_out_double (obj.Handle, new Selector (\"Test_{s}Struct_out_double:\").Handle, out rvd);"); w.AppendLine ($"\t\t\t\t}}"); } - w.AppendLine ($"\t\t\t\tAssert.AreEqual (({GenerateNewExpression (s, 5)}).ToString (), rv.ToString (), \"a\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (3.14, rvd, \"double out\");"); + w.AppendLine ($"\t\t\t\tAssert.That (rv.ToString (), Is.EqualTo (({GenerateNewExpression (s, 5)}).ToString ()), \"a\");"); + w.AppendLine ($"\t\t\t\tAssert.That (rvd, Is.EqualTo (3.14), \"double out\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\trvf = 0;"); @@ -1783,8 +1783,8 @@ public partial class TrampolineTestGenerated {"); w.AppendLine ($"\t\t\t\t\trv = S{s}_objc_msgSend_out_float (class_ptr, new Selector (\"Test_Static{s}Struct_out_float:\").Handle, out rvf);"); w.AppendLine ($"\t\t\t\t}}"); } - w.AppendLine ($"\t\t\t\tAssert.AreEqual (({GenerateNewExpression (s, 6)}).ToString (), rv.ToString (), \"a\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (3.15f, rvf, \"float out\");"); + w.AppendLine ($"\t\t\t\tAssert.That (rv.ToString (), Is.EqualTo (({GenerateNewExpression (s, 6)}).ToString ()), \"a\");"); + w.AppendLine ($"\t\t\t\tAssert.That (rvf, Is.EqualTo (3.15f), \"float out\");"); w.AppendLine (); w.AppendLine ($"\t\t\t}}"); @@ -1793,7 +1793,7 @@ public partial class TrampolineTestGenerated {"); w.AppendLine ($"\t\t\tusing (var obj = new OverrideRegistrarTest ()) {{"); w.AppendLine ($"\t\t\t\tvar structValue = {GenerateNewExpression (s, 7)};"); w.AppendLine ($"\t\t\t\tvoid_objc_msgSend_{s} (obj.Handle, Selector.GetHandle (\"setProperty{s}:\"), structValue);"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (structValue.ToString (), obj.V{s}.ToString (), \"SetProperty#1\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.V{s}.ToString (), Is.EqualTo (structValue.ToString ()), \"SetProperty#1\");"); w.AppendLine ($"\t\t\t}}"); w.AppendLine (); @@ -1813,9 +1813,9 @@ public partial class TrampolineTestGenerated {"); for (var x = 1; x <= i + 1; x++) { if (x == i) { - w.AppendLine ($"\t\t\t\tAssert.AreEqual (structValue.ToString (), obj.V{s}.ToString (), \"SetProperty#2-S{x}\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.V{s}.ToString (), Is.EqualTo (structValue.ToString ()), \"SetProperty#2-S{x}\");"); } else { - w.AppendLine ($"\t\t\t\tAssert.AreEqual ((nint) {x}, obj.ManagedVoidArg{x}, \"SetProperty#2-X{x}\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.ManagedVoidArg{x}, Is.EqualTo ((nint) {x}), \"SetProperty#2-X{x}\");"); } } w.AppendLine ($"\t\t\t}}"); @@ -1838,11 +1838,11 @@ public partial class TrampolineTestGenerated {"); for (var x = 1; x <= i + 1; x++) { if (x == 1) { - w.AppendLine ($"\t\t\t\tAssert.AreEqual ((float) {x}, obj.ManagedFloatArg{x}, \"SetProperty#3-X{x}\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.ManagedFloatArg{x}, Is.EqualTo ((float) {x}), \"SetProperty#3-X{x}\");"); } else if (x == i) { - w.AppendLine ($"\t\t\t\tAssert.AreEqual (structValue.ToString (), obj.V{s}.ToString (), \"SetProperty#3-S{x}\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.V{s}.ToString (), Is.EqualTo (structValue.ToString ()), \"SetProperty#3-S{x}\");"); } else { - w.AppendLine ($"\t\t\t\tAssert.AreEqual ((nint) {x}, obj.ManagedVoidArg{x}, \"SetProperty#3-X{x}\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.ManagedVoidArg{x}, Is.EqualTo ((nint) {x}), \"SetProperty#3-X{x}\");"); } } w.AppendLine ($"\t\t\t}}"); @@ -1865,11 +1865,11 @@ public partial class TrampolineTestGenerated {"); for (var x = 1; x <= i + 1; x++) { if (x == 1) { - w.AppendLine ($"\t\t\t\tAssert.AreEqual ((double) {x}, obj.ManagedDoubleArg{x}, \"SetProperty#3-X{x}\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.ManagedDoubleArg{x}, Is.EqualTo ((double) {x}), \"SetProperty#3-X{x}\");"); } else if (x == i) { - w.AppendLine ($"\t\t\t\tAssert.AreEqual (structValue.ToString (), obj.V{s}.ToString (), \"SetProperty#3-S{x}\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.V{s}.ToString (), Is.EqualTo (structValue.ToString ()), \"SetProperty#3-S{x}\");"); } else { - w.AppendLine ($"\t\t\t\tAssert.AreEqual ((nint) {x}, obj.ManagedVoidArg{x}, \"SetProperty#3-X{x}\");"); + w.AppendLine ($"\t\t\t\tAssert.That (obj.ManagedVoidArg{x}, Is.EqualTo ((nint) {x}), \"SetProperty#3-X{x}\");"); } } w.AppendLine ($"\t\t\t}}"); diff --git a/tests/xcframework-test/XCFrameworkTests.cs b/tests/xcframework-test/XCFrameworkTests.cs index 206951ea19b0..2926ee397ed8 100644 --- a/tests/xcframework-test/XCFrameworkTests.cs +++ b/tests/xcframework-test/XCFrameworkTests.cs @@ -19,14 +19,14 @@ public class FrameworkTests { [Test] public void CFunction () { - Assert.AreEqual (42, CFunctions.theUltimateAnswer (), "a"); + Assert.That (CFunctions.theUltimateAnswer (), Is.EqualTo (42), "a"); } [Test] public void ObjCClass () { using (var obj = new FrameworkTest ()) { - Assert.AreEqual (42, obj.Func (), "a"); + Assert.That (obj.Func (), Is.EqualTo (42), "a"); } } } diff --git a/tests/xtro-sharpie/UnitTests/UnitTests.csproj b/tests/xtro-sharpie/UnitTests/UnitTests.csproj index dd9a60e4b579..5bd7b152f1f2 100644 --- a/tests/xtro-sharpie/UnitTests/UnitTests.csproj +++ b/tests/xtro-sharpie/UnitTests/UnitTests.csproj @@ -6,9 +6,9 @@ - - - + + + diff --git a/tests/xtro-sharpie/UnitTests/Xtro.cs b/tests/xtro-sharpie/UnitTests/Xtro.cs index 9b4dd819c721..6037f79e44c1 100644 --- a/tests/xtro-sharpie/UnitTests/Xtro.cs +++ b/tests/xtro-sharpie/UnitTests/Xtro.cs @@ -29,7 +29,7 @@ public void RunTest () TestContext.AddTestAttachment (zippedReport, "HTML report (zipped)"); } - Assert.AreEqual (0, rv, "ExitCode"); + Assert.That (rv, Is.EqualTo (0), "ExitCode"); } } } From e3f0f385b69236b37f80ec5cfc3d06c71a4e6a8f Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 25 May 2026 08:18:40 +0200 Subject: [PATCH 07/79] [Foundation] Simplify memory management of NSObjectData for CoreCLR. (#25507) On CoreCLR, the tagged memory returned by ObjectiveCMarshal.CreateReferenceTrackingHandle is guaranteed to be stable (same pointer per object) and its lifetime is tied to the GC freeing the instance. This means we can use it directly as our NSObjectData storage, removing the need for: - The TrackedObjectInfo indirection struct (both in C# and native code). - The ConditionalWeakTable used to prevent premature collection of separately-allocated native memory. - The Interlocked.CompareExchange synchronization (since the pointer is always the same). --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- runtime/coreclr-bridge.m | 16 ++++++--------- src/Foundation/NSObject2.cs | 33 ++++++++++++++++++------------ src/ObjCRuntime/Runtime.CoreCLR.cs | 27 +++++++++++++++--------- 3 files changed, 43 insertions(+), 33 deletions(-) diff --git a/runtime/coreclr-bridge.m b/runtime/coreclr-bridge.m index 8554e64980c9..e55c125461c8 100644 --- a/runtime/coreclr-bridge.m +++ b/runtime/coreclr-bridge.m @@ -213,10 +213,6 @@ * Ref: https://github.com/dotnet/designs/blob/1bb5844c165195e2f633cb1dbe042c4b92aefc4d/accepted/2021/objectivec-interop.md */ -struct TrackedObjectInfo { - struct NSObjectData* data; -}; - void xamarin_bridge_setup () { @@ -296,10 +292,10 @@ // But we can access the native memory given to us when the object was toggled // (and which is passed as the 'ptr' argument), so let's get the data we need from there. int rv = 0; - struct TrackedObjectInfo *info = (struct TrackedObjectInfo *) ptr; - enum NSObjectFlags flags = (enum NSObjectFlags) info->data->flags; + struct NSObjectData *data = (struct NSObjectData *) ptr; + enum NSObjectFlags flags = (enum NSObjectFlags) data->flags; bool isRegisteredToggleRef = (flags & NSObjectFlagsRegisteredToggleRef) == NSObjectFlagsRegisteredToggleRef; - id handle = info->data->handle; + id handle = data->handle; MonoToggleRefStatus res = (MonoToggleRefStatus) 0; if (isRegisteredToggleRef) { @@ -337,9 +333,9 @@ void xamarin_coreclr_reference_tracking_tracked_object_entered_finalization (void* ptr) { - struct TrackedObjectInfo *info = (struct TrackedObjectInfo *) ptr; - info->data->flags = (enum NSObjectFlags) (info->data->flags | NSObjectFlagsInFinalizerQueue); - LOG_CORECLR (stderr, "%s (%p) flags: %i\n", __func__, ptr, (int) info->flags); + struct NSObjectData *data = (struct NSObjectData *) ptr; + data->flags = (enum NSObjectFlags) (data->flags | NSObjectFlagsInFinalizerQueue); + LOG_CORECLR (stderr, "%s (%p) flags: %i\n", __func__, ptr, (int) data->flags); } void diff --git a/src/Foundation/NSObject2.cs b/src/Foundation/NSObject2.cs index 92657093752f..22498aeed364 100644 --- a/src/Foundation/NSObject2.cs +++ b/src/Foundation/NSObject2.cs @@ -120,7 +120,7 @@ internal interface INSObjectFactory { #if !COREBUILD // Allocated in native memory, so that it can be accessed from native code without having to deal with the GC. // This is mirrored in runtime.h and the definition needs to be in sync. - struct NSObjectData { + internal struct NSObjectData { public NativeHandle handle; public NSObject.Flags flags; } @@ -228,18 +228,24 @@ unsafe NativeHandle handle { if (data != IntPtr.Zero) return (NSObjectData*) data; - var data_handle = new NSObjectDataHandle (); - var existing_data = Interlocked.CompareExchange (ref __data, (IntPtr) data_handle.Data, IntPtr.Zero); - if (existing_data != IntPtr.Zero) { - // return the existing data, the GC will collect the other one we just created - return (NSObjectData*) existing_data; + if (Runtime.IsCoreCLR) { + data = (IntPtr) Runtime.GetTaggedMemory (this); + __data = data; // Runtime.GetTaggedMemory will always return the same pointer for the same object, so no synchronization is needed here (redundant writes are benign). + return (NSObjectData*) data; + } else { + var data_handle = new NSObjectDataHandle (); + var existing_data = Interlocked.CompareExchange (ref __data, (IntPtr) data_handle.Data, IntPtr.Zero); + if (existing_data != IntPtr.Zero) { + // return the existing data, the GC will collect the other one we just created + return (NSObjectData*) existing_data; + } + // tell the data handle we just created to track us + data_handle.CreateHandle (this); + // make sure the data isn't freed before this NSObject is collected, but also + // that it is freed after this NSObject is collected. + data_table.Add (this, data_handle); + return data_handle.Data; } - // tell the data handle we just created to track us - data_handle.CreateHandle (this); - // make sure the data isn't freed before this NSObject is collected, but also - // that it is freed after this NSObject is collected. - data_table.Add (this, data_handle); - return data_handle.Data; } unsafe Flags flags { @@ -417,7 +423,8 @@ unsafe NativeHandle GetSuper () internal static NativeHandle Initialize () { - data_table = new ConditionalWeakTable (); + if (!Runtime.IsCoreCLR) + data_table = new ConditionalWeakTable (); super_map = new ConditionalWeakTable (); return class_ptr; } diff --git a/src/ObjCRuntime/Runtime.CoreCLR.cs b/src/ObjCRuntime/Runtime.CoreCLR.cs index fff4e97daf4f..c4cf3636de4b 100644 --- a/src/ObjCRuntime/Runtime.CoreCLR.cs +++ b/src/ObjCRuntime/Runtime.CoreCLR.cs @@ -205,28 +205,35 @@ static void RaiseAppDomainUnhandledExceptionEvent (IntPtr gchandle) ExceptionHandling.RaiseAppDomainUnhandledExceptionEvent (exception); } - // Size: 2 pointers - internal struct TrackedObjectInfo { - public unsafe NSObjectData* Data; - } - [SupportedOSPlatform ("macos")] internal static GCHandle CreateTrackingGCHandle (NSObject obj, IntPtr handle) { var gchandle = ObjectiveCMarshal.CreateReferenceTrackingHandle (obj, out var info); unsafe { - TrackedObjectInfo* tracked_info; fixed (void* ptr = info) - tracked_info = (TrackedObjectInfo*) ptr; - tracked_info->Data = obj.GetData (); - - log_coreclr ($"GetOrCreateTrackingGCHandle ({obj.GetType ().FullName}, 0x{handle.ToString ("x")}) => Info=0x{((IntPtr) tracked_info).ToString ("x")} Data=0x{(IntPtr) tracked_info->Data:x} Created new"); + log_coreclr ($"GetOrCreateTrackingGCHandle ({obj.GetType ().FullName}, 0x{handle.ToString ("x")}) => 0x{((IntPtr) ptr).ToString ("x")} Created new"); } return gchandle; } + internal unsafe static void* GetTaggedMemory (NSObject obj) + { + // If https://github.com/dotnet/runtime/issues/128476 is accepted and implemented, + // can just call that new API instead of calling ObjectiveCMarshal.CreateReferenceTrackingHandle and + // freeing the returned handle. + + var gchandle = ObjectiveCMarshal.CreateReferenceTrackingHandle (obj, out var info); + // We only care about the tagged memory ('info'), so just free the GCHandle. + // We might want to request an API to just get the tagged memory at some point. + gchandle.Free (); + // The tagged memory pointer is guaranteed to be the same for every call to ObjectiveCMarshal.CreateReferenceTrackingHandle, + // and it will automatically be freed once the 'obj' is freed by the GC (in particular _once the instance is freed_, + // not _once the instance is collectible_, which is a very important distinction for us). + return Unsafe.AsPointer (ref info.GetPinnableReference ()); + } + // See "Toggle-ref support for CoreCLR" in coreclr-bridge.m for more information. internal static void RegisterToggleReferenceCoreCLR (NSObject obj, IntPtr handle, bool isCustomType) { From c284e3eba0f0fcb5030c5b43dee4bb09d72c31a4 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 25 May 2026 11:28:00 +0200 Subject: [PATCH 08/79] [devops] Remove the fix to avoid using a leaked GitHub ssh key. (#25504) The current script breaks the build: + ssh-keygen -R github.com Cannot stat /Users/builder/.ssh/known_hosts: No such file or directory ##[error]Bash exited with code '255'. The script was added over three years ago, and all our bots have been re-imaged multiple times since then, thus this isn't an issue anymore. So just remove the script. --- .../automation/scripts/bash/fix-github-ssh-key.sh | 10 ---------- tools/devops/automation/templates/common/setup.yml | 3 --- 2 files changed, 13 deletions(-) delete mode 100755 tools/devops/automation/scripts/bash/fix-github-ssh-key.sh diff --git a/tools/devops/automation/scripts/bash/fix-github-ssh-key.sh b/tools/devops/automation/scripts/bash/fix-github-ssh-key.sh deleted file mode 100755 index a603c99bc60d..000000000000 --- a/tools/devops/automation/scripts/bash/fix-github-ssh-key.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -ex - -# ensure that we do remove the old ssh key from github which was leaked: https://blog.gitguardian.com/github-exposed-private-ssh-key/ - -ssh-keygen -R github.com -{ - echo "github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl" - echo "github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=" - echo "github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=" -} >> ~/.ssh/known_hosts diff --git a/tools/devops/automation/templates/common/setup.yml b/tools/devops/automation/templates/common/setup.yml index a713adac8593..a80833941c2f 100644 --- a/tools/devops/automation/templates/common/setup.yml +++ b/tools/devops/automation/templates/common/setup.yml @@ -22,9 +22,6 @@ steps: name: disableCodeQLOnArm64 condition: and(succeeded(), eq('${{ parameters.disableCodeQL }}', 'true')) -- bash: $(Build.SourcesDirectory)/$(BUILD_REPOSITORY_TITLE)/tools/devops/automation/scripts/bash/fix-github-ssh-key.sh - displayName: 'Fix GitHub SSH host key' - - pwsh: '& "$Env:SYSTEM_DEFAULTWORKINGDIRECTORY/$Env:BUILD_REPOSITORY_TITLE/tools/devops/automation/scripts/show_bot_info.ps1"' displayName: 'Show Bot Info' From 97a86d5a903c299cede26d90e48a37fd29d684f2 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 25 May 2026 18:20:55 +0200 Subject: [PATCH 09/79] [tests] Upload expected app size files as artifacts on test failure (#25482) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When app size tests fail in Azure DevOps, write the updated expected files to `$(Build.ArtifactStagingDirectory)/updated-expected-sizes/` so they get published as a pipeline artifact. This makes it easy to download and apply the corrected files without having to reproduce the build locally. Also add a Copilot skill that can fetch the artifacts and apply them locally. Changes: - **AppSizeTest.cs**: When differences are detected (either app size exceeds tolerance OR the file list changed), write the updated expected file to the artifact staging directory. Falls back to a temp directory when not running in CI. - **run-tests.yml**: Add a `PublishPipelineArtifact@1` step to upload the `updated-expected-sizes` directory after tests complete. - **Tolerance fix**: Previously, artifacts were only uploaded when the total app size exceeded tolerance. If files were added/removed from the bundle but the total size stayed within tolerance, no artifact was produced. Now both size and file-list changes trigger artifact upload. - **Expected file updates**: Update expected files to match current CI output. - **Copilot skill** (`.github/skills/update-expected-app-size/`): Helps download these artifacts from Azure DevOps and apply them locally. 🤖 Pull request created by Copilot --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../skills/update-expected-app-size/SKILL.md | 126 ++++++++++++++++++ tests/dotnet/UnitTests/AppSizeTest.cs | 69 +++++++--- .../expected/MacCatalyst-MonoVM-size.txt | 8 +- ...reCLR-Interpreter-TrimmableStatic-size.txt | 10 +- .../expected/TVOS-MonoVM-interpreter-size.txt | 6 +- .../TVOS-NativeAOT-TrimmableStatic-size.txt | 6 +- .../expected/iOS-MonoVM-interpreter-size.txt | 6 +- .../UnitTests/expected/iOS-NativeAOT-size.txt | 6 +- .../automation/templates/tests/run-tests.yml | 9 ++ 9 files changed, 206 insertions(+), 40 deletions(-) create mode 100644 .github/skills/update-expected-app-size/SKILL.md diff --git a/.github/skills/update-expected-app-size/SKILL.md b/.github/skills/update-expected-app-size/SKILL.md new file mode 100644 index 000000000000..88d888c17a0e --- /dev/null +++ b/.github/skills/update-expected-app-size/SKILL.md @@ -0,0 +1,126 @@ +--- +name: update-expected-app-size +description: >- + Download updated expected app size files from Azure DevOps CI artifacts for + the current branch. Use when app size tests fail in CI and the user wants to + update the expected files locally. Trigger on "update app size", "download + expected files", "fix app size test", or "update expected app size files". +--- + +# Update Expected App Size Files + +Download updated expected app size files from Azure DevOps artifacts for the current branch's PR build, and apply them to the repository. + +## When to Use + +- App size tests failed in CI and the user wants to update the expected files +- User asks to "update app size", "download expected files", or "fix app size test failures" +- User wants to pull the updated expected files from a recent CI build + +## Background + +The app size tests (`tests/dotnet/UnitTests/AppSizeTest.cs`) compare the built app's size and preserved APIs against expected files stored in `tests/dotnet/UnitTests/expected/`. When the test detects a difference and `WRITE_KNOWN_FAILURES` is not set, it writes the updated expected file to `$(Build.ArtifactStagingDirectory)/updated-expected-sizes/`, and a pipeline step publishes this directory as a build artifact. + +The artifact name follows the pattern `updated-expected-sizes-{testPrefix}-{attempt}` (e.g., `updated-expected-sizes-dotnettests_ios-1`). Inside the artifact, files are named: +- `{Platform}-{Runtime}-size.txt` — e.g., `iOS-MonoVM-size.txt` +- `{Platform}-{Runtime}-preservedapis.txt` — e.g., `iOS-MonoVM-preservedapis.txt` + +The expected files on disk are at: +- `tests/dotnet/UnitTests/expected/{Platform}-{Runtime}-size.txt` +- `tests/dotnet/UnitTests/expected/{Platform}-{Runtime}-preservedapis.txt` + +## Workflow + +### 1. Determine the current branch and PR + +```bash +BRANCH=$(git branch --show-current) +# Find the PR number for this branch +gh pr list --head "$BRANCH" --repo dotnet/macios --json number,url --jq '.[0]' +``` + +If no PR is found, inform the user that this skill requires a PR to exist for the current branch (so that CI has run). + +### 2. Find the Azure DevOps build + +The CI builds for PRs in dotnet/macios run in the `devdiv` Azure DevOps organization, project `DevDiv`. + +Use the GitHub PR checks to find the Azure DevOps build URL: + +```bash +gh pr checks --repo dotnet/macios +``` + +Look for a check that links to Azure DevOps. The build URL will look like: +``` +https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=XXXXXXX +``` + +Extract the `buildId` from the URL. + +### 3. Download the artifacts + +Use the Azure DevOps REST API to list and download artifacts: + +```bash +# List artifacts for the build +TOKEN=$(az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv) +curl -s "https://devdiv.visualstudio.com/DevDiv/_apis/build/builds/{buildId}/artifacts?api-version=7.0" \ + -H "Authorization: Bearer $TOKEN" +``` + +Look for artifacts whose names contain `updated-expected-sizes` (e.g., `updated-expected-sizes-dotnettests_ios-1`). Get the artifact's `downloadUrl` and download it: + +```bash +# Get the download URL for a specific artifact +ARTIFACT_INFO=$(curl -s "https://devdiv.visualstudio.com/DevDiv/_apis/build/builds/{buildId}/artifacts?artifactName={artifactName}&api-version=7.0" \ + -H "Authorization: Bearer $TOKEN") +DOWNLOAD_URL=$(echo "$ARTIFACT_INFO" | python3 -c "import sys,json; print(json.load(sys.stdin)['resource']['downloadUrl'])") + +# Download the artifact zip +curl -sL "$DOWNLOAD_URL" -H "Authorization: Bearer $TOKEN" -o artifact.zip +``` + +If `az` is not available or not authenticated, direct the user to download manually from the Azure DevOps build artifacts page. + +### 4. Place the files + +Extract the downloaded artifact zip and place the files in the expected directory: + +```bash +EXPECTED_DIR="tests/dotnet/UnitTests/expected" +unzip -o artifact.zip -d /tmp/updated-sizes/ +cp /tmp/updated-sizes/*/*.txt "$EXPECTED_DIR/" +``` + +The files inside the zip already have the correct names (e.g., `iOS-MonoVM-size.txt`) and can be copied directly. + +### 5. Verify and commit + +After placing the files: +1. Run `git diff` to show what changed +2. Ask the user if the changes look correct +3. If confirmed, commit the changes: + ```bash + git add tests/dotnet/UnitTests/expected/ + git commit -m "[tests] Update expected app size files" + ``` + +## Fallback: Manual Download + +If automated download fails (auth issues, etc.), provide the user with: +1. The Azure DevOps build URL +2. Instructions to navigate to the build → Summary → Artifacts section +3. Look for individual artifacts whose names match the patterns above +4. Download each file and place it as `tests/dotnet/UnitTests/expected/{artifactName}.txt` + +## Fallback: Run Locally + +If the user can build locally, they can update the expected files directly: + +```bash +WRITE_KNOWN_FAILURES=1 tests-dotnet AppSizeTest +``` + +This runs the tests, updates the expected files in place, and marks the tests as passed. + diff --git a/tests/dotnet/UnitTests/AppSizeTest.cs b/tests/dotnet/UnitTests/AppSizeTest.cs index 047d3613c45f..db6d4d432dbf 100644 --- a/tests/dotnet/UnitTests/AppSizeTest.cs +++ b/tests/dotnet/UnitTests/AppSizeTest.cs @@ -114,7 +114,8 @@ void Run (ApplePlatform platform, string runtimeIdentifiers, string configuratio DotNet.AssertBuild (project_path, properties); // FORCE_UPDATE_KNOWN_FAILURES will update the known failures files even if the test doesn't actually fail - // WRITE_KNOWN_FAILURES will only update the known failures files if the test fails + // WRITE_KNOWN_FAILURES will only update the known failures files if the test fails (and mark the test as passed) + // If neither is set, the updated expected file is uploaded as an Azure DevOps artifact. var forceUpdate = !string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("FORCE_UPDATE_KNOWN_FAILURES")); var update = forceUpdate || !string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("WRITE_KNOWN_FAILURES")); @@ -167,30 +168,21 @@ static void AssertAppSize (ApplePlatform platform, string name, string appPath, msg = $"App size changed significantly ({FormatBytes (appSizeDifference, true)} different > tolerance of +-{FormatBytes (toleranceInBytes)}). Expected app size: {FormatBytes (expectedAppBundleSize)}, actual app size: {FormatBytes (appBundleSize)}."; } - var updated = false; - if (forceUpdate || (update && !withinTolerance)) { - Directory.CreateDirectory (expectedDirectory); - File.WriteAllText (expectedSizeReportPath, report.ToString ()); - msg += " Check the modified files for more information."; - updated = true; - } else if (!withinTolerance) { - msg += " Set the environment variable WRITE_KNOWN_FAILURES=1, run the test again, and verify the modified files for more information."; - } - Console.WriteLine ($" {msg}"); + // Compare individual files in the app bundle var expectedLines = expectedSizeReport.SplitLines ().Skip (2).Where (v => v.IndexOf (':') >= 0).ToDictionary (v => v [..v.IndexOf (':')], v => v [(v.IndexOf (':') + 1)..]); var actualLines = report.ToString ().SplitLines ().Skip (2).Where (v => v.IndexOf (':') >= 0).ToDictionary (v => v [..v.IndexOf (':')], v => v [(v.IndexOf (':') + 1)..]); var allKeys = expectedLines.Keys.Union (actualLines.Keys).OrderBy (v => v); + var filesAdded = new List (); + var filesRemoved = new List (); foreach (var key in allKeys) { if (!expectedLines.TryGetValue (key, out var expectedLine)) { Console.WriteLine ($" File '{key}' was added to app bundle: {actualLines [key]}"); - if (!updated) - Assert.Fail ($"The file '{key}' was added to the app bundle."); + filesAdded.Add (key); } else if (!actualLines.TryGetValue (key, out var actualLine)) { Console.WriteLine ($" File '{key}' was removed from app bundle: {expectedLine}"); - if (!updated) - Assert.Fail ($"The file '{key}' was removed from the app bundle."); + filesRemoved.Add (key); } else if (expectedLine != actualLine) { Console.WriteLine ($" File '{key}' changed in app bundle:"); Console.WriteLine ($" -{expectedLine}"); @@ -198,8 +190,27 @@ static void AssertAppSize (ApplePlatform platform, string name, string appPath, } } - if (!updated && !withinTolerance) - Assert.Fail (msg); + // Determine if there are any meaningful differences + var hasFileDifferences = filesAdded.Count > 0 || filesRemoved.Count > 0; + var hasSizeDifference = !withinTolerance; + var hasDifferences = hasFileDifferences || hasSizeDifference; + + if (forceUpdate || (update && hasDifferences)) { + Directory.CreateDirectory (expectedDirectory); + File.WriteAllText (expectedSizeReportPath, report.ToString ()); + Console.WriteLine ($" Updated expected file: {expectedSizeReportPath}"); + } else if (hasDifferences) { + UploadUpdatedExpectedFile (expectedSizeReportPath, report.ToString ()); + if (hasFileDifferences) { + var details = new List (); + foreach (var key in filesAdded) + details.Add ($"added: '{key}'"); + foreach (var key in filesRemoved) + details.Add ($"removed: '{key}'"); + Assert.Fail ($"The app bundle's file list changed ({string.Join (", ", details)}). The updated expected file is available as a build artifact (set WRITE_KNOWN_FAILURES=1 to update locally)."); + } + Assert.Fail ($"{msg} The updated expected file is available as a build artifact (set WRITE_KNOWN_FAILURES=1 to update locally)."); + } } // Create a file with all the APIs that survived the trimmer; this can be useful to determine what is not trimmed away. @@ -238,9 +249,29 @@ void AssertAssemblyReport (ApplePlatform platform, string name, string appPath, } if (!update) { - Assert.That (addedAPIs, Is.Empty, "No added APIs (set the environment variable WRITE_KNOWN_FAILURES=1 and run the test again to update the expected set of APIs)"); - Assert.That (removedAPIs, Is.Empty, "No removed APIs (set the environment variable WRITE_KNOWN_FAILURES=1 and run the test again to update the expected set of APIs)"); + if (addedAPIs.Count > 0 || removedAPIs.Count > 0) { + UploadUpdatedExpectedFile (expectedFile, string.Join ('\n', preservedAPIs) + "\n"); + var updateMsg = " The updated expected file is available as a build artifact (set WRITE_KNOWN_FAILURES=1 to update locally)."; + Assert.That (addedAPIs, Is.Empty, "No added APIs." + updateMsg); + Assert.That (removedAPIs, Is.Empty, "No removed APIs." + updateMsg); + } + } + } + + static void UploadUpdatedExpectedFile (string expectedFilePath, string content) + { + var fileName = Path.GetFileName (expectedFilePath); + var artifactStagingDir = Environment.GetEnvironmentVariable ("BUILD_ARTIFACTSTAGINGDIRECTORY"); + string outputDir; + if (!string.IsNullOrEmpty (artifactStagingDir)) { + outputDir = Path.Combine (artifactStagingDir, "updated-expected-sizes"); + } else { + outputDir = Path.Combine (Cache.CreateTemporaryDirectory ("AppSizeTest"), "updated-expected-sizes"); } + Directory.CreateDirectory (outputDir); + var outputFile = Path.Combine (outputDir, fileName); + File.WriteAllText (outputFile, content); + Console.WriteLine ($" Updated expected file written to: {outputFile}"); } static string FormatBytes (long bytes, bool alwaysShowSign = false) diff --git a/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-size.txt b/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-size.txt index 48f7aafc2dad..b2abcecdbae4 100644 --- a/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-size.txt +++ b/tests/dotnet/UnitTests/expected/MacCatalyst-MonoVM-size.txt @@ -1,10 +1,10 @@ -AppBundleSize: 16,325,774 bytes (15,943.1 KB = 15.6 MB) +AppBundleSize: 16,321,420 bytes (15,938.9 KB = 15.6 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: Contents/_CodeSignature/CodeResources: 4,134 bytes (4.0 KB = 0.0 MB) -Contents/Info.plist: 1,119 bytes (1.1 KB = 0.0 MB) -Contents/MacOS/SizeTestApp: 13,814,640 bytes (13,490.9 KB = 13.2 MB) +Contents/Info.plist: 1,085 bytes (1.1 KB = 0.0 MB) +Contents/MacOS/SizeTestApp: 13,810,240 bytes (13,486.6 KB = 13.2 MB) Contents/MonoBundle/aot-instances.aotdata.arm64: 1,045,032 bytes (1,020.5 KB = 1.0 MB) -Contents/MonoBundle/Microsoft.MacCatalyst.aotdata.arm64: 36,288 bytes (35.4 KB = 0.0 MB) +Contents/MonoBundle/Microsoft.MacCatalyst.aotdata.arm64: 36,368 bytes (35.5 KB = 0.0 MB) Contents/MonoBundle/Microsoft.MacCatalyst.dll: 50,688 bytes (49.5 KB = 0.0 MB) Contents/MonoBundle/runtimeconfig.bin: 1,481 bytes (1.4 KB = 0.0 MB) Contents/MonoBundle/SizeTestApp.aotdata.arm64: 1,552 bytes (1.5 KB = 0.0 MB) diff --git a/tests/dotnet/UnitTests/expected/MacOSX-CoreCLR-Interpreter-TrimmableStatic-size.txt b/tests/dotnet/UnitTests/expected/MacOSX-CoreCLR-Interpreter-TrimmableStatic-size.txt index 726ac17a14c8..e639fde89acf 100644 --- a/tests/dotnet/UnitTests/expected/MacOSX-CoreCLR-Interpreter-TrimmableStatic-size.txt +++ b/tests/dotnet/UnitTests/expected/MacOSX-CoreCLR-Interpreter-TrimmableStatic-size.txt @@ -1,12 +1,12 @@ -AppBundleSize: 260,131,912 bytes (254,035.1 KB = 248.1 MB) +AppBundleSize: 260,129,862 bytes (254,033.1 KB = 248.1 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: Contents/_CodeSignature/CodeResources: 67,868 bytes (66.3 KB = 0.1 MB) -Contents/Info.plist: 750 bytes (0.7 KB = 0.0 MB) -Contents/MacOS/SizeTestApp: 7,431,792 bytes (7,257.6 KB = 7.1 MB) +Contents/Info.plist: 716 bytes (0.7 KB = 0.0 MB) +Contents/MacOS/SizeTestApp: 7,430,800 bytes (7,256.6 KB = 7.1 MB) Contents/MonoBundle/.xamarin/osx-arm64/_Microsoft.macOS.TypeMap.dll: 4,847,616 bytes (4,734.0 KB = 4.6 MB) Contents/MonoBundle/.xamarin/osx-arm64/_SizeTestApp.TypeMap.dll: 3,072 bytes (3.0 KB = 0.0 MB) Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.CSharp.dll: 893,192 bytes (872.3 KB = 0.9 MB) -Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.macOS.dll: 38,524,416 bytes (37,621.5 KB = 36.7 MB) +Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.macOS.dll: 38,523,904 bytes (37,621.0 KB = 36.7 MB) Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.VisualBasic.Core.dll: 1,335,096 bytes (1,303.8 KB = 1.3 MB) Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.VisualBasic.dll: 17,680 bytes (17.3 KB = 0.0 MB) Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.Win32.Primitives.dll: 16,144 bytes (15.8 KB = 0.0 MB) @@ -182,7 +182,7 @@ Contents/MonoBundle/.xamarin/osx-arm64/WindowsBase.dll: 16,688 bytes (16.3 KB = Contents/MonoBundle/.xamarin/osx-x64/_Microsoft.macOS.TypeMap.dll: 4,847,616 bytes (4,734.0 KB = 4.6 MB) Contents/MonoBundle/.xamarin/osx-x64/_SizeTestApp.TypeMap.dll: 3,072 bytes (3.0 KB = 0.0 MB) Contents/MonoBundle/.xamarin/osx-x64/Microsoft.CSharp.dll: 796,432 bytes (777.8 KB = 0.8 MB) -Contents/MonoBundle/.xamarin/osx-x64/Microsoft.macOS.dll: 38,524,416 bytes (37,621.5 KB = 36.7 MB) +Contents/MonoBundle/.xamarin/osx-x64/Microsoft.macOS.dll: 38,523,904 bytes (37,621.0 KB = 36.7 MB) Contents/MonoBundle/.xamarin/osx-x64/Microsoft.VisualBasic.Core.dll: 1,166,648 bytes (1,139.3 KB = 1.1 MB) Contents/MonoBundle/.xamarin/osx-x64/Microsoft.VisualBasic.dll: 17,680 bytes (17.3 KB = 0.0 MB) Contents/MonoBundle/.xamarin/osx-x64/Microsoft.Win32.Primitives.dll: 16,176 bytes (15.8 KB = 0.0 MB) diff --git a/tests/dotnet/UnitTests/expected/TVOS-MonoVM-interpreter-size.txt b/tests/dotnet/UnitTests/expected/TVOS-MonoVM-interpreter-size.txt index aa1d0fe08b6c..4a3f73193711 100644 --- a/tests/dotnet/UnitTests/expected/TVOS-MonoVM-interpreter-size.txt +++ b/tests/dotnet/UnitTests/expected/TVOS-MonoVM-interpreter-size.txt @@ -1,12 +1,12 @@ -AppBundleSize: 3,616,438 bytes (3,531.7 KB = 3.4 MB) +AppBundleSize: 3,616,596 bytes (3,531.8 KB = 3.4 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: _CodeSignature/CodeResources: 3,999 bytes (3.9 KB = 0.0 MB) archived-expanded-entitlements.xcent: 384 bytes (0.4 KB = 0.0 MB) -Info.plist: 1,138 bytes (1.1 KB = 0.0 MB) +Info.plist: 1,104 bytes (1.1 KB = 0.0 MB) Microsoft.tvOS.dll: 154,624 bytes (151.0 KB = 0.1 MB) PkgInfo: 8 bytes (0.0 KB = 0.0 MB) runtimeconfig.bin: 1,405 bytes (1.4 KB = 0.0 MB) -SizeTestApp: 2,404,416 bytes (2,348.1 KB = 2.3 MB) +SizeTestApp: 2,404,608 bytes (2,348.2 KB = 2.3 MB) SizeTestApp.dll: 7,680 bytes (7.5 KB = 0.0 MB) System.Private.CoreLib.aotdata.arm64: 41,312 bytes (40.3 KB = 0.0 MB) System.Private.CoreLib.dll: 988,160 bytes (965.0 KB = 0.9 MB) diff --git a/tests/dotnet/UnitTests/expected/TVOS-NativeAOT-TrimmableStatic-size.txt b/tests/dotnet/UnitTests/expected/TVOS-NativeAOT-TrimmableStatic-size.txt index 756b1289a4de..e01435700c48 100644 --- a/tests/dotnet/UnitTests/expected/TVOS-NativeAOT-TrimmableStatic-size.txt +++ b/tests/dotnet/UnitTests/expected/TVOS-NativeAOT-TrimmableStatic-size.txt @@ -1,8 +1,8 @@ -AppBundleSize: 7,922,040 bytes (7,736.4 KB = 7.6 MB) +AppBundleSize: 7,922,198 bytes (7,736.5 KB = 7.6 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: _CodeSignature/CodeResources: 2,589 bytes (2.5 KB = 0.0 MB) archived-expanded-entitlements.xcent: 384 bytes (0.4 KB = 0.0 MB) -Info.plist: 1,138 bytes (1.1 KB = 0.0 MB) +Info.plist: 1,104 bytes (1.1 KB = 0.0 MB) PkgInfo: 8 bytes (0.0 KB = 0.0 MB) runtimeconfig.bin: 1,889 bytes (1.8 KB = 0.0 MB) -SizeTestApp: 7,916,032 bytes (7,730.5 KB = 7.5 MB) +SizeTestApp: 7,916,224 bytes (7,730.7 KB = 7.5 MB) diff --git a/tests/dotnet/UnitTests/expected/iOS-MonoVM-interpreter-size.txt b/tests/dotnet/UnitTests/expected/iOS-MonoVM-interpreter-size.txt index ebe56d99fd5b..93230234dc12 100644 --- a/tests/dotnet/UnitTests/expected/iOS-MonoVM-interpreter-size.txt +++ b/tests/dotnet/UnitTests/expected/iOS-MonoVM-interpreter-size.txt @@ -1,12 +1,12 @@ -AppBundleSize: 3,616,747 bytes (3,532.0 KB = 3.4 MB) +AppBundleSize: 3,603,385 bytes (3,518.9 KB = 3.4 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: _CodeSignature/CodeResources: 3,997 bytes (3.9 KB = 0.0 MB) archived-expanded-entitlements.xcent: 384 bytes (0.4 KB = 0.0 MB) -Info.plist: 1,161 bytes (1.1 KB = 0.0 MB) +Info.plist: 1,127 bytes (1.1 KB = 0.0 MB) Microsoft.iOS.dll: 154,624 bytes (151.0 KB = 0.1 MB) PkgInfo: 8 bytes (0.0 KB = 0.0 MB) runtimeconfig.bin: 1,405 bytes (1.4 KB = 0.0 MB) -SizeTestApp: 2,404,704 bytes (2,348.3 KB = 2.3 MB) +SizeTestApp: 2,391,376 bytes (2,335.3 KB = 2.3 MB) SizeTestApp.dll: 7,680 bytes (7.5 KB = 0.0 MB) System.Private.CoreLib.aotdata.arm64: 41,312 bytes (40.3 KB = 0.0 MB) System.Private.CoreLib.dll: 988,160 bytes (965.0 KB = 0.9 MB) diff --git a/tests/dotnet/UnitTests/expected/iOS-NativeAOT-size.txt b/tests/dotnet/UnitTests/expected/iOS-NativeAOT-size.txt index 9977114c7ece..5cc462cd43e5 100644 --- a/tests/dotnet/UnitTests/expected/iOS-NativeAOT-size.txt +++ b/tests/dotnet/UnitTests/expected/iOS-NativeAOT-size.txt @@ -1,8 +1,8 @@ -AppBundleSize: 2,800,030 bytes (2,734.4 KB = 2.7 MB) +AppBundleSize: 2,784,380 bytes (2,719.1 KB = 2.7 MB) # The following list of files and their sizes is just informational / for review, and isn't used in the test: _CodeSignature/CodeResources: 2,589 bytes (2.5 KB = 0.0 MB) archived-expanded-entitlements.xcent: 384 bytes (0.4 KB = 0.0 MB) -Info.plist: 1,161 bytes (1.1 KB = 0.0 MB) +Info.plist: 1,127 bytes (1.1 KB = 0.0 MB) PkgInfo: 8 bytes (0.0 KB = 0.0 MB) runtimeconfig.bin: 1,808 bytes (1.8 KB = 0.0 MB) -SizeTestApp: 2,794,080 bytes (2,728.6 KB = 2.7 MB) +SizeTestApp: 2,778,464 bytes (2,713.3 KB = 2.6 MB) diff --git a/tools/devops/automation/templates/tests/run-tests.yml b/tools/devops/automation/templates/tests/run-tests.yml index 188474aa28ea..40c85db23a1a 100644 --- a/tools/devops/automation/templates/tests/run-tests.yml +++ b/tools/devops/automation/templates/tests/run-tests.yml @@ -172,6 +172,15 @@ steps: continueOnError: true condition: succeededOrFailed() +# Upload updated expected app size files if the app size tests produced any. +- task: PublishPipelineArtifact@1 + displayName: 'Publish Artifact: Updated expected app size files' + inputs: + targetPath: '$(Build.ArtifactStagingDirectory)/updated-expected-sizes' + artifactName: '${{ parameters.uploadPrefix }}updated-expected-sizes-${{ parameters.testPrefix }}-$(System.JobAttempt)' + continueOnError: true + condition: succeededOrFailed() + - pwsh: | $summaryName = "TestSummary-${{ parameters.testPrefix }}.md" $summaryPath = "$Env:SYSTEM_DEFAULTWORKINGDIRECTORY/$(BUILD_REPOSITORY_TITLE)/tests/TestSummary.md" From 26af1ee903a23bb2c8def747be34ae4ac8e84fcf Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 25 May 2026 18:23:03 +0200 Subject: [PATCH 10/79] [msbuild] Improve how we handle the path to Xcode. (#24021) * Add support for an MSBuild property ('XcodeLocation') to set the Xcode location. * Use a new facility in macios-devtools (the 'XcodeLocator' class) to find Xcode. This required a bump to the corresponding submodule. * Deprecate the file-based locations. * Always set DEVELOPER_DIR when executing subprocesses. * Don't use static state to store the Xcode location. This fixes a problem where the static state would remain in memory when MSBuild's build server is being used, and then we'd do the wrong thing when building a different project using a different version of Xcode. Misc improvements: * We now warn if the selected Xcode is a symlink (ref: #21762). * The settings files are deprecated, and won't be used in .NET 11 (we'll warn about this in .NET 10). * The environment variable MD_APPLE_SDK_ROOT is deprecated, and won't be used in .NET 11 (we'll warn about this in .NET 10). This will hopefully remove some confusion about which Xcode is being used; now we just look at the system's Xcode, unless it's overridden by the MSBuild property 'XcodeLocation'. Fixes https://github.com/dotnet/macios/issues/3931. Fixes https://github.com/dotnet/macios/issues/11172. Fixes https://github.com/dotnet/macios/issues/21762. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Rolf Bjarne Kvinge --- Make.config | 1 - docs/building-apps/build-properties.md | 15 +++++ .../MSBStrings.resx | 16 +++++ msbuild/Xamarin.MacDev.Tasks/Sdks.cs | 61 ------------------- .../Tasks/CompileAppManifest.cs | 14 ++--- .../Tasks/CompileEntitlements.cs | 2 +- .../Tasks/DetectSdkLocation.cs | 60 +++++++++--------- .../Tasks/DetectSigningIdentity.cs | 8 +-- .../Tasks/ReadAppManifest.cs | 2 +- .../Xamarin.MacDev.Tasks/Tasks/XamarinTask.cs | 46 ++++++++++++++ msbuild/Xamarin.Shared/Xamarin.Shared.targets | 5 ++ tests/common/BinLog.cs | 3 + tests/common/Configuration.cs | 2 +- tests/dotnet/UnitTests/XcodeVersionTest.cs | 13 ++-- .../AssemblySetup.cs | 2 +- .../TaskTests/ACToolTaskTest.cs | 5 +- .../TaskTests/CompileAppManifestTaskTests.cs | 2 +- .../GeneratePlistTaskTests_Core.cs | 1 + .../GeneratePlistTaskTests_iOS.cs | 2 +- .../GeneratePlistTaskTests_tvOS.cs | 2 +- .../TaskTests/IBToolTaskTests.cs | 15 +++-- tests/package-mac-tests.sh | 2 +- .../Jenkins/TestTasks/AppleTestTask.cs | 2 +- 23 files changed, 146 insertions(+), 135 deletions(-) delete mode 100644 msbuild/Xamarin.MacDev.Tasks/Sdks.cs diff --git a/Make.config b/Make.config index 37e7563a336d..1a27f14f8f87 100644 --- a/Make.config +++ b/Make.config @@ -239,7 +239,6 @@ endif # Tell both Xcode and our build logic which Xcode we're using. export DEVELOPER_DIR=$(XCODE_DEVELOPER_ROOT) -export MD_APPLE_SDK_ROOT=$(abspath $(XCODE_DEVELOPER_ROOT)/../..) else # Without Xcode, set placeholder Xcode values but honor XCODE_CHANNEL so we configure bots correctly ifeq ($(XCODE_CHANNEL),Beta) diff --git a/docs/building-apps/build-properties.md b/docs/building-apps/build-properties.md index 8126bb6cd0ed..461d2986fdca 100644 --- a/docs/building-apps/build-properties.md +++ b/docs/building-apps/build-properties.md @@ -1505,6 +1505,21 @@ Consider using the unified [AppBundleResourcePrefix](#appbundleresourceprefix) p See also [IPhoneResourcePrefix](#iphoneresourceprefix) and [MonoMacResourcePrefix](#monomacresourceprefix). +## XcodeLocation + +Specifies the location of Xcode. + +When the build searches for Xcode, it's done in this order: + +1. If the `XcodeLocation` property is set, use that. Note that since all environment variables are automatically MSBuild properties as well, it's also possible to set the `XcodeLocation` environment variable for the same effect. +2. If the `MD_APPLE_SDK_ROOT` environment variable is set, use that. +3. If either of the files `~/Library/Preferences/maui/Settings.plist` or `~/Library/Preferences/Xamarin/Settings.plist` exist, and has the property list value `AppleSdkRoot`, use that. +4. Use the system version of Xcode (as determined by executing `xcode-select --print-path`). + +> [!WARNING] +> Support for the `MD_APPLE_SDK_ROOT` environment variable, and the `~/Library/Preferences/maui/Settings.plist` and `~/Library/Preferences/Xamarin/Settings.plist` files, is deprecated and will be removed in the future. +> Going forward, choose which Xcode to use by either making it the system's version of Xcode (either using `xcode-select --switch ...` on the command line, or in Xcode's settings), or by setting the `XcodeLocation` MSBuild property / environment variable. + ## ZipPath The full path to the `zip` command-line tool. diff --git a/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx b/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx index f85052143906..5f68eea415e1 100644 --- a/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx +++ b/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx @@ -1627,4 +1627,20 @@ The task '{0}' requires the property '{1}' to be set. Please file an issue at https://github.com/dotnet/macios/issues/new/choose. + + + The environment variable '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + + The environment variable '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + + The settings file '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + + The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + diff --git a/msbuild/Xamarin.MacDev.Tasks/Sdks.cs b/msbuild/Xamarin.MacDev.Tasks/Sdks.cs deleted file mode 100644 index ca04c9c665f1..000000000000 --- a/msbuild/Xamarin.MacDev.Tasks/Sdks.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.IO; - -using Xamarin.Localization.MSBuild; -using Xamarin.Utils; -using Xamarin.MacDev.Tasks; - -#nullable enable - -namespace Xamarin.MacDev { - public static class Sdks { - public static AppleIPhoneSdk IOS { get; private set; } - public static MacOSXSdk MacOS { get; private set; } - public static AppleTVOSSdk TVOS { get; private set; } - - static Sdks () - { - IOS = new AppleIPhoneSdk (AppleSdkSettings.DeveloperRoot, AppleSdkSettings.DeveloperRootVersionPlist); - TVOS = new AppleTVOSSdk (AppleSdkSettings.DeveloperRoot, AppleSdkSettings.DeveloperRootVersionPlist); - MacOS = new MacOSXSdk (AppleSdkSettings.DeveloperRoot, AppleSdkSettings.DeveloperRootVersionPlist); - } - - public static AppleSdk GetSdk (ApplePlatform framework) - { - switch (framework) { - case ApplePlatform.iOS: - return IOS; - case ApplePlatform.TVOS: - return TVOS; - default: - throw new InvalidOperationException (string.Format (MSBStrings.InvalidFramework, framework)); - } - } - - public static AppleSdk GetSdk (string targetFrameworkMoniker) - { - return GetSdk (PlatformFrameworkHelper.GetFramework (targetFrameworkMoniker)); - } - - public static IAppleSdk GetAppleSdk (ApplePlatform framework) - { - switch (framework) { - case ApplePlatform.iOS: - return IOS; - case ApplePlatform.TVOS: - return TVOS; - case ApplePlatform.MacCatalyst: - case ApplePlatform.MacOSX: - return MacOS; - default: - throw new InvalidOperationException (string.Format (MSBStrings.InvalidFramework, framework)); - } - } - - public static IAppleSdk GetAppleSdk (string targetFrameworkMoniker) - { - return GetAppleSdk (PlatformFrameworkHelper.GetFramework (targetFrameworkMoniker)); - } - - } -} diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/CompileAppManifest.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/CompileAppManifest.cs index fa798ad621be..f9650087d607 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/CompileAppManifest.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/CompileAppManifest.cs @@ -236,7 +236,7 @@ bool SetMinimumOSVersion (PDictionary plist) if (Platform == ApplePlatform.MacCatalyst && !string.IsNullOrEmpty (SupportedOSPlatformVersion)) { // SupportedOSPlatformVersion is the iOS version for Mac Catalyst. // But we need to store the macOS version in the app manifest, so convert it to the macOS version here. - if (!MacCatalystSupport.TryGetMacOSVersion (Sdks.GetAppleSdk (Platform).GetSdkPath (SdkVersion), SupportedOSPlatformVersion, out var convertedVersion, out var knowniOSVersions)) { + if (!MacCatalystSupport.TryGetMacOSVersion (CurrentSdk.GetSdkPath (SdkVersion), SupportedOSPlatformVersion, out var convertedVersion, out var knowniOSVersions)) { Log.LogError (MSBStrings.E0188, SupportedOSPlatformVersion, string.Join (", ", knowniOSVersions.OrderBy (v => v))); return false; } @@ -250,7 +250,7 @@ bool SetMinimumOSVersion (PDictionary plist) var minimumiOSVersionInManifest = plist.Get (ManifestKeys.MinimumOSVersion)?.Value; if (!string.IsNullOrEmpty (minimumiOSVersionInManifest)) { // Convert to the macOS version - if (!MacCatalystSupport.TryGetMacOSVersion (Sdks.GetAppleSdk (Platform).GetSdkPath (SdkVersion), minimumiOSVersionInManifest!, out var convertedVersion, out var knowniOSVersions)) { + if (!MacCatalystSupport.TryGetMacOSVersion (CurrentSdk.GetSdkPath (SdkVersion), minimumiOSVersionInManifest!, out var convertedVersion, out var knowniOSVersions)) { Log.LogError (MSBStrings.E0188, minimumiOSVersionInManifest, string.Join (", ", knowniOSVersions.OrderBy (v => v))); return false; } @@ -309,14 +309,12 @@ bool Compile (PDictionary plist) return false; } - var currentSDK = Sdks.GetAppleSdk (Platform); - sdkVersion = AppleSdkVersion.Parse (DefaultSdkVersion); - if (!currentSDK.SdkIsInstalled (sdkVersion, SdkIsSimulator)) { + if (!CurrentSdk.SdkIsInstalled (sdkVersion, SdkIsSimulator)) { Log.LogError (null, null, null, null, 0, 0, 0, 0, MSBStrings.E0013, Platform, sdkVersion); return false; } - SetXcodeValues (plist, currentSDK); + SetXcodeValues (plist, CurrentSdk); } switch (Platform) { @@ -424,7 +422,7 @@ void Validation (PDictionary plist) GetMinimumOSVersion (plist, out var minimumOSVersion); if (minimumOSVersion < new Version (11, 0)) { string miniOSVersion = "?"; - if (MacCatalystSupport.TryGetiOSVersion (Sdks.GetAppleSdk (Platform).GetSdkPath (SdkVersion), minimumOSVersion, out var iOSVersion, out var _)) + if (MacCatalystSupport.TryGetiOSVersion (CurrentSdk.GetSdkPath (SdkVersion), minimumOSVersion, out var iOSVersion, out var _)) miniOSVersion = iOSVersion?.ToString () ?? "?"; LogAppManifestError (MSBStrings.E7099 /* The UIDeviceFamily value '6' requires macOS 11.0. Please set the 'SupportedOSPlatformVersion' in the project file to at least 14.0 (the Mac Catalyst version equivalent of macOS 11.0). The current value is {0} (equivalent to macOS {1}). */, miniOSVersion, minimumOSVersion); } @@ -510,7 +508,7 @@ void SetXcodeValues (PDictionary plist, IAppleSdk currentSDK) SetValueIfNotNull (plist, "DTPlatformName", PlatformUtils.GetTargetPlatform (SdkPlatform, false)); SetValueIfNotNull (plist, "DTPlatformVersion", dtSettings.DTPlatformVersion); SetValueIfNotNull (plist, "DTSDKName", sdkSettings.CanonicalName); - SetValueIfNotNull (plist, "DTXcode", AppleSdkSettings.DTXcode); + SetValueIfNotNull (plist, "DTXcode", GetXcodeLocator ().DTXcode); SetValueIfNotNull (plist, "DTXcodeBuild", dtSettings.DTXcodeBuild); } diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/CompileEntitlements.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/CompileEntitlements.cs index 3f5ac630ef5c..3e2e85074957 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/CompileEntitlements.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/CompileEntitlements.cs @@ -117,7 +117,7 @@ string DefaultEntitlementsPath { return "Entitlements.plist"; } - return Path.Combine (Sdks.GetAppleSdk (TargetFrameworkMoniker).GetSdkPath (SdkVersion, false), "Entitlements.plist"); + return Path.Combine (CurrentSdk.GetSdkPath (SdkVersion, false), "Entitlements.plist"); } } diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/DetectSdkLocation.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/DetectSdkLocation.cs index 1f393504e6a8..f06f132d6a7b 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/DetectSdkLocation.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/DetectSdkLocation.cs @@ -29,6 +29,7 @@ public string SdkRoot { get; set; } = ""; + // this is input too (the variable 'XcodeLocation') [Output] public new string SdkDevPath { get => base.SdkDevPath; @@ -59,12 +60,6 @@ public string XcodeVersion { #endregion Outputs - protected IAppleSdk CurrentSdk { - get { - return Sdks.GetAppleSdk (Platform); - } - } - IAppleSdkVersion GetDefaultSdkVersion () { switch (Platform) { @@ -145,33 +140,40 @@ bool ExecuteImpl () return ExecuteRemotely (); } - AppleSdkSettings.Init (); - - if (EnsureAppleSdkRoot ()) - EnsureSdkPath (); - EnsureXamarinSdkRoot (); + var isNet11OrNewer = TargetFramework.Version.Major >= 11; + var appleSdkSettings = GetXcodeLocator (initialDiscovery: true, (locator) => { + locator.SupportEnvironmentVariableLookup = !isNet11OrNewer; + locator.SupportSettingsFileLookup = !isNet11OrNewer; + }); + SetXcodeLocator (appleSdkSettings); + SdkDevPath = appleSdkSettings.DeveloperRoot; + XcodeVersion = appleSdkSettings.XcodeVersion.ToString (); + + if (appleSdkSettings.SystemHasEnvironmentVariable) { + if (isNet11OrNewer) { + Log.LogWarning (MSBStrings.W7172 /* The environment variable '{0}' is deprecated, and will be ignored. Please set use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. */, XcodeLocator.EnvironmentVariableName); + } else { + Log.LogWarning (MSBStrings.W7171 /* The environment variable '{0}' is deprecated, and will be ignored in .NET 11+. Please set use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. */, XcodeLocator.EnvironmentVariableName); + } + } + foreach (var file in appleSdkSettings.SystemExistingSettingsFiles) { + if (isNet11OrNewer) { + Log.LogWarning (MSBStrings.W7174 /* The settings file '{0}' is deprecated, and will be ignored. Please set use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. */, file); + } else { + Log.LogWarning (MSBStrings.W7173 /* The settings file '{0}' is deprecated, and will be ignored in .NET 11+. Please set use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. */, file); + } + } - XcodeVersion = AppleSdkSettings.XcodeVersion.ToString (); + if (Log.HasLoggedErrors) + return false; - return !Log.HasLoggedErrors; - } + Log.LogMessage (MessageImportance.Low, "DeveloperRoot: {0}", CurrentSdk.DeveloperRoot); + Log.LogMessage (MessageImportance.Low, "GetPlatformPath: {0}", CurrentSdk.GetPlatformPath (SdkIsSimulator)); - protected bool EnsureAppleSdkRoot () - { - var currentSdk = CurrentSdk; - if (!currentSdk.IsInstalled) { - Log.LogError (MSBStrings.E0044v2 /* Could not find a valid Xcode app bundle at '{0}'. Please verify that 'xcode-select -p' points to your Xcode installation. For more information see https://aka.ms/macios-missing-xcode. */, AppleSdkSettings.InvalidDeveloperRoot); - return false; - } - Log.LogMessage (MessageImportance.Low, "DeveloperRoot: {0}", currentSdk.DeveloperRoot); - Log.LogMessage (MessageImportance.Low, "GetPlatformPath: {0}", currentSdk.GetPlatformPath (SdkIsSimulator)); + EnsureSdkPath (); + EnsureXamarinSdkRoot (); - SdkDevPath = currentSdk.DeveloperRoot; - if (string.IsNullOrEmpty (SdkDevPath)) { - Log.LogError (MSBStrings.E0086 /* Could not find a valid Xcode developer path */); - return false; - } - return true; + return !Log.HasLoggedErrors; } protected string? DirExists (string checkingFor, params string [] paths) diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/DetectSigningIdentity.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/DetectSigningIdentity.cs index 224c8e37edc4..8718934d6918 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/DetectSigningIdentity.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/DetectSigningIdentity.cs @@ -29,12 +29,6 @@ public class DetectSigningIdentity : XamarinTask, ITaskCallback, ICancelableTask static readonly string [] macDirectDistributionPrefixes = { "Developer ID Application" }; static readonly string [] macDevelopmentPrefixes = { "Mac Developer", "Apple Development" }; - protected string DeveloperRoot { - get { - return Sdks.GetAppleSdk (TargetFrameworkMoniker).DeveloperRoot; - } - } - protected string [] DevelopmentPrefixes { get { switch (Platform) { @@ -583,7 +577,7 @@ bool ExecuteImpl () else if (ProvisioningProfile == AutomaticAdHocProvision) type = MobileProvisionDistributionType.AdHoc; - DetectedCodesignAllocate = Path.Combine (DeveloperRoot, "Toolchains", "XcodeDefault.xctoolchain", "usr", "bin", "codesign_allocate"); + DetectedCodesignAllocate = Path.Combine (CurrentSdk.DeveloperRoot, "Toolchains", "XcodeDefault.xctoolchain", "usr", "bin", "codesign_allocate"); DetectedDistributionType = type.ToString (); identity.BundleId = BundleIdentifier; diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/ReadAppManifest.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/ReadAppManifest.cs index ed31af9848f6..6e70959e2d15 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/ReadAppManifest.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/ReadAppManifest.cs @@ -73,7 +73,7 @@ public override bool Execute () if (Platform == ApplePlatform.MacCatalyst) { // The minimum version in the Info.plist is the macOS version. However, the rest of our tooling // expects the iOS version, so expose that. - if (!MacCatalystSupport.TryGetiOSVersion (Sdks.GetAppleSdk (Platform).GetSdkPath (), MinimumOSVersion!, out var convertedVersion, out var knownMacOSVersions)) + if (!MacCatalystSupport.TryGetiOSVersion (CurrentSdk.GetSdkPath (), MinimumOSVersion!, out var convertedVersion, out var knownMacOSVersions)) Log.LogError (MSBStrings.E0187, MinimumOSVersion, string.Join (", ", knownMacOSVersions.OrderBy (v => v))); MinimumOSVersion = convertedVersion; } diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/XamarinTask.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/XamarinTask.cs index 1313a4d5e902..2ad8f996c312 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/XamarinTask.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/XamarinTask.cs @@ -35,6 +35,52 @@ public string GetSdkDevPath () return SdkDevPath; } + XcodeLocator? xcodeLocator = null; + public XcodeLocator GetXcodeLocator (bool initialDiscovery = false, Action? preprocess = null) + { + if (xcodeLocator is null) { + if (!initialDiscovery && string.IsNullOrEmpty (SdkDevPath)) { + Log.LogError (MSBStrings.E7169, /* The task '{0}' requires the property '{1}' to be set. Please file an issue at https://github.com/dotnet/macios/issues/new/choose. */ GetType ().Name, "SdkDevPath"); + } + + var xcodeLocator = new XcodeLocator (this); + preprocess?.Invoke (xcodeLocator); + if (!xcodeLocator.TryLocatingXcode (SdkDevPath)) + Log.LogError (MSBStrings.E0086 /* Could not find a valid Xcode developer path */); + this.xcodeLocator = xcodeLocator; + } + return xcodeLocator; + } + + protected void SetXcodeLocator (XcodeLocator xcodeLocator) + { + this.xcodeLocator = xcodeLocator; + } + + IAppleSdk? currentSdk; + public IAppleSdk CurrentSdk { + get { + if (currentSdk is null) { + var xcodeLocator = GetXcodeLocator (); + switch (Platform) { + case ApplePlatform.iOS: + currentSdk = new AppleIPhoneSdk (xcodeLocator.DeveloperRoot, xcodeLocator.DeveloperRootVersionPlist); + break; + case ApplePlatform.TVOS: + currentSdk = new AppleTVOSSdk (xcodeLocator.DeveloperRoot, xcodeLocator.DeveloperRootVersionPlist); + break; + case ApplePlatform.MacCatalyst: + case ApplePlatform.MacOSX: + currentSdk = new MacOSXSdk (xcodeLocator.DeveloperRoot, xcodeLocator.DeveloperRootVersionPlist); + break; + default: + throw new InvalidOperationException (string.Format (MSBStrings.InvalidPlatform, Platform)); + } + } + return currentSdk; + } + } + void VerifyTargetFrameworkMoniker () { if (!string.IsNullOrEmpty (TargetFrameworkMoniker)) diff --git a/msbuild/Xamarin.Shared/Xamarin.Shared.targets b/msbuild/Xamarin.Shared/Xamarin.Shared.targets index 1c5787205d7e..d9965c1b6db8 100644 --- a/msbuild/Xamarin.Shared/Xamarin.Shared.targets +++ b/msbuild/Xamarin.Shared/Xamarin.Shared.targets @@ -684,6 +684,7 @@ Copyright (C) 2018 Microsoft. All rights reserved. ProjectDir="$(MSBuildProjectDirectory)" ResourcePrefix="$(_ResourcePrefix)" ResourceRules="$(_PreparedResourceRules)" + SdkDevPath="$(_SdkDevPath)" SdkIsSimulator="$(_SdkIsSimulator)" SdkVersion="$(_SdkVersion)" SupportedOSPlatformVersion="$(SupportedOSPlatformVersion)" @@ -710,6 +711,7 @@ Copyright (C) 2018 Microsoft. All rights reserved. Condition="'$(IsMacEnabled)' == 'true' And '$(_BundleOriginalResources)' != 'true'" SessionId="$(BuildSessionId)" AppManifest="$(_TemporaryAppManifest)" + SdkDevPath="$(_SdkDevPath)" TargetFrameworkMoniker="$(_ComputedTargetFrameworkMoniker)" > @@ -797,6 +799,7 @@ Copyright (C) 2018 Microsoft. All rights reserved. Entitlements="$(CodesignEntitlements)" CompiledEntitlements="$(_CompiledEntitlementsPath)" ProvisioningProfile="$(_ProvisioningProfile)" + SdkDevPath="$(_SdkDevPath)" SdkIsSimulator="$(_SdkIsSimulator)" SdkPlatform="$(_SdkPlatform)" SdkVersion="$(_SdkVersion)" @@ -2069,6 +2072,7 @@ Copyright (C) 2018 Microsoft. All rights reserved. SdkIsSimulator="$(_SdkIsSimulator)" SdkPlatform="$(_SdkPlatform)" ProvisioningProfile="$(CodesignProvision)" + SdkDevPath="$(_SdkDevPath)" SigningKey="$(_SpecifiedCodesignKey)" DetectedCodeSigningKey="$(_CodeSigningKey)" TargetFrameworkMoniker="$(_ComputedTargetFrameworkMoniker)" @@ -2091,6 +2095,7 @@ Copyright (C) 2018 Microsoft. All rights reserved. GetBuildLogWarnings (string path) .Where (v => v.Type == BuildLogEventType.Warning) // We're often referencing earlier .NET projects (Touch.Unit/MonoTouch.Dialog), so ignore any out-of-support warnings. .Where (v => v.Message?.Contains ("is out of support and will not receive security updates in the future") != true) + // Ignore any messages about the settings files, we get *a lot* of those + .Where (v => !(v.Message?.Contains ("The settings file '/Users/") == true && v.Message?.Contains ("/Library/Preferences/maui/Settings.plist' is deprecated, and will be ignored in .NET 11+. ") == true)) + .Where (v => !(v.Message?.Contains ("The settings file '/Users/") == true && v.Message?.Contains ("/Library/Preferences/Xamarin/Settings.plist' is deprecated, and will be ignored in .NET 11+. ") == true)) ; } diff --git a/tests/common/Configuration.cs b/tests/common/Configuration.cs index 3e048d52eab1..8154d4e9b100 100644 --- a/tests/common/Configuration.cs +++ b/tests/common/Configuration.cs @@ -694,7 +694,7 @@ public static void SetBuildVariables (ApplePlatform platform, [NotNullIfNotNull if (environment is null) environment = new Dictionary (); - environment ["MD_APPLE_SDK_ROOT"] = Path.GetDirectoryName (Path.GetDirectoryName (xcode_root)!)!; + environment ["DEVELOPER_DIR"] = Path.GetDirectoryName (Path.GetDirectoryName (xcode_root)!)!; // This is set by `dotnet test` and can cause building legacy projects to fail to build with: // Microsoft.NET.Build.Extensions.ConflictResolution.targets(30,5): diff --git a/tests/dotnet/UnitTests/XcodeVersionTest.cs b/tests/dotnet/UnitTests/XcodeVersionTest.cs index a365af92f75e..301eb5e57fe0 100644 --- a/tests/dotnet/UnitTests/XcodeVersionTest.cs +++ b/tests/dotnet/UnitTests/XcodeVersionTest.cs @@ -35,16 +35,11 @@ public void Test (ApplePlatform platform, string xcodePath, Version xcodeVersion Clean (project_path); var properties = GetDefaultProperties (runtimeIdentifiers); properties ["BundleOriginalResources"] = "true"; // this prevents a different and unrelated error message from failing to build library projects + properties ["XcodeLocation"] = xcodePath; - var existingDeveloperDir = Environment.GetEnvironmentVariable ("MD_APPLE_SDK_ROOT"); - try { - Environment.SetEnvironmentVariable ("MD_APPLE_SDK_ROOT", Path.GetDirectoryName (Path.GetDirectoryName (xcodePath))); - var rv = DotNet.AssertBuildFailure (project_path, properties); - var errors = BinLog.GetBuildLogErrors (rv.BinLogPath).ToArray (); - AssertErrorMessages (errors, $"This version of .NET for {platform.AsString ()} ({Configuration.GetNuGetVersionNoMetadata (platform)}) requires Xcode {Configuration.XcodeVersion}. The current version of Xcode is {xcodeVersion}. Either install Xcode {Configuration.XcodeVersion}, or use a different version of .NET for {platform.AsString ()}. See https://aka.ms/xcode-requirement for more information."); - } finally { - Environment.SetEnvironmentVariable ("MD_APPLE_SDK_ROOT", existingDeveloperDir); - } + var rv = DotNet.AssertBuildFailure (project_path, properties); + var errors = BinLog.GetBuildLogErrors (rv.BinLogPath).ToArray (); + AssertErrorMessages (errors, $"This version of .NET for {platform.AsString ()} ({Configuration.GetNuGetVersionNoMetadata (platform)}) requires Xcode {Configuration.XcodeVersion}. The current version of Xcode is {xcodeVersion}. Either install Xcode {Configuration.XcodeVersion}, or use a different version of .NET for {platform.AsString ()}. See https://aka.ms/xcode-requirement for more information."); } } } diff --git a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/AssemblySetup.cs b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/AssemblySetup.cs index db3cb3520b67..5059aab86236 100644 --- a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/AssemblySetup.cs +++ b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/AssemblySetup.cs @@ -17,7 +17,7 @@ public void AssemblyInitialization () const string msbuild_exe_path = "/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/msbuild/15.0/bin/MSBuild.dll"; if (is_in_vsmac) { var env = new Dictionary { - { "MD_APPLE_SDK_ROOT", Path.GetDirectoryName (Path.GetDirectoryName (Configuration.xcode_root)) ?? string.Empty }, + { "DEVELOPER_DIR", Path.GetDirectoryName (Path.GetDirectoryName (Configuration.xcode_root)) ?? string.Empty }, { "MSBUILD_EXE_PATH", msbuild_exe_path }, }; diff --git a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs index f0f790709c06..e41ff90d614e 100644 --- a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs +++ b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs @@ -22,9 +22,9 @@ ACTool CreateACToolTask (ApplePlatform platform, string projectDir, out string i intermediateOutputPath = Cache.CreateTemporaryDirectory (); - var sdk = Sdks.GetAppleSdk (platform); + var task = CreateTask (); + var version = AppleSdkVersion.UseDefault.ToString (); - var root = sdk.GetSdkPath (version, false); string sdkPlatform; var uiDeviceFamily = ""; @@ -47,7 +47,6 @@ ACTool CreateACToolTask (ApplePlatform platform, string projectDir, out string i throw new NotImplementedException (platform.ToString ()); } - var task = CreateTask (); task.ImageAssets = imageAssets .Select (v => { var spl = v.Split ('|'); diff --git a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/CompileAppManifestTaskTests.cs b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/CompileAppManifestTaskTests.cs index 963fc4e4ad83..e07a1d80c94d 100644 --- a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/CompileAppManifestTaskTests.cs +++ b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/CompileAppManifestTaskTests.cs @@ -20,11 +20,11 @@ CompileAppManifest CreateTask (string? tmpdir = null, ApplePlatform platform = A var task = CreateTask (); task.AppBundleName = "AppBundleName"; task.CompiledAppManifest = new TaskItem (Path.Combine (tmpdir, "TemporaryAppManifest.plist")); - task.DefaultSdkVersion = Sdks.GetAppleSdk (platform).GetInstalledSdkVersions (false).First ().ToString ()!; task.MinSupportedOSPlatformVersion = "10.0"; task.SupportedOSPlatformVersion = "15.0"; task.SdkVersion = task.DefaultSdkVersion ?? string.Empty; task.TargetFrameworkMoniker = TargetFramework.GetTargetFramework (platform).ToString (); + task.DefaultSdkVersion = task.CurrentSdk.GetInstalledSdkVersions (false).First ().ToString ()!; return task; } diff --git a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GeneratePlistTaskTests/GeneratePlistTaskTests_Core.cs b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GeneratePlistTaskTests/GeneratePlistTaskTests_Core.cs index c98886bb4f40..f6b0181548aa 100644 --- a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GeneratePlistTaskTests/GeneratePlistTaskTests_Core.cs +++ b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GeneratePlistTaskTests/GeneratePlistTaskTests_Core.cs @@ -46,6 +46,7 @@ protected virtual void ConfigureTask () Task.MinSupportedOSPlatformVersion = "10.0"; Task.SupportedOSPlatformVersion = "15.0"; Task.SdkVersion = "10.0"; + Task.TargetFrameworkMoniker = TargetFramework.GetTargetFramework (Platform).ToString (); Plist = new PDictionary (); Plist ["CFBundleDisplayName"] = displayName; diff --git a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GeneratePlistTaskTests/GeneratePlistTaskTests_iOS.cs b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GeneratePlistTaskTests/GeneratePlistTaskTests_iOS.cs index 144e00166d4f..eec87ffd32f5 100644 --- a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GeneratePlistTaskTests/GeneratePlistTaskTests_iOS.cs +++ b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GeneratePlistTaskTests/GeneratePlistTaskTests_iOS.cs @@ -15,7 +15,7 @@ protected override void ConfigureTask () Configuration.IgnoreIfIgnoredPlatform (ApplePlatform.iOS); base.ConfigureTask (); - Task.DefaultSdkVersion = Sdks.IOS.GetClosestInstalledSdk (AppleSdkVersion.V6_1, true).ToString (); + Task.DefaultSdkVersion = Task.CurrentSdk.GetClosestInstalledSdk (AppleSdkVersion.V6_1, true).ToString () ?? ""; Task.TargetFrameworkMoniker = TargetFramework.DotNet_iOS_String; Task.TargetArchitectures = "ARM64"; } diff --git a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GeneratePlistTaskTests/GeneratePlistTaskTests_tvOS.cs b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GeneratePlistTaskTests/GeneratePlistTaskTests_tvOS.cs index b77904722891..06ab59fadfbe 100644 --- a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GeneratePlistTaskTests/GeneratePlistTaskTests_tvOS.cs +++ b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GeneratePlistTaskTests/GeneratePlistTaskTests_tvOS.cs @@ -14,7 +14,7 @@ protected override void ConfigureTask () Configuration.IgnoreIfIgnoredPlatform (ApplePlatform.TVOS); base.ConfigureTask (); - Task.DefaultSdkVersion = Sdks.TVOS.GetClosestInstalledSdk (AppleSdkVersion.V9_0, true).ToString (); + Task.DefaultSdkVersion = Task.CurrentSdk.GetClosestInstalledSdk (AppleSdkVersion.V9_0, true).ToString () ?? ""; Task.TargetFrameworkMoniker = TargetFramework.DotNet_tvOS_String; } } diff --git a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/IBToolTaskTests.cs b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/IBToolTaskTests.cs index 54819e3ef1cf..2b66ae4a5f93 100644 --- a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/IBToolTaskTests.cs +++ b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/IBToolTaskTests.cs @@ -18,10 +18,10 @@ namespace Xamarin.MacDev.Tasks { public class IBToolTaskTests : TestBase { IBTool CreateIBToolTask (ApplePlatform framework, string projectDir, string intermediateOutputPath) { + var task = CreateTask (); + var interfaceDefinitions = new List (); - var sdk = Sdks.GetSdk (framework); - var version = AppleSdkVersion.GetDefault (sdk, false); - var root = sdk.GetSdkPath (version, false); + var version = AppleSdkVersion.UseDefault.ToString (); string platform; switch (framework) { @@ -39,7 +39,6 @@ IBTool CreateIBToolTask (ApplePlatform framework, string projectDir, string inte foreach (var item in Directory.EnumerateFiles (projectDir, "*.xib", SearchOption.AllDirectories)) interfaceDefinitions.Add (new TaskItem (item)); - var task = CreateTask (); task.InterfaceDefinitions = interfaceDefinitions.ToArray (); task.IntermediateOutputPath = intermediateOutputPath; task.MinimumOSVersion = PDictionary.OpenFile (Path.Combine (projectDir, "Info.plist")).GetMinimumOSVersion (); @@ -48,8 +47,8 @@ IBTool CreateIBToolTask (ApplePlatform framework, string projectDir, string inte task.SdkDevPath = Configuration.xcode_root; task.SdkPlatform = platform; task.SdkVersion = version.ToString (); - task.SdkRoot = root; task.TargetFrameworkMoniker = TargetFramework.DotNet_iOS_String; + task.SdkRoot = task.CurrentSdk.GetSdkPath (version, false); return task; } @@ -62,7 +61,7 @@ public void TestBasicIBToolFunctionality () var ibtool = CreateIBToolTask (ApplePlatform.iOS, srcdir, tmp); var bundleResources = new HashSet (); - Assert.That (ibtool.Execute (), Is.True, "Execution of IBTool task failed."); + ExecuteTask (ibtool); foreach (var bundleResource in ibtool.BundleResources) { Assert.That (File.Exists (bundleResource.ItemSpec), Is.True, $"File does not exist: {bundleResource.ItemSpec}"); @@ -104,7 +103,7 @@ public void TestAdvancedIBToolFunctionality () ibtool.EnableOnDemandResources = true; - Assert.That (ibtool.Execute (), Is.True, "Execution of IBTool task failed."); + ExecuteTask (ibtool); foreach (var bundleResource in ibtool.BundleResources) { var bundleName = bundleResource.GetMetadata ("LogicalName"); @@ -179,7 +178,7 @@ void TestGenericAndDeviceSpecificXibsGeneric (params string [] fileNames) ibtool.EnableOnDemandResources = true; - Assert.That (ibtool.Execute (), Is.True, "Execution of IBTool task failed."); + ExecuteTask (ibtool); foreach (var bundleResource in ibtool.BundleResources) { var bundleName = bundleResource.GetMetadata ("LogicalName"); diff --git a/tests/package-mac-tests.sh b/tests/package-mac-tests.sh index ce02bb0d67eb..0b8dc92d6dab 100755 --- a/tests/package-mac-tests.sh +++ b/tests/package-mac-tests.sh @@ -19,7 +19,7 @@ cat test.config INCLUDE_MAC=$(grep ^INCLUDE_MAC= test.config | sed 's/.*=//') INCLUDE_MACCATALYST=$(grep ^INCLUDE_MACCATALYST= test.config | sed 's/.*=//') XCODE_DEVELOPER_ROOT=$(grep ^XCODE_DEVELOPER_ROOT= test.config | sed 's/.*=//') -export MD_APPLE_SDK_ROOT="$(dirname "$(dirname "$XCODE_DEVELOPER_ROOT")")" +export DEVELOPER_DIR="$(dirname "$(dirname "$XCODE_DEVELOPER_ROOT")")" export RootTestsDirectory="$(pwd)" make diff --git a/tests/xharness/Jenkins/TestTasks/AppleTestTask.cs b/tests/xharness/Jenkins/TestTasks/AppleTestTask.cs index 896e9a506d18..851fb2a9d42e 100644 --- a/tests/xharness/Jenkins/TestTasks/AppleTestTask.cs +++ b/tests/xharness/Jenkins/TestTasks/AppleTestTask.cs @@ -35,7 +35,7 @@ public override void SetEnvironmentVariables (Process process) var xcodeRoot = Jenkins.Harness.XcodeRoot; process.StartInfo.EnvironmentVariables ["RootTestsDirectory"] = HarnessConfiguration.RootDirectory; - process.StartInfo.EnvironmentVariables ["MD_APPLE_SDK_ROOT"] = xcodeRoot; + process.StartInfo.EnvironmentVariables ["DEVELOPER_DIR"] = xcodeRoot; foreach (var kvp in Environment) { if (kvp.Value is null) { From 58f44c5ff83b82040ee332ec25e11922ac17691b Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 26 May 2026 05:06:36 +0200 Subject: [PATCH 11/79] Fix missing 'don't' in docs/os-onboarding.md that reverses sentence meaning (#25508) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Line 23 of `docs/os-onboarding.md` was missing the word "don't", which reversed the intended meaning of the sentence. **Before:** "even if we bind any of the new APIs" **After:** "even if we **don't** bind any of the new APIs" The sentence explains that releasing support for a new OS version is technically optional because bindings aren't strictly required — the missing negation made it say the opposite. ## Changes - `docs/os-onboarding.md`: Inserted missing "don't" on line 23 Single-word typo fix, no functional code changes. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/os-onboarding.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/os-onboarding.md b/docs/os-onboarding.md index db4f7364a34f..6bb151226afc 100644 --- a/docs/os-onboarding.md +++ b/docs/os-onboarding.md @@ -20,7 +20,7 @@ This involves: ### Bind new APIs -This is technically optional, because we can release support for a new OS version even if we bind any of the new APIs. +This is technically optional, because we can release support for a new OS version even if we don't bind any of the new APIs. Yet we try to bind most of new APIs, because it's hard to predict what users will need. From ff427e0963aa32f708129b995642d145c9ad1236 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 26 May 2026 05:07:14 +0200 Subject: [PATCH 12/79] [github] Update to gh aw v0.74.8 (#25514) --- .github/agents/agentic-workflows.agent.md | 66 +++- .github/aw/actions-lock.json | 6 +- .github/dependabot.yml | 16 +- .github/workflows/ci-postmortem.lock.yml | 338 +++++++++++++++------ .github/workflows/code-radiator.lock.yml | 196 +++++++----- .github/workflows/copilot-setup-steps.yml | 4 +- .github/workflows/macios-reviewer.lock.yml | 198 +++++++----- 7 files changed, 547 insertions(+), 277 deletions(-) diff --git a/.github/agents/agentic-workflows.agent.md b/.github/agents/agentic-workflows.agent.md index cd376c5b1b30..f7e5eb4f1cd1 100644 --- a/.github/agents/agentic-workflows.agent.md +++ b/.github/agents/agentic-workflows.agent.md @@ -19,7 +19,13 @@ This is a **dispatcher agent** that routes your request to the appropriate speci - **Creating shared components**: Routes to `create-shared-agentic-workflow` prompt - **Fixing Dependabot PRs**: Routes to `dependabot` prompt — use this when Dependabot opens PRs that modify generated manifest files (`.github/workflows/package.json`, `.github/workflows/requirements.txt`, `.github/workflows/go.mod`). Never merge those PRs directly; instead update the source `.md` files and rerun `gh aw compile --dependabot` to bundle all fixes - **Analyzing test coverage**: Routes to `test-coverage` prompt — consult this whenever the workflow reads, analyzes, or reports on test coverage data from PRs or CI runs +- **Rendering ASCII charts in markdown**: Routes to `asciicharts` guide — consult this whenever the workflow needs compact charts that render reliably in GitHub issues, comments, or discussions - **CLI commands and triggering workflows**: Routes to `cli-commands` guide — consult this whenever the user asks how to run, compile, debug, or manage workflows from the command line, or when they need the MCP tool equivalent of a `gh aw` command +- **Reducing token consumption / cost optimization**: Routes to `token-optimization` guide — consult this whenever the user asks how to reduce token usage, lower costs, speed up workflows, or measure the impact of prompt changes with experiments +- **Choosing workflow architectures and design patterns**: Routes to `patterns` guide — consult this whenever the user asks for strategy, architecture, operating models, or pattern selection for agentic workflows + +> [!IMPORTANT] +> For architecture/pattern-selection requests, load `https://github.com/github/gh-aw/blob/v0.74.8/.github/aw/patterns.md` first. Workflows may optionally include: @@ -31,7 +37,7 @@ Workflows may optionally include: - Workflow files: `.github/workflows/*.md` and `.github/workflows/**/*.md` - Workflow lock files: `.github/workflows/*.lock.yml` - Shared components: `.github/workflows/shared/*.md` -- Configuration: https://github.com/github/gh-aw/blob/v0.72.1/.github/aw/github-agentic-workflows.md +- Configuration: https://github.com/github/gh-aw/blob/v0.74.8/.github/aw/github-agentic-workflows.md ## Problems This Solves @@ -53,7 +59,7 @@ When you interact with this agent, it will: ### Create New Workflow **Load when**: User wants to create a new workflow from scratch, add automation, or design a workflow that doesn't exist yet -**Prompt file**: https://github.com/github/gh-aw/blob/v0.72.1/.github/aw/create-agentic-workflow.md +**Prompt file**: https://github.com/github/gh-aw/blob/v0.74.8/.github/aw/create-agentic-workflow.md **Use cases**: - "Create a workflow that triages issues" @@ -63,7 +69,7 @@ When you interact with this agent, it will: ### Update Existing Workflow **Load when**: User wants to modify, improve, or refactor an existing workflow -**Prompt file**: https://github.com/github/gh-aw/blob/v0.72.1/.github/aw/update-agentic-workflow.md +**Prompt file**: https://github.com/github/gh-aw/blob/v0.74.8/.github/aw/update-agentic-workflow.md **Use cases**: - "Add web-fetch tool to the issue-classifier workflow" @@ -73,7 +79,7 @@ When you interact with this agent, it will: ### Debug Workflow **Load when**: User needs to investigate, audit, debug, or understand a workflow, troubleshoot issues, analyze logs, or fix errors -**Prompt file**: https://github.com/github/gh-aw/blob/v0.72.1/.github/aw/debug-agentic-workflow.md +**Prompt file**: https://github.com/github/gh-aw/blob/v0.74.8/.github/aw/debug-agentic-workflow.md **Use cases**: - "Why is this workflow failing?" @@ -83,7 +89,7 @@ When you interact with this agent, it will: ### Upgrade Agentic Workflows **Load when**: User wants to upgrade workflows to a new gh-aw version or fix deprecations -**Prompt file**: https://github.com/github/gh-aw/blob/v0.72.1/.github/aw/upgrade-agentic-workflows.md +**Prompt file**: https://github.com/github/gh-aw/blob/v0.74.8/.github/aw/upgrade-agentic-workflows.md **Use cases**: - "Upgrade all workflows to the latest version" @@ -93,7 +99,7 @@ When you interact with this agent, it will: ### Create a Report-Generating Workflow **Load when**: The workflow being created or updated produces reports — recurring status updates, audit summaries, analyses, or any structured output posted as a GitHub issue, discussion, or comment -**Prompt file**: https://github.com/github/gh-aw/blob/v0.72.1/.github/aw/report.md +**Prompt file**: https://github.com/github/gh-aw/blob/v0.74.8/.github/aw/report.md **Use cases**: - "Create a weekly CI health report" @@ -103,7 +109,7 @@ When you interact with this agent, it will: ### Create Shared Agentic Workflow **Load when**: User wants to create a reusable workflow component or wrap an MCP server -**Prompt file**: https://github.com/github/gh-aw/blob/v0.72.1/.github/aw/create-shared-agentic-workflow.md +**Prompt file**: https://github.com/github/gh-aw/blob/v0.74.8/.github/aw/create-shared-agentic-workflow.md **Use cases**: - "Create a shared component for Notion integration" @@ -113,7 +119,7 @@ When you interact with this agent, it will: ### Fix Dependabot PRs **Load when**: User needs to close or fix open Dependabot PRs that update dependencies in generated manifest files (`.github/workflows/package.json`, `.github/workflows/requirements.txt`, `.github/workflows/go.mod`) -**Prompt file**: https://github.com/github/gh-aw/blob/v0.72.1/.github/aw/dependabot.md +**Prompt file**: https://github.com/github/gh-aw/blob/v0.74.8/.github/aw/dependabot.md **Use cases**: - "Fix the open Dependabot PRs for npm dependencies" @@ -123,17 +129,27 @@ When you interact with this agent, it will: ### Analyze Test Coverage **Load when**: The workflow reads, analyzes, or reports test coverage — whether triggered by a PR, a schedule, or a slash command. Always consult this prompt before designing the coverage data strategy. -**Prompt file**: https://github.com/github/gh-aw/blob/v0.72.1/.github/aw/test-coverage.md +**Prompt file**: https://github.com/github/gh-aw/blob/v0.74.8/.github/aw/test-coverage.md **Use cases**: - "Create a workflow that comments coverage on PRs" - "Analyze coverage trends over time" - "Add a coverage gate that blocks PRs below a threshold" +### Render ASCII Charts in Markdown +**Load when**: The workflow needs in-markdown charts (sparklines, bars, table+trend views) that must align cleanly and render reliably across GitHub surfaces, including mobile. + +**Reference file**: https://github.com/github/gh-aw/blob/v0.74.8/.github/aw/asciicharts.md + +**Use cases**: +- "Show a compact trend chart in an issue comment" +- "Render a dashboard table with sparkline trends" +- "Generate aligned ASCII bars for service metrics" + ### CLI Commands Reference **Load when**: The user asks how to run, compile, debug, or manage workflows from the command line; needs the MCP tool equivalent of a `gh aw` command; or is in a restricted environment (e.g., Copilot Cloud) without direct CLI access. -**Reference file**: https://github.com/github/gh-aw/blob/v0.72.1/.github/aw/cli-commands.md +**Reference file**: https://github.com/github/gh-aw/blob/v0.74.8/.github/aw/cli-commands.md **Use cases**: - "How do I trigger workflow X on the main branch?" @@ -141,6 +157,30 @@ When you interact with this agent, it will: - "I'm in Copilot Cloud — how do I compile a workflow?" - "Show me all available gh aw commands" +### Token Consumption Optimization +**Load when**: The user asks how to reduce token usage, lower workflow costs, make a workflow faster or cheaper, or measure the impact of prompt or configuration changes. + +**Reference file**: https://github.com/github/gh-aw/blob/v0.74.8/.github/aw/token-optimization.md + +**Use cases**: +- "How do I reduce the token cost of this workflow?" +- "My workflow is too expensive — how do I optimize it?" +- "How do I compare token usage between two runs?" +- "Should I use gh-proxy or the MCP server?" +- "How do I use sub-agents to reduce costs?" +- "How do I measure the impact of a prompt change?" + +### Workflow Pattern Selection +**Load when**: The user asks for architecture, strategy, operating model selection, or pattern recommendations for building agentic workflows. + +**Reference file**: https://github.com/github/gh-aw/blob/v0.74.8/.github/aw/patterns.md + +**Use cases**: +- "Which pattern should I use for multi-repo rollout?" +- "How should I structure this workflow architecture?" +- "What pattern fits slash-command triage?" +- "Should this be DispatchOps or DailyOps?" + ## Instructions When a user interacts with you: @@ -185,12 +225,12 @@ gh aw compile --validate ## Important Notes -- Always reference the instructions file at https://github.com/github/gh-aw/blob/v0.72.1/.github/aw/github-agentic-workflows.md for complete documentation +- Always reference the instructions file at https://github.com/github/gh-aw/blob/v0.74.8/.github/aw/github-agentic-workflows.md for complete documentation - Use the MCP tool `agentic-workflows` when running in GitHub Copilot Cloud - Workflows must be compiled to `.lock.yml` files before running in GitHub Actions - **Bash tools are enabled by default** - Don't restrict bash commands unnecessarily since workflows are sandboxed by the AWF - Follow security best practices: minimal permissions, explicit network access, no template injection -- **Network configuration**: Use ecosystem identifiers (`node`, `python`, `go`, etc.) or explicit FQDNs in `network.allowed`. Bare shorthands like `npm` or `pypi` are **not** valid. See https://github.com/github/gh-aw/blob/v0.72.1/.github/aw/network.md for the full list of valid ecosystem identifiers and domain patterns. +- **Network configuration**: Use ecosystem identifiers (`node`, `python`, `go`, etc.) or explicit FQDNs in `network.allowed`. Bare shorthands like `npm` or `pypi` are **not** valid. See https://github.com/github/gh-aw/blob/v0.74.8/.github/aw/network.md for the full list of valid ecosystem identifiers and domain patterns. - **Single-file output**: When creating a workflow, produce exactly **one** workflow `.md` file. Do not create separate documentation files (architecture docs, runbooks, usage guides, etc.). If documentation is needed, add a brief `## Usage` section inside the workflow file itself. - **Triggering runs**: Always use `gh aw run ` to trigger a workflow on demand — not `gh workflow run .lock.yml`. `gh aw run` handles workflow resolution by short name, input parsing and validation, and correct run-tracking for agentic workflows. Use `--ref ` to run on a specific branch. -- **CLI commands reference**: For a complete guide on all `gh aw` commands and their MCP tool equivalents (for restricted environments), see https://github.com/github/gh-aw/blob/v0.72.1/.github/aw/cli-commands.md +- **CLI commands reference**: For a complete guide on all `gh aw` commands and their MCP tool equivalents (for restricted environments), see https://github.com/github/gh-aw/blob/v0.74.8/.github/aw/cli-commands.md diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json index 4b30f2d3389b..06276c7f1e4c 100644 --- a/.github/aw/actions-lock.json +++ b/.github/aw/actions-lock.json @@ -25,10 +25,10 @@ "version": "v7.0.1", "sha": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" }, - "github/gh-aw-actions/setup@v0.72.1": { + "github/gh-aw-actions/setup@v0.74.8": { "repo": "github/gh-aw-actions/setup", - "version": "v0.72.1", - "sha": "bc56a0cad2f450c562810785ef38649c04db812a" + "version": "v0.74.8", + "sha": "efa55847f72aadb03490d955263ff911bf758700" } }, "containers": { diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6cc00712d1c7..13434fb1a2d8 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,8 +1,10 @@ -version: 2 updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - cooldown: - default-days: 7 +- cooldown: + default-days: 7 + directory: / + ignore: + - dependency-name: "github/gh-aw-actions/**" # Managed by gh aw compile. Version-locked to the gh-aw compiler; do not bump. + package-ecosystem: github-actions + schedule: + interval: weekly +version: 2 diff --git a/.github/workflows/ci-postmortem.lock.yml b/.github/workflows/ci-postmortem.lock.yml index 9d6b4a9a0a6e..c8d473b80999 100644 --- a/.github/workflows/ci-postmortem.lock.yml +++ b/.github/workflows/ci-postmortem.lock.yml @@ -1,5 +1,5 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"15354af11629eb0049ecb70f03b13ed2df90af330f1d6a40d7ce9a202538bb0b","compiler_version":"v0.71.1","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.5"} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"239aec45b78c8799417efdd5bc6d8cc036629ec1","version":"v0.71.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.28","digest":"sha256:a8834e285807654bf680154faa710d43fe4365a0868142f5c20e48c85e137a7a","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.25.28@sha256:a8834e285807654bf680154faa710d43fe4365a0868142f5c20e48c85e137a7a"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.28","digest":"sha256:93290f2393752252911bd7c39a047f776c0b53063575e7bde4e304962a9a61cb","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.28@sha256:93290f2393752252911bd7c39a047f776c0b53063575e7bde4e304962a9a61cb"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.28","digest":"sha256:844c18280f82cd1b06345eb2f4e91966b34185bfc51c9f237c3e022e848fb474","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.25.28@sha256:844c18280f82cd1b06345eb2f4e91966b34185bfc51c9f237c3e022e848fb474"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.0","digest":"sha256:9c2228324fb1f26f39dc9471612e530ae3efc3156dac05efb2e8d212878d454d","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.0@sha256:9c2228324fb1f26f39dc9471612e530ae3efc3156dac05efb2e8d212878d454d"},{"image":"ghcr.io/github/github-mcp-server:v1.0.2","digest":"sha256:26db03408086a99cf1916348dcc4f9614206658f9082a8060dc7c81ad787f4ba","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.2@sha256:26db03408086a99cf1916348dcc4f9614206658f9082a8060dc7c81ad787f4ba"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"15354af11629eb0049ecb70f03b13ed2df90af330f1d6a40d7ce9a202538bb0b","compiler_version":"v0.74.8","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.5"} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"efa55847f72aadb03490d955263ff911bf758700","version":"v0.74.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.49"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.9","digest":"sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) # | |_| | __ _ ___ _ __ | |_ _ ___ @@ -14,7 +14,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.71.1). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.74.8). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -32,29 +32,29 @@ # Custom actions used: # - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 -# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 +# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 # - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 -# - github/gh-aw-actions/setup@239aec45b78c8799417efdd5bc6d8cc036629ec1 # v0.71.1 +# - github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 # # Container images used: -# - ghcr.io/github/gh-aw-firewall/agent:0.25.28@sha256:a8834e285807654bf680154faa710d43fe4365a0868142f5c20e48c85e137a7a -# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.28@sha256:93290f2393752252911bd7c39a047f776c0b53063575e7bde4e304962a9a61cb -# - ghcr.io/github/gh-aw-firewall/squid:0.25.28@sha256:844c18280f82cd1b06345eb2f4e91966b34185bfc51c9f237c3e022e848fb474 -# - ghcr.io/github/gh-aw-mcpg:v0.3.0@sha256:9c2228324fb1f26f39dc9471612e530ae3efc3156dac05efb2e8d212878d454d -# - ghcr.io/github/github-mcp-server:v1.0.2@sha256:26db03408086a99cf1916348dcc4f9614206658f9082a8060dc7c81ad787f4ba +# - ghcr.io/github/gh-aw-firewall/agent:0.25.49 +# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49 +# - ghcr.io/github/gh-aw-firewall/squid:0.25.49 +# - ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388 +# - ghcr.io/github/github-mcp-server:v1.0.4 # - node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f name: "CI Post-Mortem Analysis" -"on": +on: schedule: - - cron: "45 11 * * 0" + - cron: "11 2 * * 0" # Friendly format: weekly on sunday (scattered) workflow_dispatch: inputs: aw_context: default: "" - description: Agent caller context (used internally by Agentic Workflows). + description: "Agent caller context (used internally by Agentic Workflows)." required: false type: string @@ -78,35 +78,42 @@ jobs: lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }} model: ${{ steps.generate_aw_info.outputs.model }} secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} + setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} + setup-span-id: ${{ steps.setup.outputs.span-id }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@239aec45b78c8799417efdd5bc6d8cc036629ec1 # v0.71.1 + uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "CI Post-Mortem Analysis" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/ci-postmortem.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Generate agentic run info id: generate_aw_info env: GH_AW_INFO_ENGINE_ID: "copilot" GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" GH_AW_INFO_MODEL: "claude-sonnet-4.5" - GH_AW_INFO_VERSION: "1.0.35" - GH_AW_INFO_AGENT_VERSION: "1.0.35" - GH_AW_INFO_CLI_VERSION: "v0.71.1" + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_AGENT_VERSION: "1.0.48" + GH_AW_INFO_CLI_VERSION: "v0.74.8" GH_AW_INFO_WORKFLOW_NAME: "CI Post-Mortem Analysis" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" GH_AW_INFO_STAGED: "false" GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","dotnet","github","aka.ms","dev.azure.com","devdiv.visualstudio.com","microsoft.com","vsassets.io"]' GH_AW_INFO_FIREWALL_ENABLED: "true" - GH_AW_INFO_AWF_VERSION: "v0.25.28" + GH_AW_INFO_AWF_VERSION: "v0.25.49" GH_AW_INFO_AWMG_VERSION: "" GH_AW_INFO_FIREWALL_TYPE: "squid" GH_AW_COMPILED_STRICT: "true" - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); @@ -130,17 +137,18 @@ jobs: .crush .gemini .opencode + .pi sparse-checkout-cone-mode: true fetch-depth: 1 - name: Save agent config folders for base branch restoration env: - GH_AW_AGENT_FOLDERS: ".agents .claude .codex .crush .gemini .github .opencode" - GH_AW_AGENT_FILES: ".crush.json AGENTS.md CLAUDE.md GEMINI.md opencode.jsonc" + GH_AW_AGENT_FOLDERS: ".agents .claude .codex .crush .gemini .github .opencode .pi" + GH_AW_AGENT_FILES: ".crush.json AGENTS.md CLAUDE.md GEMINI.md PI.md opencode.jsonc" # poutine:ignore untrusted_checkout_exec run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh" - name: Check workflow lock file id: check-lock-file - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_WORKFLOW_FILE: "ci-postmortem.lock.yml" GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}" @@ -151,9 +159,9 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs'); await main(); - name: Check compile-agentic version - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: - GH_AW_COMPILED_VERSION: "v0.71.1" + GH_AW_COMPILED_VERSION: "v0.74.8" with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); @@ -164,11 +172,11 @@ jobs: env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl + GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} @@ -187,30 +195,33 @@ jobs: Tools: add_comment(max:20), create_issue(max:20), update_issue(max:20), missing_tool, missing_data, noop + GH_AW_PROMPT_956c986f1e45d6a0_EOF + cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" + cat << 'GH_AW_PROMPT_956c986f1e45d6a0_EOF' The following GitHub context information is available for this workflow: - {{#if __GH_AW_GITHUB_ACTOR__ }} + {{#if github.actor}} - **actor**: __GH_AW_GITHUB_ACTOR__ {{/if}} - {{#if __GH_AW_GITHUB_REPOSITORY__ }} + {{#if github.repository}} - **repository**: __GH_AW_GITHUB_REPOSITORY__ {{/if}} - {{#if __GH_AW_GITHUB_WORKSPACE__ }} + {{#if github.workspace}} - **workspace**: __GH_AW_GITHUB_WORKSPACE__ {{/if}} - {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} - - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ + {{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}} + - **issue-number**: #__GH_AW_EXPR_802A9F6A__ {{/if}} - {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} - - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ + {{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}} + - **discussion-number**: #__GH_AW_EXPR_1A3A194A__ {{/if}} - {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} - - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ + {{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}} + - **pull-request-number**: #__GH_AW_EXPR_463A214A__ {{/if}} - {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} - - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ + {{#if github.event.comment.id || github.aw.context.comment_id}} + - **comment-id**: __GH_AW_EXPR_FF1D34CE__ {{/if}} - {{#if __GH_AW_GITHUB_RUN_ID__ }} + {{#if github.run_id}} - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ {{/if}} @@ -223,9 +234,10 @@ jobs: GH_AW_PROMPT_956c986f1e45d6a0_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_ENGINE_ID: "copilot" with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); @@ -233,17 +245,18 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs'); await main(); - name: Substitute placeholders - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools' with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); @@ -255,14 +268,15 @@ jobs: return await substitutePlaceholders({ file: process.env.GH_AW_PROMPT, substitutions: { + GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A, + GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A, + GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A, + GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE, GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, - GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, - GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE + GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, + GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST } }); - name: Validate prompt placeholders @@ -280,11 +294,15 @@ jobs: uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: activation + include-hidden-files: true path: | /tmp/gh-aw/aw_info.json /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/aw-prompts/prompt-template.txt + /tmp/gh-aw/aw-prompts/prompt-import-tree.json /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/base + /tmp/gh-aw/.github/agents if-no-files-found: ignore retention-days: 1 @@ -307,6 +325,7 @@ jobs: agentic_engine_timeout: ${{ steps.detect-copilot-errors.outputs.agentic_engine_timeout || 'false' }} checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }} + effective_tokens_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.effective_tokens_rate_limit_error || 'false' }} has_patch: ${{ steps.collect_output.outputs.has_patch }} inference_access_error: ${{ steps.detect-copilot-errors.outputs.inference_access_error || 'false' }} mcp_policy_error: ${{ steps.detect-copilot-errors.outputs.mcp_policy_error || 'false' }} @@ -314,15 +333,23 @@ jobs: model_not_supported_error: ${{ steps.detect-copilot-errors.outputs.model_not_supported_error || 'false' }} output: ${{ steps.collect_output.outputs.output }} output_types: ${{ steps.collect_output.outputs.output_types }} + setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} + setup-span-id: ${{ steps.setup.outputs.span-id }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@239aec45b78c8799417efdd5bc6d8cc036629ec1 # v0.71.1 + uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "CI Post-Mortem Analysis" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/ci-postmortem.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Set runtime paths id: set-runtime-paths run: | @@ -358,7 +385,7 @@ jobs: id: checkout-pr if: | github.event.pull_request || github.event.issue.pull_request - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} with: @@ -369,11 +396,11 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); await main(); - name: Install GitHub Copilot CLI - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.35 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.48 env: GH_HOST: github.com - name: Install AWF binary - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.28 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.49 - name: Parse integrity filter lists id: parse-guard-vars env: @@ -389,12 +416,17 @@ jobs: - name: Restore agent config folders from base branch if: steps.checkout-pr.outcome == 'success' env: - GH_AW_AGENT_FOLDERS: ".agents .claude .codex .crush .gemini .github .opencode" - GH_AW_AGENT_FILES: ".crush.json AGENTS.md CLAUDE.md GEMINI.md opencode.jsonc" + GH_AW_AGENT_FOLDERS: ".agents .claude .codex .crush .gemini .github .opencode .pi" + GH_AW_AGENT_FILES: ".crush.json AGENTS.md CLAUDE.md GEMINI.md PI.md opencode.jsonc" run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh" + - name: Restore inline sub-agents from activation artifact + env: + GH_AW_SUB_AGENT_DIR: ".github/agents" + GH_AW_SUB_AGENT_EXT: ".agent.md" + run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh" - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.28@sha256:a8834e285807654bf680154faa710d43fe4365a0868142f5c20e48c85e137a7a ghcr.io/github/gh-aw-firewall/api-proxy:0.25.28@sha256:93290f2393752252911bd7c39a047f776c0b53063575e7bde4e304962a9a61cb ghcr.io/github/gh-aw-firewall/squid:0.25.28@sha256:844c18280f82cd1b06345eb2f4e91966b34185bfc51c9f237c3e022e848fb474 ghcr.io/github/gh-aw-mcpg:v0.3.0@sha256:9c2228324fb1f26f39dc9471612e530ae3efc3156dac05efb2e8d212878d454d ghcr.io/github/github-mcp-server:v1.0.2@sha256:26db03408086a99cf1916348dcc4f9614206658f9082a8060dc7c81ad787f4ba node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f - - name: Write Safe Outputs Config + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.49 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49 ghcr.io/github/gh-aw-firewall/squid:0.25.49 ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388 ghcr.io/github/github-mcp-server:v1.0.4 node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f + - name: Generate Safe Outputs Config run: | mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs @@ -402,7 +434,7 @@ jobs: cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_c2d474e65378a915_EOF' {"add_comment":{"max":20},"create_issue":{"max":20},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{},"update_issue":{"allow_body":true,"max":20}} GH_AW_SAFE_OUTPUTS_CONFIG_c2d474e65378a915_EOF - - name: Write Safe Outputs Tools + - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | { @@ -447,6 +479,9 @@ jobs: "sanitize": true, "maxLength": 65000 }, + "fields": { + "type": "array" + }, "labels": { "type": "array", "itemType": "string", @@ -599,7 +634,7 @@ jobs: "customValidation": "requiresOneOf:status,title,body" } } - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); @@ -660,6 +695,7 @@ jobs: # Export gateway environment variables for MCP config and gateway script export MCP_GATEWAY_PORT="8080" export MCP_GATEWAY_DOMAIN="host.docker.internal" + export MCP_GATEWAY_HOST_DOMAIN="localhost" MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') echo "::add-mask::${MCP_GATEWAY_API_KEY}" export MCP_GATEWAY_API_KEY @@ -671,8 +707,13 @@ jobs: export GH_AW_ENGINE="copilot" MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0') MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0') - DOCKER_SOCK_GID=$(stat -c '%g' /var/run/docker.sock 2>/dev/null || echo '0') - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.0' + case "${DOCKER_HOST:-}" in + unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;; + /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;; + * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;; + esac + DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0') + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.9' mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) @@ -681,7 +722,7 @@ jobs: "mcpServers": { "github": { "type": "stdio", - "container": "ghcr.io/github/github-mcp-server:v1.0.2", + "container": "ghcr.io/github/github-mcp-server:v1.0.4", "env": { "GITHUB_HOST": "\${GITHUB_SERVER_URL}", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", @@ -721,32 +762,59 @@ jobs: } } GH_AW_MCP_CONFIG_7bd5cd6513b45b21_EOF - - name: Clean git credentials + - name: Mount MCP servers as CLIs + id: mount-mcp-clis + continue-on-error: true + env: + MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} + MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }} + MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs'); + await main(); + - name: Clean credentials continue-on-error: true run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh" + - name: Audit pre-agent workspace + id: pre_agent_audit + continue-on-error: true + run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh" - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): timeout-minutes: 20 run: | set -o pipefail + printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt touch /tmp/gh-aw/agent-step-summary.md GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) export GH_AW_NODE_BIN + export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" (umask 177 && touch /tmp/gh-aw/agent-stdio.log) + printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.49/awf-config.schema.json","network":{"allowDomains":["*.githubusercontent.com","*.vsblob.vsassets.io","aka.ms","api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.nuget.org","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","azuresearch-usnc.nuget.org","azuresearch-ussc.nuget.org","builds.dotnet.microsoft.com","ci.dot.net","codeload.github.com","crl.geotrust.com","crl.globalsign.com","crl.identrust.com","crl.sectigo.com","crl.thawte.com","crl.usertrust.com","crl.verisign.com","crl3.digicert.com","crl4.digicert.com","crls.ssl.com","dc.services.visualstudio.com","dev.azure.com","devdiv.visualstudio.com","dist.nuget.org","docs.github.com","dot.net","dotnet.microsoft.com","dotnetcli.blob.core.windows.net","github-cloud.githubusercontent.com","github-cloud.s3.amazonaws.com","github.blog","github.com","github.githubassets.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.com","lfs.github.com","microsoft.com","nuget.org","nuget.pkg.github.com","nugetregistryv2prod.blob.core.windows.net","objects.githubusercontent.com","ocsp.digicert.com","ocsp.geotrust.com","ocsp.globalsign.com","ocsp.identrust.com","ocsp.sectigo.com","ocsp.ssl.com","ocsp.thawte.com","ocsp.usertrust.com","ocsp.verisign.com","oneocsp.microsoft.com","packagecloud.io","packages.cloud.google.com","packages.microsoft.com","patch-diff.githubusercontent.com","pkgs.dev.azure.com","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","vsassets.io","www.googleapis.com","www.microsoft.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000,"models":{"agent":["sonnet-6x","gpt-5.4","gpt-5","gemini-pro","haiku","any"],"any":["copilot/*","anthropic/*","openai/*","google/*","gemini/*"],"auto":["large"],"claude":["agent","sonnet-6x","haiku","any"],"codex":["agent","gpt-5-codex","gpt-5","any"],"coding":["copilot/gpt-5*codex*","openai/gpt-5*codex*","gpt-5-codex"],"copilot":["agent","gpt-5.4","sonnet","gpt-5","any"],"deep-research":["copilot/deep-research*","copilot/o3-deep-research*","copilot/o4-mini-deep-research*","google/deep-research*","gemini/deep-research*","openai/o3-deep-research*","openai/o4-mini-deep-research*"],"gemini":["agent","gemini-pro","gemini-flash","any"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*","gemini/gemini-*flash*"],"gemini-flash-lite":["copilot/gemini-*flash*lite*","google/gemini-*flash*lite*","gemini/gemini-*flash*lite*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*","gemini/gemini-*pro*"],"gemma":["copilot/gemma*","google/gemma*","gemini/gemma*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash-lite","copilot/raptor*mini*"],"opus":["copilot/*opus*","anthropic/*opus*"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"],"sonnet-6x":["copilot/*sonnet-4.5*","copilot/*sonnet-4-5*","anthropic/*sonnet-4.5*","anthropic/*sonnet-4-5*","copilot/*sonnet-3.7*","copilot/*sonnet-3-7*","anthropic/*sonnet-3.7*","anthropic/*sonnet-3-7*","copilot/*sonnet-3.5*","copilot/*sonnet-3-5*","anthropic/*sonnet-3.5*","anthropic/*sonnet-3-5*"],"vision":["copilot/gemini-*image*","gemini/gemini-*image*","copilot/gemini-*flash*","gemini/gemini-*flash*"]}},"container":{"imageTag":"0.25.49"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" + cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="" + if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw" + fi # shellcheck disable=SC1003 - sudo -E awf --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --allow-domains '*.githubusercontent.com,*.vsblob.vsassets.io,aka.ms,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,ci.dot.net,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,dev.azure.com,devdiv.visualstudio.com,dist.nuget.org,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,microsoft.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkgs.dev.azure.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,vsassets.io,www.googleapis.com,www.microsoft.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --image-tag 0.25.28,squid=sha256:844c18280f82cd1b06345eb2f4e91966b34185bfc51c9f237c3e022e848fb474,agent=sha256:a8834e285807654bf680154faa710d43fe4365a0868142f5c20e48c85e137a7a,api-proxy=sha256:93290f2393752252911bd7c39a047f776c0b53063575e7bde4e304962a9a61cb,cli-proxy=sha256:fdf310e4678ce58d248c466b89399e9680a3003038fd19322c388559016aaac7 --skip-pull --enable-api-proxy \ - -- /bin/bash -c 'GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_driver.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ + -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: + AWF_REFLECT_ENABLED: 1 COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_API_KEY: dummy-byok-key-for-offline-mode + COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} COPILOT_MODEL: claude-sonnet-4.5 GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.71.1 + GH_AW_VERSION: v0.74.8 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows @@ -794,7 +862,7 @@ jobs: bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID" - name: Redact secrets in logs if: always() - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); @@ -820,10 +888,10 @@ jobs: - name: Ingest agent output id: collect_output if: always() - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.vsblob.vsassets.io,aka.ms,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,ci.dot.net,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,dev.azure.com,devdiv.visualstudio.com,dist.nuget.org,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,microsoft.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkgs.dev.azure.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,vsassets.io,www.googleapis.com,www.microsoft.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.vsblob.vsassets.io,aka.ms,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,ci.dot.net,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,dev.azure.com,devdiv.visualstudio.com,dist.nuget.org,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,microsoft.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkgs.dev.azure.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,vsassets.io,www.googleapis.com,www.microsoft.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} with: @@ -834,7 +902,7 @@ jobs: await main(); - name: Parse agent logs for step summary if: always() - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ with: @@ -846,7 +914,7 @@ jobs: - name: Parse MCP Gateway logs for step summary if: always() id: parse-mcp-gateway - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); @@ -861,7 +929,7 @@ jobs: run: | # Fix permissions on firewall logs/audit dirs so they can be uploaded as artifacts # AWF runs with sudo, creating files owned by root - sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall 2>/dev/null || true + sudo chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) if command -v awf &> /dev/null; then awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" @@ -871,13 +939,23 @@ jobs: - name: Parse token usage for step summary if: always() continue-on-error: true - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io, getOctokit); const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_token_usage.cjs'); await main(); + - name: Print AWF reflect summary + if: always() + continue-on-error: true + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/awf_reflect_summary.cjs'); + await main(); - name: Write agent output placeholder if missing if: always() run: | @@ -899,14 +977,17 @@ jobs: !/tmp/gh-aw/proxy-logs/proxy-tls/ /tmp/gh-aw/agent_usage.json /tmp/gh-aw/agent-stdio.log + /tmp/gh-aw/pre-agent-audit.txt /tmp/gh-aw/agent/ /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/safeoutputs.jsonl /tmp/gh-aw/agent_output.json /tmp/gh-aw/aw-*.patch /tmp/gh-aw/aw-*.bundle + /tmp/gh-aw/awf-config.json /tmp/gh-aw/sandbox/firewall/logs/ /tmp/gh-aw/sandbox/firewall/audit/ + /tmp/gh-aw/sandbox/firewall/awf-reflect.json if-no-files-found: ignore conclusion: @@ -927,6 +1008,7 @@ jobs: concurrency: group: "gh-aw-conclusion-ci-postmortem" cancel-in-progress: false + queue: max outputs: incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }} noop_message: ${{ steps.noop.outputs.noop_message }} @@ -935,11 +1017,17 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@239aec45b78c8799417efdd5bc6d8cc036629ec1 # v0.71.1 + uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "CI Post-Mortem Analysis" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/ci-postmortem.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Download agent output artifact id: download-agent-output continue-on-error: true @@ -956,7 +1044,7 @@ jobs: echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" - name: Process no-op messages id: noop - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} GH_AW_NOOP_MAX: "1" @@ -973,7 +1061,7 @@ jobs: await main(); - name: Log detection run id: detection_runs - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} GH_AW_WORKFLOW_NAME: "CI Post-Mortem Analysis" @@ -989,7 +1077,7 @@ jobs: await main(); - name: Record missing tool id: missing_tool - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} GH_AW_MISSING_TOOL_CREATE_ISSUE: "true" @@ -1003,7 +1091,7 @@ jobs: await main(); - name: Record incomplete id: report_incomplete - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true" @@ -1018,7 +1106,7 @@ jobs: - name: Handle agent failure id: handle_agent_failure if: always() - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} GH_AW_WORKFLOW_NAME: "CI Post-Mortem Analysis" @@ -1029,15 +1117,21 @@ jobs: GH_AW_ENGINE_ID: "copilot" GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} + GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }} + GH_AW_EFFECTIVE_TOKENS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.effective_tokens_rate_limit_error || 'false' }} GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }} GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }} GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }} + GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com" GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }} GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }} GH_AW_GROUP_REPORTS: "false" GH_AW_FAILURE_REPORT_AS_ISSUE: "true" + GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true" + GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true" GH_AW_TIMEOUT_MINUTES: "20" + GH_AW_MAX_EFFECTIVE_TOKENS: "25000000" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | @@ -1062,11 +1156,17 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@239aec45b78c8799417efdd5bc6d8cc036629ec1 # v0.71.1 + uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "CI Post-Mortem Analysis" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/ci-postmortem.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Download agent output artifact id: download-agent-output continue-on-error: true @@ -1092,7 +1192,7 @@ jobs: rm -rf /tmp/gh-aw/sandbox/firewall/logs rm -rf /tmp/gh-aw/sandbox/firewall/audit - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.28@sha256:a8834e285807654bf680154faa710d43fe4365a0868142f5c20e48c85e137a7a ghcr.io/github/gh-aw-firewall/api-proxy:0.25.28@sha256:93290f2393752252911bd7c39a047f776c0b53063575e7bde4e304962a9a61cb ghcr.io/github/gh-aw-firewall/squid:0.25.28@sha256:844c18280f82cd1b06345eb2f4e91966b34185bfc51c9f237c3e022e848fb474 + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.49 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49 ghcr.io/github/gh-aw-firewall/squid:0.25.49 - name: Check if detection needed id: detection_guard if: always() @@ -1107,7 +1207,7 @@ jobs: echo "run_detection=false" >> "$GITHUB_OUTPUT" echo "Detection skipped: no agent outputs or patches to analyze" fi - - name: Clear MCP configuration for detection + - name: Clear MCP Config for detection if: always() && steps.detection_guard.outputs.run_detection == 'true' run: | rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json" @@ -1129,7 +1229,7 @@ jobs: ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true - name: Setup threat detection if: always() && steps.detection_guard.outputs.run_detection == 'true' - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: WORKFLOW_NAME: "CI Post-Mortem Analysis" WORKFLOW_DESCRIPTION: "No description provided" @@ -1151,33 +1251,43 @@ jobs: node-version: '24' package-manager-cache: false - name: Install GitHub Copilot CLI - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.35 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.48 env: GH_HOST: github.com - name: Install AWF binary - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.28 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.49 - name: Execute GitHub Copilot CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' + continue-on-error: true id: detection_agentic_execution # Copilot CLI tool arguments (sorted): timeout-minutes: 20 run: | set -o pipefail + printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt touch /tmp/gh-aw/agent-step-summary.md GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) export GH_AW_NODE_BIN + export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log) + printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.49/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","host.docker.internal","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000},"container":{"imageTag":"0.25.49"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" + cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="" + if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw" + fi # shellcheck disable=SC1003 - sudo -E awf --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,telemetry.enterprise.githubcopilot.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --image-tag 0.25.28,squid=sha256:844c18280f82cd1b06345eb2f4e91966b34185bfc51c9f237c3e022e848fb474,agent=sha256:a8834e285807654bf680154faa710d43fe4365a0868142f5c20e48c85e137a7a,api-proxy=sha256:93290f2393752252911bd7c39a047f776c0b53063575e7bde4e304962a9a61cb,cli-proxy=sha256:fdf310e4678ce58d248c466b89399e9680a3003038fd19322c388559016aaac7 --skip-pull --enable-api-proxy \ - -- /bin/bash -c 'GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_driver.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log + sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ + -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log env: + AWF_REFLECT_ENABLED: 1 COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_API_KEY: dummy-byok-key-for-offline-mode + COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} COPILOT_MODEL: claude-sonnet-4.5 GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.71.1 + GH_AW_VERSION: v0.74.8 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows @@ -1201,16 +1311,35 @@ jobs: - name: Parse and conclude threat detection id: detection_conclusion if: always() - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + continue-on-error: true + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} + DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }} GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" with: script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_threat_detection_results.cjs'); - await main(); + try { + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_threat_detection_results.cjs'); + await main(); + } catch (loadErr) { + const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false'; + const detectionExecutionFailed = process.env.DETECTION_AGENTIC_EXECUTION_OUTCOME === 'failure'; + const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr)); + core.error(msg); + core.setOutput('reason', 'parse_error'); + if (continueOnError && !detectionExecutionFailed) { + core.warning('\u26A0\uFE0F ' + msg); + core.setOutput('conclusion', 'warning'); + core.setOutput('success', 'false'); + } else { + core.setOutput('conclusion', 'failure'); + core.setOutput('success', 'false'); + core.setFailed(msg); + } + } safe_outputs: needs: @@ -1232,7 +1361,7 @@ jobs: GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }} GH_AW_ENGINE_ID: "copilot" GH_AW_ENGINE_MODEL: "claude-sonnet-4.5" - GH_AW_ENGINE_VERSION: "1.0.35" + GH_AW_ENGINE_VERSION: "1.0.48" GH_AW_WORKFLOW_ID: "ci-postmortem" GH_AW_WORKFLOW_NAME: "CI Post-Mortem Analysis" outputs: @@ -1249,11 +1378,17 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@239aec45b78c8799417efdd5bc6d8cc036629ec1 # v0.71.1 + uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "CI Post-Mortem Analysis" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/ci-postmortem.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Download agent output artifact id: download-agent-output continue-on-error: true @@ -1279,10 +1414,11 @@ jobs: echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" - name: Process Safe Outputs id: process_safe_outputs - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.vsblob.vsassets.io,aka.ms,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,ci.dot.net,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,dev.azure.com,devdiv.visualstudio.com,dist.nuget.org,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,microsoft.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkgs.dev.azure.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,vsassets.io,www.googleapis.com,www.microsoft.com" + GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }} + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.vsblob.vsassets.io,aka.ms,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,ci.dot.net,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,dev.azure.com,devdiv.visualstudio.com,dist.nuget.org,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,microsoft.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkgs.dev.azure.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,vsassets.io,www.googleapis.com,www.microsoft.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":20},\"create_issue\":{\"max\":20},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{},\"update_issue\":{\"allow_body\":true,\"max\":20}}" diff --git a/.github/workflows/code-radiator.lock.yml b/.github/workflows/code-radiator.lock.yml index 8c0f8d46a7c0..cd32f396e575 100644 --- a/.github/workflows/code-radiator.lock.yml +++ b/.github/workflows/code-radiator.lock.yml @@ -1,5 +1,5 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"a665debda1a902047622496aa625b015b0b9f053d3892454a63ac7b73cc61808","compiler_version":"v0.72.1","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.5"} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"bc56a0cad2f450c562810785ef38649c04db812a","version":"v0.72.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.41","digest":"sha256:cb2b565d070116d4b67e355775340528b5a2c3cb18b2c9049638bcc2df681770","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.25.41@sha256:cb2b565d070116d4b67e355775340528b5a2c3cb18b2c9049638bcc2df681770"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41","digest":"sha256:fadd0de387209f69a9a7a1b8722bb5e7fdfb80ba9749a5c60f0e4cd7582a74d0","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41@sha256:fadd0de387209f69a9a7a1b8722bb5e7fdfb80ba9749a5c60f0e4cd7582a74d0"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.41","digest":"sha256:1260445d25968dbf3ae70143964177a0e5914cf2ce07a6117f7d3caec6c3e3c4","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.25.41@sha256:1260445d25968dbf3ae70143964177a0e5914cf2ce07a6117f7d3caec6c3e3c4"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"a665debda1a902047622496aa625b015b0b9f053d3892454a63ac7b73cc61808","compiler_version":"v0.74.8","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.5"} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"efa55847f72aadb03490d955263ff911bf758700","version":"v0.74.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.49"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.9","digest":"sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) # | |_| | __ _ ___ _ __ | |_ _ ___ @@ -14,7 +14,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.72.1). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.74.8). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -36,18 +36,18 @@ # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 # - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 -# - github/gh-aw-actions/setup@bc56a0cad2f450c562810785ef38649c04db812a # v0.72.1 +# - github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 # # Container images used: -# - ghcr.io/github/gh-aw-firewall/agent:0.25.41@sha256:cb2b565d070116d4b67e355775340528b5a2c3cb18b2c9049638bcc2df681770 -# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41@sha256:fadd0de387209f69a9a7a1b8722bb5e7fdfb80ba9749a5c60f0e4cd7582a74d0 -# - ghcr.io/github/gh-aw-firewall/squid:0.25.41@sha256:1260445d25968dbf3ae70143964177a0e5914cf2ce07a6117f7d3caec6c3e3c4 -# - ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c -# - ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959 +# - ghcr.io/github/gh-aw-firewall/agent:0.25.49 +# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49 +# - ghcr.io/github/gh-aw-firewall/squid:0.25.49 +# - ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388 +# - ghcr.io/github/github-mcp-server:v1.0.4 # - node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f name: "Code Radiator" -"on": +on: # roles: # Roles processed as role check in pre-activation job # - admin # Roles processed as role check in pre-activation job # - maintain # Roles processed as role check in pre-activation job @@ -59,7 +59,7 @@ name: "Code Radiator" inputs: aw_context: default: "" - description: Agent caller context (used internally by Agentic Workflows). + description: "Agent caller context (used internally by Agentic Workflows)." required: false type: string @@ -84,35 +84,38 @@ jobs: lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }} model: ${{ steps.generate_aw_info.outputs.model }} secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} + setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} + setup-span-id: ${{ steps.setup.outputs.span-id }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@bc56a0cad2f450c562810785ef38649c04db812a # v0.72.1 + uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} env: GH_AW_SETUP_WORKFLOW_NAME: "Code Radiator" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/code-radiator.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Generate agentic run info id: generate_aw_info env: GH_AW_INFO_ENGINE_ID: "copilot" GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" GH_AW_INFO_MODEL: "claude-sonnet-4.5" - GH_AW_INFO_VERSION: "1.0.40" - GH_AW_INFO_AGENT_VERSION: "1.0.40" - GH_AW_INFO_CLI_VERSION: "v0.72.1" + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_AGENT_VERSION: "1.0.48" + GH_AW_INFO_CLI_VERSION: "v0.74.8" GH_AW_INFO_WORKFLOW_NAME: "Code Radiator" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" GH_AW_INFO_STAGED: "false" GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]' GH_AW_INFO_FIREWALL_ENABLED: "true" - GH_AW_INFO_AWF_VERSION: "v0.25.41" + GH_AW_INFO_AWF_VERSION: "v0.25.49" GH_AW_INFO_AWMG_VERSION: "" GH_AW_INFO_FIREWALL_TYPE: "squid" GH_AW_COMPILED_STRICT: "true" @@ -164,7 +167,7 @@ jobs: - name: Check compile-agentic version uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: - GH_AW_COMPILED_VERSION: "v0.72.1" + GH_AW_COMPILED_VERSION: "v0.74.8" with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); @@ -175,11 +178,11 @@ jobs: env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl + GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} @@ -207,28 +210,28 @@ jobs: cat << 'GH_AW_PROMPT_0170a78c8d56645a_EOF' The following GitHub context information is available for this workflow: - {{#if __GH_AW_GITHUB_ACTOR__ }} + {{#if github.actor}} - **actor**: __GH_AW_GITHUB_ACTOR__ {{/if}} - {{#if __GH_AW_GITHUB_REPOSITORY__ }} + {{#if github.repository}} - **repository**: __GH_AW_GITHUB_REPOSITORY__ {{/if}} - {{#if __GH_AW_GITHUB_WORKSPACE__ }} + {{#if github.workspace}} - **workspace**: __GH_AW_GITHUB_WORKSPACE__ {{/if}} - {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} - - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ + {{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}} + - **issue-number**: #__GH_AW_EXPR_802A9F6A__ {{/if}} - {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} - - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ + {{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}} + - **discussion-number**: #__GH_AW_EXPR_1A3A194A__ {{/if}} - {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} - - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ + {{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}} + - **pull-request-number**: #__GH_AW_EXPR_463A214A__ {{/if}} - {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} - - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ + {{#if github.event.comment.id || github.aw.context.comment_id}} + - **comment-id**: __GH_AW_EXPR_FF1D34CE__ {{/if}} - {{#if __GH_AW_GITHUB_RUN_ID__ }} + {{#if github.run_id}} - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ {{/if}} - **checkouts**: The following repositories have been checked out and are available in the workspace: @@ -258,11 +261,11 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} @@ -278,11 +281,11 @@ jobs: return await substitutePlaceholders({ file: process.env.GH_AW_PROMPT, substitutions: { + GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A, + GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A, + GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A, + GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE, GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, - GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, @@ -335,6 +338,7 @@ jobs: agentic_engine_timeout: ${{ steps.detect-copilot-errors.outputs.agentic_engine_timeout || 'false' }} checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }} + effective_tokens_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.effective_tokens_rate_limit_error || 'false' }} has_patch: ${{ steps.collect_output.outputs.has_patch }} inference_access_error: ${{ steps.detect-copilot-errors.outputs.inference_access_error || 'false' }} mcp_policy_error: ${{ steps.detect-copilot-errors.outputs.mcp_policy_error || 'false' }} @@ -342,19 +346,23 @@ jobs: model_not_supported_error: ${{ steps.detect-copilot-errors.outputs.model_not_supported_error || 'false' }} output: ${{ steps.collect_output.outputs.output }} output_types: ${{ steps.collect_output.outputs.output_types }} + setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} + setup-span-id: ${{ steps.setup.outputs.span-id }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@bc56a0cad2f450c562810785ef38649c04db812a # v0.72.1 + uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} env: GH_AW_SETUP_WORKFLOW_NAME: "Code Radiator" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/code-radiator.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Set runtime paths id: set-runtime-paths run: | @@ -408,11 +416,11 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); await main(); - name: Install GitHub Copilot CLI - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.40 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.48 env: GH_HOST: github.com - name: Install AWF binary - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.41 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.49 - name: Parse integrity filter lists id: parse-guard-vars env: @@ -437,7 +445,7 @@ jobs: GH_AW_SUB_AGENT_EXT: ".agent.md" run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh" - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.41@sha256:cb2b565d070116d4b67e355775340528b5a2c3cb18b2c9049638bcc2df681770 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41@sha256:fadd0de387209f69a9a7a1b8722bb5e7fdfb80ba9749a5c60f0e4cd7582a74d0 ghcr.io/github/gh-aw-firewall/squid:0.25.41@sha256:1260445d25968dbf3ae70143964177a0e5914cf2ce07a6117f7d3caec6c3e3c4 ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959 node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.49 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49 ghcr.io/github/gh-aw-firewall/squid:0.25.49 ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388 ghcr.io/github/github-mcp-server:v1.0.4 node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f - name: Generate Safe Outputs Config run: | mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" @@ -778,8 +786,13 @@ jobs: export GH_AW_ENGINE="copilot" MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0') MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0') - DOCKER_SOCK_GID=$(stat -c '%g' /var/run/docker.sock 2>/dev/null || echo '0') - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.6' + case "${DOCKER_HOST:-}" in + unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;; + /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;; + * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;; + esac + DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0') + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.9' mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) @@ -788,7 +801,7 @@ jobs: "mcpServers": { "github": { "type": "stdio", - "container": "ghcr.io/github/github-mcp-server:v1.0.3", + "container": "ghcr.io/github/github-mcp-server:v1.0.4", "env": { "GITHUB_HOST": "\${GITHUB_SERVER_URL}", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", @@ -855,25 +868,32 @@ jobs: timeout-minutes: 20 run: | set -o pipefail + printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt touch /tmp/gh-aw/agent-step-summary.md GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) export GH_AW_NODE_BIN + export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" (umask 177 && touch /tmp/gh-aw/agent-stdio.log) - printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.41/awf-config.schema.json","network":{"allowDomains":["*.githubusercontent.com","api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","codeload.github.com","crl.geotrust.com","crl.globalsign.com","crl.identrust.com","crl.sectigo.com","crl.thawte.com","crl.usertrust.com","crl.verisign.com","crl3.digicert.com","crl4.digicert.com","crls.ssl.com","docs.github.com","github-cloud.githubusercontent.com","github-cloud.s3.amazonaws.com","github.blog","github.com","github.githubassets.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.com","lfs.github.com","objects.githubusercontent.com","ocsp.digicert.com","ocsp.geotrust.com","ocsp.globalsign.com","ocsp.identrust.com","ocsp.sectigo.com","ocsp.ssl.com","ocsp.thawte.com","ocsp.usertrust.com","ocsp.verisign.com","packagecloud.io","packages.cloud.google.com","packages.microsoft.com","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true,"models":{"auto":["large"],"deep-research":["copilot/deep-research*","copilot/o3-deep-research*","copilot/o4-mini-deep-research*","google/deep-research*","openai/o3-deep-research*","openai/o4-mini-deep-research*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash"],"opus":["copilot/*opus*","anthropic/*opus*"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"]}},"container":{"imageTag":"0.25.41,squid=sha256:1260445d25968dbf3ae70143964177a0e5914cf2ce07a6117f7d3caec6c3e3c4,agent=sha256:cb2b565d070116d4b67e355775340528b5a2c3cb18b2c9049638bcc2df681770,api-proxy=sha256:fadd0de387209f69a9a7a1b8722bb5e7fdfb80ba9749a5c60f0e4cd7582a74d0"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json + printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.49/awf-config.schema.json","network":{"allowDomains":["*.githubusercontent.com","api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","codeload.github.com","crl.geotrust.com","crl.globalsign.com","crl.identrust.com","crl.sectigo.com","crl.thawte.com","crl.usertrust.com","crl.verisign.com","crl3.digicert.com","crl4.digicert.com","crls.ssl.com","docs.github.com","github-cloud.githubusercontent.com","github-cloud.s3.amazonaws.com","github.blog","github.com","github.githubassets.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.com","lfs.github.com","objects.githubusercontent.com","ocsp.digicert.com","ocsp.geotrust.com","ocsp.globalsign.com","ocsp.identrust.com","ocsp.sectigo.com","ocsp.ssl.com","ocsp.thawte.com","ocsp.usertrust.com","ocsp.verisign.com","packagecloud.io","packages.cloud.google.com","packages.microsoft.com","patch-diff.githubusercontent.com","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000,"models":{"agent":["sonnet-6x","gpt-5.4","gpt-5","gemini-pro","haiku","any"],"any":["copilot/*","anthropic/*","openai/*","google/*","gemini/*"],"auto":["large"],"claude":["agent","sonnet-6x","haiku","any"],"codex":["agent","gpt-5-codex","gpt-5","any"],"coding":["copilot/gpt-5*codex*","openai/gpt-5*codex*","gpt-5-codex"],"copilot":["agent","gpt-5.4","sonnet","gpt-5","any"],"deep-research":["copilot/deep-research*","copilot/o3-deep-research*","copilot/o4-mini-deep-research*","google/deep-research*","gemini/deep-research*","openai/o3-deep-research*","openai/o4-mini-deep-research*"],"gemini":["agent","gemini-pro","gemini-flash","any"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*","gemini/gemini-*flash*"],"gemini-flash-lite":["copilot/gemini-*flash*lite*","google/gemini-*flash*lite*","gemini/gemini-*flash*lite*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*","gemini/gemini-*pro*"],"gemma":["copilot/gemma*","google/gemma*","gemini/gemma*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash-lite","copilot/raptor*mini*"],"opus":["copilot/*opus*","anthropic/*opus*"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"],"sonnet-6x":["copilot/*sonnet-4.5*","copilot/*sonnet-4-5*","anthropic/*sonnet-4.5*","anthropic/*sonnet-4-5*","copilot/*sonnet-3.7*","copilot/*sonnet-3-7*","anthropic/*sonnet-3.7*","anthropic/*sonnet-3-7*","copilot/*sonnet-3.5*","copilot/*sonnet-3-5*","anthropic/*sonnet-3.5*","anthropic/*sonnet-3-5*"],"vision":["copilot/gemini-*image*","gemini/gemini-*image*","copilot/gemini-*flash*","gemini/gemini-*flash*"]}},"container":{"imageTag":"0.25.49"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" + cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="" + if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw" + fi # shellcheck disable=SC1003 - sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ - -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ + -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: AWF_REFLECT_ENABLED: 1 COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_API_KEY: dummy-byok-key-for-offline-mode + COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} COPILOT_MODEL: claude-sonnet-4.5 GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.72.1 + GH_AW_VERSION: v0.74.8 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows @@ -950,7 +970,7 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} with: @@ -1067,6 +1087,7 @@ jobs: concurrency: group: "gh-aw-conclusion-code-radiator" cancel-in-progress: false + queue: max outputs: incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }} noop_message: ${{ steps.noop.outputs.noop_message }} @@ -1075,15 +1096,17 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@bc56a0cad2f450c562810785ef38649c04db812a # v0.72.1 + uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} env: GH_AW_SETUP_WORKFLOW_NAME: "Code Radiator" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/code-radiator.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Download agent output artifact id: download-agent-output continue-on-error: true @@ -1173,6 +1196,8 @@ jobs: GH_AW_ENGINE_ID: "copilot" GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} + GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }} + GH_AW_EFFECTIVE_TOKENS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.effective_tokens_rate_limit_error || 'false' }} GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }} GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }} @@ -1187,6 +1212,7 @@ jobs: GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true" GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true" GH_AW_TIMEOUT_MINUTES: "20" + GH_AW_MAX_EFFECTIVE_TOKENS: "25000000" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | @@ -1211,15 +1237,17 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@bc56a0cad2f450c562810785ef38649c04db812a # v0.72.1 + uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} env: GH_AW_SETUP_WORKFLOW_NAME: "Code Radiator" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/code-radiator.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Download agent output artifact id: download-agent-output continue-on-error: true @@ -1245,7 +1273,7 @@ jobs: rm -rf /tmp/gh-aw/sandbox/firewall/logs rm -rf /tmp/gh-aw/sandbox/firewall/audit - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.41@sha256:cb2b565d070116d4b67e355775340528b5a2c3cb18b2c9049638bcc2df681770 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41@sha256:fadd0de387209f69a9a7a1b8722bb5e7fdfb80ba9749a5c60f0e4cd7582a74d0 ghcr.io/github/gh-aw-firewall/squid:0.25.41@sha256:1260445d25968dbf3ae70143964177a0e5914cf2ce07a6117f7d3caec6c3e3c4 + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.49 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49 ghcr.io/github/gh-aw-firewall/squid:0.25.49 - name: Check if detection needed id: detection_guard if: always() @@ -1304,11 +1332,11 @@ jobs: node-version: '24' package-manager-cache: false - name: Install GitHub Copilot CLI - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.40 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.48 env: GH_HOST: github.com - name: Install AWF binary - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.41 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.49 - name: Execute GitHub Copilot CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' continue-on-error: true @@ -1317,23 +1345,30 @@ jobs: timeout-minutes: 20 run: | set -o pipefail + printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt touch /tmp/gh-aw/agent-step-summary.md GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) export GH_AW_NODE_BIN + export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log) - printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.41/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","host.docker.internal","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true},"container":{"imageTag":"0.25.41"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json + printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.49/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","host.docker.internal","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000},"container":{"imageTag":"0.25.49"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" + cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="" + if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw" + fi # shellcheck disable=SC1003 - sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ - -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log + sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ + -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log env: AWF_REFLECT_ENABLED: 1 COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_API_KEY: dummy-byok-key-for-offline-mode + COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} COPILOT_MODEL: claude-sonnet-4.5 GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.72.1 + GH_AW_VERSION: v0.74.8 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows @@ -1361,6 +1396,7 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} + DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }} GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" with: script: | @@ -1371,10 +1407,11 @@ jobs: await main(); } catch (loadErr) { const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false'; + const detectionExecutionFailed = process.env.DETECTION_AGENTIC_EXECUTION_OUTCOME === 'failure'; const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr)); core.error(msg); core.setOutput('reason', 'parse_error'); - if (continueOnError) { + if (continueOnError && !detectionExecutionFailed) { core.warning('\u26A0\uFE0F ' + msg); core.setOutput('conclusion', 'warning'); core.setOutput('success', 'false'); @@ -1405,7 +1442,7 @@ jobs: GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }} GH_AW_ENGINE_ID: "copilot" GH_AW_ENGINE_MODEL: "claude-sonnet-4.5" - GH_AW_ENGINE_VERSION: "1.0.40" + GH_AW_ENGINE_VERSION: "1.0.48" GH_AW_WORKFLOW_ID: "code-radiator" GH_AW_WORKFLOW_NAME: "Code Radiator" outputs: @@ -1424,15 +1461,17 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@bc56a0cad2f450c562810785ef38649c04db812a # v0.72.1 + uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} env: GH_AW_SETUP_WORKFLOW_NAME: "Code Radiator" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/code-radiator.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Download agent output artifact id: download-agent-output continue-on-error: true @@ -1476,8 +1515,16 @@ jobs: echo "Extracted base branch from safe output: $BASE_BRANCH" fi fi + - name: Checkout repository (trusted default branch for comment events) + if: ((!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') || (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) && (github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment') + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ github.event.repository.default_branch }} + token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + persist-credentials: false + fetch-depth: 1 - name: Checkout repository - if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') || (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch') + if: ((!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') || (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) && github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ steps.extract-base-branch.outputs.base-branch || github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }} @@ -1512,7 +1559,8 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" + GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }} + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10,\"target\":\"*\"},\"add_labels\":{\"max\":10,\"target\":\"*\"},\"create_pull_request\":{\"allowed_base_branches\":[\"net[0-9]*.0\",\"xcode[0-9]*\",\"xcode[0-9]*.[0-9]*\"],\"max\":10,\"max_patch_files\":100,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"]},\"create_report_incomplete_issue\":{},\"merge_pull_request\":{\"max\":10},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max\":10,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"target\":\"*\",\"title_prefix\":\"🤖 Merge 'main' =\\u003e '\"},\"report_incomplete\":{},\"update_pull_request\":{\"allow_body\":true,\"allow_title\":true,\"max\":10,\"update_branch\":false}}" diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 0ae42f6f5c4b..f4841a250f76 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -21,6 +21,6 @@ jobs: - name: Checkout repository uses: actions/checkout@v6 - name: Install gh-aw extension - uses: github/gh-aw-actions/setup-cli@bc56a0cad2f450c562810785ef38649c04db812a # v0.72.1 + uses: github/gh-aw-actions/setup-cli@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 with: - version: v0.72.1 + version: v0.74.8 diff --git a/.github/workflows/macios-reviewer.lock.yml b/.github/workflows/macios-reviewer.lock.yml index 616d54825de5..28089a715a5d 100644 --- a/.github/workflows/macios-reviewer.lock.yml +++ b/.github/workflows/macios-reviewer.lock.yml @@ -1,5 +1,5 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"a0f64139dbc6fe8697f9fd99f5489155b561c37ec0b13d7eaf30bda56d034e7f","compiler_version":"v0.72.1","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.5"} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"bc56a0cad2f450c562810785ef38649c04db812a","version":"v0.72.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.41","digest":"sha256:cb2b565d070116d4b67e355775340528b5a2c3cb18b2c9049638bcc2df681770","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.25.41@sha256:cb2b565d070116d4b67e355775340528b5a2c3cb18b2c9049638bcc2df681770"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41","digest":"sha256:fadd0de387209f69a9a7a1b8722bb5e7fdfb80ba9749a5c60f0e4cd7582a74d0","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41@sha256:fadd0de387209f69a9a7a1b8722bb5e7fdfb80ba9749a5c60f0e4cd7582a74d0"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.41","digest":"sha256:1260445d25968dbf3ae70143964177a0e5914cf2ce07a6117f7d3caec6c3e3c4","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.25.41@sha256:1260445d25968dbf3ae70143964177a0e5914cf2ce07a6117f7d3caec6c3e3c4"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"a0f64139dbc6fe8697f9fd99f5489155b561c37ec0b13d7eaf30bda56d034e7f","compiler_version":"v0.74.8","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.5"} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"efa55847f72aadb03490d955263ff911bf758700","version":"v0.74.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.49"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.9","digest":"sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) # | |_| | __ _ ___ _ __ | |_ _ ___ @@ -14,7 +14,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.72.1). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.74.8). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -35,18 +35,18 @@ # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 # - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 -# - github/gh-aw-actions/setup@bc56a0cad2f450c562810785ef38649c04db812a # v0.72.1 +# - github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 # # Container images used: -# - ghcr.io/github/gh-aw-firewall/agent:0.25.41@sha256:cb2b565d070116d4b67e355775340528b5a2c3cb18b2c9049638bcc2df681770 -# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41@sha256:fadd0de387209f69a9a7a1b8722bb5e7fdfb80ba9749a5c60f0e4cd7582a74d0 -# - ghcr.io/github/gh-aw-firewall/squid:0.25.41@sha256:1260445d25968dbf3ae70143964177a0e5914cf2ce07a6117f7d3caec6c3e3c4 -# - ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c -# - ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959 +# - ghcr.io/github/gh-aw-firewall/agent:0.25.49 +# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49 +# - ghcr.io/github/gh-aw-firewall/squid:0.25.49 +# - ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388 +# - ghcr.io/github/github-mcp-server:v1.0.4 # - node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f name: ".NET for Apple Platforms PR Reviewer" -"on": +on: issue_comment: types: - created @@ -83,6 +83,8 @@ jobs: lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }} model: ${{ steps.generate_aw_info.outputs.model }} secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} + setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} + setup-span-id: ${{ steps.setup.outputs.span-id }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} slash_command: ${{ needs.pre_activation.outputs.matched_command }} stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} @@ -91,31 +93,33 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@bc56a0cad2f450c562810785ef38649c04db812a # v0.72.1 + uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.pre_activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.pre_activation.outputs.setup-parent-span-id || needs.pre_activation.outputs.setup-span-id }} env: GH_AW_SETUP_WORKFLOW_NAME: ".NET for Apple Platforms PR Reviewer" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/macios-reviewer.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Generate agentic run info id: generate_aw_info env: GH_AW_INFO_ENGINE_ID: "copilot" GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" GH_AW_INFO_MODEL: "claude-sonnet-4.5" - GH_AW_INFO_VERSION: "1.0.40" - GH_AW_INFO_AGENT_VERSION: "1.0.40" - GH_AW_INFO_CLI_VERSION: "v0.72.1" + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_AGENT_VERSION: "1.0.48" + GH_AW_INFO_CLI_VERSION: "v0.74.8" GH_AW_INFO_WORKFLOW_NAME: ".NET for Apple Platforms PR Reviewer" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" GH_AW_INFO_STAGED: "false" GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","dotnet","github","aka.ms","dev.azure.com","microsoft.com","vsassets.io"]' GH_AW_INFO_FIREWALL_ENABLED: "true" - GH_AW_INFO_AWF_VERSION: "v0.25.41" + GH_AW_INFO_AWF_VERSION: "v0.25.49" GH_AW_INFO_AWMG_VERSION: "" GH_AW_INFO_FIREWALL_TYPE: "squid" GH_AW_COMPILED_STRICT: "true" @@ -128,7 +132,7 @@ jobs: await main(core, context); - name: Add eyes reaction for immediate feedback id: react - if: github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment' || github.event_name == 'pull_request' && github.event.pull_request.head.repo.id == github.repository_id || github.event_name == 'pull_request_review' && github.event.pull_request.head.repo.id == github.repository_id + if: github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment' || github.event_name == 'pull_request' && github.event.pull_request.head.repo.id == github.repository_id uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_REACTION: "eyes" @@ -180,7 +184,7 @@ jobs: - name: Check compile-agentic version uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: - GH_AW_COMPILED_VERSION: "v0.72.1" + GH_AW_COMPILED_VERSION: "v0.74.8" with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); @@ -191,7 +195,7 @@ jobs: id: sanitized uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.vsblob.vsassets.io,aka.ms,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,ci.dot.net,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,dev.azure.com,dist.nuget.org,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,microsoft.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkgs.dev.azure.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,vsassets.io,www.googleapis.com,www.microsoft.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.vsblob.vsassets.io,aka.ms,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,ci.dot.net,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,dev.azure.com,dist.nuget.org,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,microsoft.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkgs.dev.azure.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,vsassets.io,www.googleapis.com,www.microsoft.com" with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); @@ -200,7 +204,7 @@ jobs: await main(); - name: Add comment with workflow run link id: add-comment - if: github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment' || github.event_name == 'pull_request' && github.event.pull_request.head.repo.id == github.repository_id || github.event_name == 'pull_request_review' && github.event.pull_request.head.repo.id == github.repository_id + if: github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment' || github.event_name == 'pull_request' && github.event.pull_request.head.repo.id == github.repository_id uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_WORKFLOW_NAME: ".NET for Apple Platforms PR Reviewer" @@ -214,11 +218,11 @@ jobs: env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl + GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} @@ -243,28 +247,28 @@ jobs: cat << 'GH_AW_PROMPT_33adc77fba6f068a_EOF' The following GitHub context information is available for this workflow: - {{#if __GH_AW_GITHUB_ACTOR__ }} + {{#if github.actor}} - **actor**: __GH_AW_GITHUB_ACTOR__ {{/if}} - {{#if __GH_AW_GITHUB_REPOSITORY__ }} + {{#if github.repository}} - **repository**: __GH_AW_GITHUB_REPOSITORY__ {{/if}} - {{#if __GH_AW_GITHUB_WORKSPACE__ }} + {{#if github.workspace}} - **workspace**: __GH_AW_GITHUB_WORKSPACE__ {{/if}} - {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} - - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ + {{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}} + - **issue-number**: #__GH_AW_EXPR_802A9F6A__ {{/if}} - {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} - - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ + {{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}} + - **discussion-number**: #__GH_AW_EXPR_1A3A194A__ {{/if}} - {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} - - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ + {{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}} + - **pull-request-number**: #__GH_AW_EXPR_463A214A__ {{/if}} - {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} - - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ + {{#if github.event.comment.id || github.aw.context.comment_id}} + - **comment-id**: __GH_AW_EXPR_FF1D34CE__ {{/if}} - {{#if __GH_AW_GITHUB_RUN_ID__ }} + {{#if github.run_id}} - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ {{/if}} @@ -294,11 +298,11 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} @@ -317,11 +321,11 @@ jobs: return await substitutePlaceholders({ file: process.env.GH_AW_PROMPT, substitutions: { + GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A, + GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A, + GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A, + GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE, GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, - GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, @@ -375,6 +379,7 @@ jobs: agentic_engine_timeout: ${{ steps.detect-copilot-errors.outputs.agentic_engine_timeout || 'false' }} checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }} + effective_tokens_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.effective_tokens_rate_limit_error || 'false' }} has_patch: ${{ steps.collect_output.outputs.has_patch }} inference_access_error: ${{ steps.detect-copilot-errors.outputs.inference_access_error || 'false' }} mcp_policy_error: ${{ steps.detect-copilot-errors.outputs.mcp_policy_error || 'false' }} @@ -382,19 +387,23 @@ jobs: model_not_supported_error: ${{ steps.detect-copilot-errors.outputs.model_not_supported_error || 'false' }} output: ${{ steps.collect_output.outputs.output }} output_types: ${{ steps.collect_output.outputs.output_types }} + setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} + setup-span-id: ${{ steps.setup.outputs.span-id }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@bc56a0cad2f450c562810785ef38649c04db812a # v0.72.1 + uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} env: GH_AW_SETUP_WORKFLOW_NAME: ".NET for Apple Platforms PR Reviewer" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/macios-reviewer.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Set runtime paths id: set-runtime-paths run: | @@ -441,11 +450,11 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); await main(); - name: Install GitHub Copilot CLI - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.40 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.48 env: GH_HOST: github.com - name: Install AWF binary - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.41 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.49 - name: Parse integrity filter lists id: parse-guard-vars env: @@ -470,7 +479,7 @@ jobs: GH_AW_SUB_AGENT_EXT: ".agent.md" run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh" - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.41@sha256:cb2b565d070116d4b67e355775340528b5a2c3cb18b2c9049638bcc2df681770 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41@sha256:fadd0de387209f69a9a7a1b8722bb5e7fdfb80ba9749a5c60f0e4cd7582a74d0 ghcr.io/github/gh-aw-firewall/squid:0.25.41@sha256:1260445d25968dbf3ae70143964177a0e5914cf2ce07a6117f7d3caec6c3e3c4 ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959 node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.49 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49 ghcr.io/github/gh-aw-firewall/squid:0.25.49 ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388 ghcr.io/github/github-mcp-server:v1.0.4 node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f - name: Generate Safe Outputs Config run: | mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" @@ -694,8 +703,13 @@ jobs: export GH_AW_ENGINE="copilot" MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0') MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0') - DOCKER_SOCK_GID=$(stat -c '%g' /var/run/docker.sock 2>/dev/null || echo '0') - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.6' + case "${DOCKER_HOST:-}" in + unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;; + /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;; + * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;; + esac + DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0') + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.9' mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) @@ -704,7 +718,7 @@ jobs: "mcpServers": { "github": { "type": "stdio", - "container": "ghcr.io/github/github-mcp-server:v1.0.3", + "container": "ghcr.io/github/github-mcp-server:v1.0.4", "env": { "GITHUB_HOST": "\${GITHUB_SERVER_URL}", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", @@ -771,25 +785,32 @@ jobs: timeout-minutes: 20 run: | set -o pipefail + printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt touch /tmp/gh-aw/agent-step-summary.md GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) export GH_AW_NODE_BIN + export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" (umask 177 && touch /tmp/gh-aw/agent-stdio.log) - printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.41/awf-config.schema.json","network":{"allowDomains":["*.githubusercontent.com","*.vsblob.vsassets.io","aka.ms","api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.nuget.org","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","azuresearch-usnc.nuget.org","azuresearch-ussc.nuget.org","builds.dotnet.microsoft.com","ci.dot.net","codeload.github.com","crl.geotrust.com","crl.globalsign.com","crl.identrust.com","crl.sectigo.com","crl.thawte.com","crl.usertrust.com","crl.verisign.com","crl3.digicert.com","crl4.digicert.com","crls.ssl.com","dc.services.visualstudio.com","dev.azure.com","dist.nuget.org","docs.github.com","dot.net","dotnet.microsoft.com","dotnetcli.blob.core.windows.net","github-cloud.githubusercontent.com","github-cloud.s3.amazonaws.com","github.blog","github.com","github.githubassets.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.com","lfs.github.com","microsoft.com","nuget.org","nuget.pkg.github.com","nugetregistryv2prod.blob.core.windows.net","objects.githubusercontent.com","ocsp.digicert.com","ocsp.geotrust.com","ocsp.globalsign.com","ocsp.identrust.com","ocsp.sectigo.com","ocsp.ssl.com","ocsp.thawte.com","ocsp.usertrust.com","ocsp.verisign.com","oneocsp.microsoft.com","packagecloud.io","packages.cloud.google.com","packages.microsoft.com","pkgs.dev.azure.com","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","vsassets.io","www.googleapis.com","www.microsoft.com"]},"apiProxy":{"enabled":true,"models":{"auto":["large"],"deep-research":["copilot/deep-research*","copilot/o3-deep-research*","copilot/o4-mini-deep-research*","google/deep-research*","openai/o3-deep-research*","openai/o4-mini-deep-research*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash"],"opus":["copilot/*opus*","anthropic/*opus*"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"]}},"container":{"imageTag":"0.25.41,squid=sha256:1260445d25968dbf3ae70143964177a0e5914cf2ce07a6117f7d3caec6c3e3c4,agent=sha256:cb2b565d070116d4b67e355775340528b5a2c3cb18b2c9049638bcc2df681770,api-proxy=sha256:fadd0de387209f69a9a7a1b8722bb5e7fdfb80ba9749a5c60f0e4cd7582a74d0"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json + printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.49/awf-config.schema.json","network":{"allowDomains":["*.githubusercontent.com","*.vsblob.vsassets.io","aka.ms","api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.nuget.org","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","azuresearch-usnc.nuget.org","azuresearch-ussc.nuget.org","builds.dotnet.microsoft.com","ci.dot.net","codeload.github.com","crl.geotrust.com","crl.globalsign.com","crl.identrust.com","crl.sectigo.com","crl.thawte.com","crl.usertrust.com","crl.verisign.com","crl3.digicert.com","crl4.digicert.com","crls.ssl.com","dc.services.visualstudio.com","dev.azure.com","dist.nuget.org","docs.github.com","dot.net","dotnet.microsoft.com","dotnetcli.blob.core.windows.net","github-cloud.githubusercontent.com","github-cloud.s3.amazonaws.com","github.blog","github.com","github.githubassets.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.com","lfs.github.com","microsoft.com","nuget.org","nuget.pkg.github.com","nugetregistryv2prod.blob.core.windows.net","objects.githubusercontent.com","ocsp.digicert.com","ocsp.geotrust.com","ocsp.globalsign.com","ocsp.identrust.com","ocsp.sectigo.com","ocsp.ssl.com","ocsp.thawte.com","ocsp.usertrust.com","ocsp.verisign.com","oneocsp.microsoft.com","packagecloud.io","packages.cloud.google.com","packages.microsoft.com","patch-diff.githubusercontent.com","pkgs.dev.azure.com","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","vsassets.io","www.googleapis.com","www.microsoft.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000,"models":{"agent":["sonnet-6x","gpt-5.4","gpt-5","gemini-pro","haiku","any"],"any":["copilot/*","anthropic/*","openai/*","google/*","gemini/*"],"auto":["large"],"claude":["agent","sonnet-6x","haiku","any"],"codex":["agent","gpt-5-codex","gpt-5","any"],"coding":["copilot/gpt-5*codex*","openai/gpt-5*codex*","gpt-5-codex"],"copilot":["agent","gpt-5.4","sonnet","gpt-5","any"],"deep-research":["copilot/deep-research*","copilot/o3-deep-research*","copilot/o4-mini-deep-research*","google/deep-research*","gemini/deep-research*","openai/o3-deep-research*","openai/o4-mini-deep-research*"],"gemini":["agent","gemini-pro","gemini-flash","any"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*","gemini/gemini-*flash*"],"gemini-flash-lite":["copilot/gemini-*flash*lite*","google/gemini-*flash*lite*","gemini/gemini-*flash*lite*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*","gemini/gemini-*pro*"],"gemma":["copilot/gemma*","google/gemma*","gemini/gemma*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash-lite","copilot/raptor*mini*"],"opus":["copilot/*opus*","anthropic/*opus*"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"],"sonnet-6x":["copilot/*sonnet-4.5*","copilot/*sonnet-4-5*","anthropic/*sonnet-4.5*","anthropic/*sonnet-4-5*","copilot/*sonnet-3.7*","copilot/*sonnet-3-7*","anthropic/*sonnet-3.7*","anthropic/*sonnet-3-7*","copilot/*sonnet-3.5*","copilot/*sonnet-3-5*","anthropic/*sonnet-3.5*","anthropic/*sonnet-3-5*"],"vision":["copilot/gemini-*image*","gemini/gemini-*image*","copilot/gemini-*flash*","gemini/gemini-*flash*"]}},"container":{"imageTag":"0.25.49"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" + cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="" + if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw" + fi # shellcheck disable=SC1003 - sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ - -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ + -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: AWF_REFLECT_ENABLED: 1 COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_API_KEY: dummy-byok-key-for-offline-mode + COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} COPILOT_MODEL: claude-sonnet-4.5 GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.72.1 + GH_AW_VERSION: v0.74.8 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows @@ -866,7 +887,7 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.vsblob.vsassets.io,aka.ms,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,ci.dot.net,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,dev.azure.com,dist.nuget.org,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,microsoft.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkgs.dev.azure.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,vsassets.io,www.googleapis.com,www.microsoft.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.vsblob.vsassets.io,aka.ms,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,ci.dot.net,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,dev.azure.com,dist.nuget.org,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,microsoft.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkgs.dev.azure.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,vsassets.io,www.googleapis.com,www.microsoft.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} GH_AW_COMMAND: review @@ -982,6 +1003,7 @@ jobs: concurrency: group: "gh-aw-conclusion-macios-reviewer" cancel-in-progress: false + queue: max outputs: incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }} noop_message: ${{ steps.noop.outputs.noop_message }} @@ -990,15 +1012,17 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@bc56a0cad2f450c562810785ef38649c04db812a # v0.72.1 + uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} env: GH_AW_SETUP_WORKFLOW_NAME: ".NET for Apple Platforms PR Reviewer" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/macios-reviewer.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Download agent output artifact id: download-agent-output continue-on-error: true @@ -1088,6 +1112,8 @@ jobs: GH_AW_ENGINE_ID: "copilot" GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} + GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }} + GH_AW_EFFECTIVE_TOKENS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.effective_tokens_rate_limit_error || 'false' }} GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }} GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }} @@ -1100,6 +1126,7 @@ jobs: GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true" GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true" GH_AW_TIMEOUT_MINUTES: "20" + GH_AW_MAX_EFFECTIVE_TOKENS: "25000000" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | @@ -1144,15 +1171,17 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@bc56a0cad2f450c562810785ef38649c04db812a # v0.72.1 + uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} env: GH_AW_SETUP_WORKFLOW_NAME: ".NET for Apple Platforms PR Reviewer" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/macios-reviewer.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Download agent output artifact id: download-agent-output continue-on-error: true @@ -1178,7 +1207,7 @@ jobs: rm -rf /tmp/gh-aw/sandbox/firewall/logs rm -rf /tmp/gh-aw/sandbox/firewall/audit - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.41@sha256:cb2b565d070116d4b67e355775340528b5a2c3cb18b2c9049638bcc2df681770 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41@sha256:fadd0de387209f69a9a7a1b8722bb5e7fdfb80ba9749a5c60f0e4cd7582a74d0 ghcr.io/github/gh-aw-firewall/squid:0.25.41@sha256:1260445d25968dbf3ae70143964177a0e5914cf2ce07a6117f7d3caec6c3e3c4 + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.49 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49 ghcr.io/github/gh-aw-firewall/squid:0.25.49 - name: Check if detection needed id: detection_guard if: always() @@ -1237,11 +1266,11 @@ jobs: node-version: '24' package-manager-cache: false - name: Install GitHub Copilot CLI - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.40 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.48 env: GH_HOST: github.com - name: Install AWF binary - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.41 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.49 - name: Execute GitHub Copilot CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' continue-on-error: true @@ -1250,23 +1279,30 @@ jobs: timeout-minutes: 20 run: | set -o pipefail + printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt touch /tmp/gh-aw/agent-step-summary.md GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) export GH_AW_NODE_BIN + export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log) - printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.41/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","host.docker.internal","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true},"container":{"imageTag":"0.25.41"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json + printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.49/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","host.docker.internal","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000},"container":{"imageTag":"0.25.49"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" + cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="" + if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw" + fi # shellcheck disable=SC1003 - sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ - -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log + sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ + -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log env: AWF_REFLECT_ENABLED: 1 COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_API_KEY: dummy-byok-key-for-offline-mode + COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} COPILOT_MODEL: claude-sonnet-4.5 GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.72.1 + GH_AW_VERSION: v0.74.8 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows @@ -1294,6 +1330,7 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} + DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }} GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" with: script: | @@ -1304,10 +1341,11 @@ jobs: await main(); } catch (loadErr) { const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false'; + const detectionExecutionFailed = process.env.DETECTION_AGENTIC_EXECUTION_OUTCOME === 'failure'; const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr)); core.error(msg); core.setOutput('reason', 'parse_error'); - if (continueOnError) { + if (continueOnError && !detectionExecutionFailed) { core.warning('\u26A0\uFE0F ' + msg); core.setOutput('conclusion', 'warning'); core.setOutput('success', 'false'); @@ -1324,18 +1362,21 @@ jobs: outputs: activated: ${{ steps.check_membership.outputs.is_team_member == 'true' && steps.check_command_position.outputs.command_position_ok == 'true' }} matched_command: ${{ steps.check_command_position.outputs.matched_command }} + setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} + setup-span-id: ${{ steps.setup.outputs.span-id }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@bc56a0cad2f450c562810785ef38649c04db812a # v0.72.1 + uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} env: GH_AW_SETUP_WORKFLOW_NAME: ".NET for Apple Platforms PR Reviewer" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/macios-reviewer.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Check team membership for command workflow id: check_membership uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -1378,7 +1419,7 @@ jobs: GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }} GH_AW_ENGINE_ID: "copilot" GH_AW_ENGINE_MODEL: "claude-sonnet-4.5" - GH_AW_ENGINE_VERSION: "1.0.40" + GH_AW_ENGINE_VERSION: "1.0.48" GH_AW_WORKFLOW_ID: "macios-reviewer" GH_AW_WORKFLOW_NAME: ".NET for Apple Platforms PR Reviewer" outputs: @@ -1391,15 +1432,17 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@bc56a0cad2f450c562810785ef38649c04db812a # v0.72.1 + uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} env: GH_AW_SETUP_WORKFLOW_NAME: ".NET for Apple Platforms PR Reviewer" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/macios-reviewer.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Download agent output artifact id: download-agent-output continue-on-error: true @@ -1428,7 +1471,8 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.vsblob.vsassets.io,aka.ms,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,ci.dot.net,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,dev.azure.com,dist.nuget.org,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,microsoft.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkgs.dev.azure.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,vsassets.io,www.googleapis.com,www.microsoft.com" + GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }} + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.vsblob.vsassets.io,aka.ms,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,ci.dot.net,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,dev.azure.com,dist.nuget.org,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,microsoft.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkgs.dev.azure.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,vsassets.io,www.googleapis.com,www.microsoft.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request_review_comment\":{\"max\":50,\"side\":\"RIGHT\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{},\"submit_pull_request_review\":{\"allowed_events\":[\"COMMENT\",\"REQUEST_CHANGES\"],\"max\":1,\"supersede_older_reviews\":true}}" From c7e31395d70bf2a281cbdf0b6039d2b25416c9d4 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 26 May 2026 08:32:52 +0200 Subject: [PATCH 13/79] [github] Add autoformat-v2.yml: single zizmor-clean workflow (#25510) Create a new autoformat workflow that avoids the security concerns flagged by zizmor in the existing autoformat.yml + autoformat2.yml: - No workflow_run trigger (eliminates dangerous-triggers) - All actions pinned to SHA hashes (eliminates unpinned-uses) - persist-credentials: false on checkout (eliminates artipacked) - Job-level permissions (eliminates excessive-permissions) Two jobs handle same-repo and fork PRs differently: - Same-repo: format + commit + push directly - Fork: format + upload patch as artifact All logic is inlined (no rolfbjarne/autoformat references). --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/autoformat-v2.yml | 97 +++++++++++++++++++++++++++++ .github/workflows/autoformat.yml | 30 --------- .github/workflows/autoformat2.yml | 27 -------- 3 files changed, 97 insertions(+), 57 deletions(-) create mode 100644 .github/workflows/autoformat-v2.yml delete mode 100644 .github/workflows/autoformat.yml delete mode 100644 .github/workflows/autoformat2.yml diff --git a/.github/workflows/autoformat-v2.yml b/.github/workflows/autoformat-v2.yml new file mode 100644 index 000000000000..695354c78d1a --- /dev/null +++ b/.github/workflows/autoformat-v2.yml @@ -0,0 +1,97 @@ +name: Autoformat code v2 +on: pull_request + +permissions: {} + +jobs: + # Job for same-repo PRs: format and push directly + autoformat-push: + name: Autoformat and push + runs-on: ubuntu-latest + if: github.event.pull_request.head.repo.full_name == github.repository + permissions: + contents: write + + steps: + - name: 'Checkout' + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + ref: ${{ github.head_ref }} + fetch-depth: 1 + persist-credentials: false + + - name: 'Install .NET' + uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5 + with: + global-json-file: ./global.json + + - name: 'Autoformat' + run: ./tools/autoformat.sh + + - name: 'Check for changes' + id: check + run: | + if git diff --quiet; then + echo "changes=false" >> "$GITHUB_OUTPUT" + else + echo "changes=true" >> "$GITHUB_OUTPUT" + fi + + - name: 'Commit and push' + if: steps.check.outputs.changes == 'true' + env: + GH_TOKEN: ${{ github.token }} + run: | + git config user.email 'github-actions-autoformatter@xamarin.com' + git config user.name 'GitHub Actions Autoformatter' + git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}" + git add -A + git commit -m 'Auto-format source code' + git push + + # Job for fork PRs: format and upload patch as artifact + autoformat-artifact: + name: Autoformat and upload patch + runs-on: ubuntu-latest + if: github.event.pull_request.head.repo.full_name != github.repository + permissions: + contents: read + + steps: + - name: 'Checkout' + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + fetch-depth: 1 + + - name: 'Install .NET' + uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5 + with: + global-json-file: ./global.json + + - name: 'Autoformat' + run: ./tools/autoformat.sh + + - name: 'Create patch' + id: patch + run: | + if git diff --quiet; then + echo "changes=false" >> "$GITHUB_OUTPUT" + else + echo "changes=true" >> "$GITHUB_OUTPUT" + git config user.email 'github-actions-autoformatter@xamarin.com' + git config user.name 'GitHub Actions Autoformatter' + git add -A + git commit -m 'Auto-format source code' + mkdir -p autoformat-output + git format-patch HEAD~1 --stdout > autoformat-output/autoformat.patch + fi + + - name: 'Upload patch' + if: steps.patch.outputs.changes == 'true' + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: autoformat + path: autoformat-output/ diff --git a/.github/workflows/autoformat.yml b/.github/workflows/autoformat.yml deleted file mode 100644 index f20d04015aad..000000000000 --- a/.github/workflows/autoformat.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Autoformat code -on: pull_request - -# This action only need a single permission in order to autoformat the code. -permissions: - contents: read - -jobs: - autoformat-code: - name: Autoformat code - runs-on: ubuntu-latest - - steps: - - name: 'Checkout' - uses: actions/checkout@v6 - with: - fetch-depth: 0 - repository: ${{ github.event.pull_request.head.repo.full_name }} - ref: ${{ github.event.pull_request.head.sha }} - - name: 'Install .NET' - uses: actions/setup-dotnet@v5 - with: - global-json-file: ./global.json - - name: 'Autoformat' - uses: rolfbjarne/autoformat@v0.6 - with: - script: ./tools/autoformat.sh - git_user_email: 'github-actions-autoformatter@xamarin.com' - git_user_name: 'GitHub Actions Autoformatter' - checkoutSource: false diff --git a/.github/workflows/autoformat2.yml b/.github/workflows/autoformat2.yml deleted file mode 100644 index bc382aaf8c2e..000000000000 --- a/.github/workflows/autoformat2.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Autoformat code - push results -on: - workflow_run: - workflows: ["Autoformat code"] - types: - - completed - -# This action needs the following permissions in order to push the results back to the original branch. -permissions: - pull-requests: write - contents: write - -jobs: - push-and-notify: - name: Push autoformatted code and notify user - runs-on: ubuntu-latest - if: > - github.event.workflow_run.event == 'pull_request' && - github.event.workflow_run.conclusion == 'success' - steps: - - name: 'Push autoformatted patch' - uses: rolfbjarne/autoformat-push@v0.3 - with: - githubToken: ${{ secrets.GITHUB_TOKEN }} - git_user_email: 'github-actions-autoformatter@xamarin.com' - git_user_name: 'GitHub Actions Autoformatter' - commentOnPullRequest: false From 181d1357463a54a75ed490676e39d430ef1744de Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 26 May 2026 13:53:42 +0200 Subject: [PATCH 14/79] [dotnet-linker] Add a trimmer step to inline calls to Class.GetHandle[Intrinsic]. (#25318) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Adds a new trimmer step that inlines calls to `Class.GetHandle` (and `Class.GetHandleIntrinsic`) with direct native references to Objective-C classes. This is a companion feature to the existing `InlineDlfcnMethods` step, and together they enable the linker to replace managed dictionary lookups with direct native symbol references that are resolved at native link time. ## Motivation `Class.GetHandle ("ClassName")` calls go through a managed dictionary lookup at runtime. By inlining these calls, we: 1. **Improve startup performance** — eliminates dictionary lookups for class handles. 2. **Enable dead-code elimination** — the native linker can strip unreferenced class references, reducing binary size. 3. **Support NativeAOT** — ensures class references survive the NativeAOT compilation pipeline without relying on reflection-based discovery. ## Design The feature works in two modes controlled by the `InlineClassGetHandle` MSBuild property: - **`compatibility`** (default for IL trimming on .NET 11+): Rewrites `Class.GetHandle` calls to P/Invoke wrappers, then generates native code for all surviving wrappers after trimming. Falls back to `objc_getClass` at runtime for missing classes. - **`strict`** (default for NativeAOT on .NET 11+): Same rewriting, but does not generate runtime fallbacks — missing classes produce a build error. ### Pipeline 1. **ILTrim phase** (`InlineClassGetHandleStep`): Rewrites `Class.GetHandle`/`GetHandleIntrinsic` calls to generated P/Invoke methods. 2. **Type map generation** (`CoreTypeMapStep`): Emits class metadata (framework, introduced version, wrapper status) used by post-trim code generation. 3. **Post-trim processing** (`PostTrimmingProcessing` MSBuild task): Collects surviving class references from trimmed assemblies, generates native source files with `@interface` forward declarations and wrapper functions. 4. **NativeAOT path** (`CollectUnresolvedNativeSymbols` + `ComputeNativeAOTSurvivingNativeSymbols`): Extracts unresolved symbols from NativeAOT output and generates native code for surviving references only. ## Changes - **New linker steps**: `InlineClassGetHandleStep`, `GenerateInlinedClassGetHandleCodeStep`, updated `CoreTypeMapStep`. - **New MSBuild tasks**: `PostTrimmingProcessing` (generates native code), `CollectUnresolvedNativeSymbols`, `ComputeNativeAOTSurvivingNativeSymbols`, `CollectPostILTrimInformation`. - **Shared utilities**: `FileUtils.WriteIfDifferent`, `MachO` unresolved symbol extraction, framework lookup helpers. - **MSBuild integration**: New properties (`InlineClassGetHandle`), targets for post-trim native code generation and compilation. - **Tests**: New test variations (`inline-class-gethandle-compat`, `inline-class-gethandle-strict`) for both ILTrim and NativeAOT paths. - **Documentation**: `docs/code/class-handles.md`, `docs/building-apps/build-properties.md` updated. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/copilot-instructions.md | 4 + docs/building-apps/build-properties.md | 36 +++ docs/code/class-handles.md | 57 ++++ dotnet/targets/Xamarin.Shared.Sdk.props | 6 + dotnet/targets/Xamarin.Shared.Sdk.targets | 47 +++- .../Tasks/CollectPostILTrimInformation.cs | 74 +++-- .../Tasks/CollectUnresolvedNativeSymbols.cs | 2 +- .../ComputeNativeAOTSurvivingNativeSymbols.cs | 42 +-- .../Tasks/PostTrimmingProcessing.cs | 185 ++++++++++-- src/ObjCRuntime/Registrar.cs | 22 +- src/coreml.cs | 12 +- tests/common/test-variations.csproj | 29 ++ tests/dotnet/UnitTests/RegistrarTest.cs | 1 + tests/linker/link all/dotnet/shared.csproj | 6 + .../linker/trimmode link/dotnet/shared.csproj | 6 + tests/monotouch-test/dotnet/shared.csproj | 4 + .../xharness/Jenkins/TestVariationsFactory.cs | 8 +- tools/common/DerivedLinkContext.cs | 14 +- tools/common/FileUtils.cs | 14 + tools/common/PathUtils.cs | 20 ++ tools/common/StaticRegistrar.cs | 7 +- tools/dotnet-linker/AppBundleRewriter.cs | 50 ++++ tools/dotnet-linker/LinkerConfiguration.cs | 24 +- .../Steps/InlineClassGetHandleStep.cs | 264 ++++++++++++++++++ .../Steps/InlineDlfcnMethodsStep.cs | 41 +-- .../MonoTouch.Tuner/ListExportedSymbols.cs | 59 ++-- tools/mtouch/Errors.designer.cs | 9 + tools/mtouch/Errors.resx | 6 + 28 files changed, 883 insertions(+), 166 deletions(-) create mode 100644 docs/code/class-handles.md create mode 100644 tools/dotnet-linker/Steps/InlineClassGetHandleStep.cs diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 4a072d2b8a8d..618589332491 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -136,6 +136,10 @@ interface SomeClass { Located in `msbuild/` directory: - `Xamarin.MacDev.Tasks` - Shared Apple development tasks +### FileWrites + +If a target or task creates a file, that file must be added to the `FileWrites` item group. This ensures MSBuild's incremental clean can delete generated files. Additionally, if a target produces multiple output files, all of them should be listed in the target's `Outputs` attribute for correct incremental build behavior. + ### Project Templates Common project structure for Apple platform apps: diff --git a/docs/building-apps/build-properties.md b/docs/building-apps/build-properties.md index 461d2986fdca..684cd816cb7d 100644 --- a/docs/building-apps/build-properties.md +++ b/docs/building-apps/build-properties.md @@ -559,6 +559,42 @@ See also: * The [AlternateAppIcon](build-items.md#alternateappicon) item group. * The [AppIcon](#appicon) property. +## InlineClassGetHandle + +Controls whether the build system replaces runtime calls to `Class.GetHandle` / +`Class.GetHandleIntrinsic` with direct native references to Objective-C classes +at build time. + +See [docs/code/class-handles.md](../code/class-handles.md) for an overview. + +The valid options are: + +* `compatibility`: Inlines `Class.GetHandle` calls only for types whose declaring + type matches the requested Objective-C class name. +* `strict`: Inlines all `Class.GetHandle` calls unconditionally. Requires using + the static registrar (not the dynamic registrar). +* (empty): Disables inlining of `Class.GetHandle` calls. + +Default value: +* .NET 11+: `strict` when using NativeAOT (`PublishAot=true`), `compatibility` otherwise. +* .NET 10 and earlier: not set (disabled). + +Example: + +```xml + + compatibility + +``` + +Custom behavior for specific Objective-C classes can be set using the [ReferenceNativeSymbol](build-items.md#referencenativesymbols) item group: + +```xml + + + +``` + ## InlineDlfcnMethods Controls whether the build system replaces runtime calls to `ObjCRuntime.Dlfcn` methods with direct native symbol lookups at build time, eliminating the overhead of `dlsym` at runtime. diff --git a/docs/code/class-handles.md b/docs/code/class-handles.md new file mode 100644 index 000000000000..48255fb5aa6a --- /dev/null +++ b/docs/code/class-handles.md @@ -0,0 +1,57 @@ +# Objective-C classes + +Objective-C classes can be referenced from managed code in several ways: + +* Calls to Class.GetHandle / GetHandleIntrinsic + +It's highly desirable to use a direct native reference to Objective-C classes when building a mobile app, for a few reasons: + +* It's faster at runtime, and the app is smaller. +* If the referenced Objective-C class comes from a third-party static library, the + native linker can remove it if it's configured to remove unused code + (because the native linker can't see that the class is in fact used + at runtime) unless there's a direct native reference to the class. + +On the other hand there's one scenario when a direct native reference is not desirable: when the native Objective-C class does not exist. + +In order to create a direct native reference to Objective-C classes, we need to know the names of those Objective-C classes. + +## The `InlineClassGetHandle` property + +This behavior is controlled by the `InlineClassGetHandle` MSBuild property, which +can either be enabled or disabled. + +See the [build properties documentation](../building-apps/build-properties.md) for default values. + +## How it works + +During the build we try to collect the following: + +* Any calls to `Class.GetHandle[Intrinsic]` APIs: we try to collect the class name (this might not always succeed, if the class name is not a constant). + +This is further complicated by the fact that we only want to create native +references for managed references that survive trimming. + +So we do the following: + +1. During trimming, two custom linker steps execute: + + * `InlineClassGetHandleStep`: for every call `Class.GetHandle` we've + collected, this step creates a P/Invoke to a native method that will + return the Objective-C class for that symbol (using a direct native + reference), and modifies the code that fetches that symbol to call said + P/Invoke. + +2. After trimming, we figure out which of those symbols survived: + + * For ILTrim: the `_CollectPostILTrimInformation` MSBuild target inspects + the trimmed assemblies and collects all the inlined P/Invokes that + survived. Per-assembly results are cached to speed up incremental builds. + * For NativeAOT: the `_CollectPostNativeAOTTrimInformation` MSBuild target + inspects the native object file (or static library) produced by NativeAOT, + collects all unresolved native references, and filters them against the + Objective-C classes to determine which survived. + +3. The `_PostTrimmingProcessing` MSBuild target takes the surviving symbols + from either path, generates the corresponding native Objective-C code, and + adds it to the list of files to compile and link into the final executable. diff --git a/dotnet/targets/Xamarin.Shared.Sdk.props b/dotnet/targets/Xamarin.Shared.Sdk.props index 40b953914bac..532e7e2a44cb 100644 --- a/dotnet/targets/Xamarin.Shared.Sdk.props +++ b/dotnet/targets/Xamarin.Shared.Sdk.props @@ -107,6 +107,12 @@ compatibility + + + strict + compatibility + + diff --git a/dotnet/targets/Xamarin.Shared.Sdk.targets b/dotnet/targets/Xamarin.Shared.Sdk.targets index d1f37e5aceba..02926aa4ed8c 100644 --- a/dotnet/targets/Xamarin.Shared.Sdk.targets +++ b/dotnet/targets/Xamarin.Shared.Sdk.targets @@ -642,6 +642,7 @@ @(_BundlerEnvironmentVariables -> 'EnvironmentVariable=Overwrite=%(Overwrite)|%(Identity)=%(Value)') @(_XamarinFrameworkAssemblies -> 'FrameworkAssembly=%(Filename)') Interpreter=$(MtouchInterpreter) + InlineClassGetHandle=$(InlineClassGetHandle) InlineDlfcnMethods=$(InlineDlfcnMethods) IntermediateLinkDir=$(IntermediateLinkDir) IntermediateOutputPath=$(DeviceSpecificIntermediateOutputPath) @@ -674,6 +675,7 @@ TargetArchitectures=$(TargetArchitectures) TargetFramework=$(_ComputedTargetFrameworkMoniker) TypeMapAssemblyName=$(_TypeMapAssemblyName) + TypeMapFilePath=$(_TypeMapFilePath) TypeMapOutputDirectory=$(_TypeMapOutputDirectory) UseLlvm=$(MtouchUseLlvm) Verbosity=$(_BundlerVerbosity) @@ -798,6 +800,7 @@ <_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="MarkStep" Condition="'$(_AreAnyAssembliesTrimmed)' == 'true'" Type="Xamarin.Linker.Steps.PreMarkDispatcher" /> <_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="MarkStep" Type="Xamarin.Linker.ManagedRegistrarStep" Condition="'$(Registrar)' == 'managed-static' Or '$(Registrar)' == 'trimmable-static'" /> <_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="MarkStep" Type="Xamarin.Linker.TrimmableRegistrarStep" Condition="'$(Registrar)' == 'trimmable-static'" /> + <_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="MarkStep" Type="Xamarin.Linker.Steps.InlineClassGetHandleStep" Condition="'$(InlineClassGetHandle)' != '' And '$(InlineClassGetHandle)' != 'disabled'" /> <_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="OutputStep" Type="Xamarin.Linker.Steps.ListExportedSymbols" /> <_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="OutputStep" Type="Xamarin.Linker.Steps.PreOutputDispatcher" /> - <_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="OutputStep" Type="Xamarin.Linker.ClassHandleRewriterStep" /> + <_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="OutputStep" Type="Xamarin.Linker.ClassHandleRewriterStep" Condition="'$(InlineClassGetHandle)' == '' Or '$(InlineClassGetHandle)' == 'disabled'" /> + <_TypeMapFilePath Condition="'$(_TypeMapFilePath)' == ''">$(DeviceSpecificIntermediateOutputPath)type-map.txt @@ -1688,20 +1694,22 @@ <_ILTrimSurvivingNativeSymbolsFile>$(DeviceSpecificIntermediateOutputPath)inlined-dlfcn\iltrim-surviving-native-symbols.txt - <_NativeAOTUnresolvedSymbolsFile>$(DeviceSpecificIntermediateOutputPath)nativeaot-unresolved-symbols.txt <_NativeAOTSurvivingNativeSymbolsFile>$(DeviceSpecificIntermediateOutputPath)nativeaot-surviving-native-symbols.txt + <_ILTrimSurvivingClassesFile>$(DeviceSpecificIntermediateOutputPath)inlined-class-gethandle\iltrim-classes.txt + <_NativeAOTSurvivingClassesFile>$(DeviceSpecificIntermediateOutputPath)inlined-class-gethandle\nativeaot-classes.txt @@ -1710,6 +1718,7 @@ + @@ -1720,12 +1729,17 @@ <_SurvivingNativeSymbolsFile Include="$(_ILTrimSurvivingNativeSymbolsFile)" Condition="Exists('$(_ILTrimSurvivingNativeSymbolsFile)')" /> <_SurvivingNativeSymbolsFile Include="$(_NativeAOTSurvivingNativeSymbolsFile)" Condition="Exists('$(_NativeAOTSurvivingNativeSymbolsFile)')" /> + + <_SurvivingClassesFiles Include="$(_ILTrimSurvivingClassesFile)" Condition="Exists('$(_ILTrimSurvivingClassesFile)')" /> + <_SurvivingClassesFiles Include="$(_NativeAOTSurvivingClassesFile)" Condition="Exists('$(_NativeAOTSurvivingClassesFile)')" /> @@ -1735,8 +1749,15 @@ <_PostTrimmingSourceFiles> $(DeviceSpecificIntermediateOutputPath)posttrim-info-compiled/%(Arch)/%(Filename).o + <_NativeExecutableObjectFiles Include="@(_PostTrimmingSourceFiles -> '%(OutputFile)')" /> + + - <_CompiledPostTrimmingFiles Include="@(_PostTrimmingSourceFiles -> '%(OutputFile)')" /> - <_NativeExecutableObjectFiles Include="@(_CompiledPostTrimmingFiles)" /> - + @@ -1814,7 +1833,7 @@ _ReadAppManifest; _WriteAppManifest; _CompileNativeExecutable; - _PostTrimmingProcessing; + _CompilePostTrimmingFiles; _ReidentifyDynamicLibraries; _AddSwiftLinkerFlags; _ComputeLinkNativeExecutableInputs; @@ -1827,8 +1846,11 @@ Condition="'$(_UseNativeAot)' == 'true'" DependsOnTargets="_ComputePostTrimmingPaths;_CompileNativeExecutable" Inputs="$(NativeObject)" - Outputs="$(_NativeAOTSurvivingNativeSymbolsFile)" + Outputs="$(_NativeAOTSurvivingNativeSymbolsFile);$(_NativeAOTSurvivingClassesFile)" > + + <_NativeAOTUnresolvedSymbolsFile>$(DeviceSpecificIntermediateOutputPath)nativeaot-unresolved-symbols.txt + + + + + diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/CollectPostILTrimInformation.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/CollectPostILTrimInformation.cs index 116ce430cb49..7f2baaad79e3 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/CollectPostILTrimInformation.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/CollectPostILTrimInformation.cs @@ -9,12 +9,15 @@ using Mono.Cecil; +using Xamarin.Bundler; +using Xamarin.Utils; + #nullable enable namespace Xamarin.MacDev.Tasks { /// /// Scans trimmed assemblies to collect information that survived trimming. - /// See docs/code/native-symbols.md for an overview of native symbol handling. + /// See docs/code/native-symbols.md and docs/code/class-handles.md for an overview of native symbol handling. /// public class CollectPostILTrimInformation : XamarinTask { [Required] @@ -26,6 +29,12 @@ public class CollectPostILTrimInformation : XamarinTask { [Required] public string SurvivingNativeSymbolsFile { get; set; } = ""; + /// + /// Output file listing the Class.GetHandle calls that survived trimming. + /// + [Required] + public string SurvivingClassesFile { get; set; } = ""; + /// /// Directory for per-assembly cache files, to avoid re-scanning unchanged assemblies. /// @@ -51,17 +60,17 @@ void CollectSurvivingNativeSymbols () continue; var assemblyName = Path.GetFileNameWithoutExtension (assemblyPath); - var cacheFile = Path.Combine (CacheDirectory, assemblyName + ".dlfcn-symbols.cache"); + var cacheFile = Path.Combine (CacheDirectory, assemblyName + ".internal-symbols.cache"); string []? cachedSymbols = null; if (File.Exists (cacheFile) && File.GetLastWriteTimeUtc (cacheFile) >= File.GetLastWriteTimeUtc (assemblyPath)) { cachedSymbols = File.ReadAllLines (cacheFile); - Log.LogMessage (MessageImportance.Low, "Using cached dlfcn symbols for {0}", assemblyName); + Log.LogMessage (MessageImportance.Low, "Using cached internal symbols for {0}", assemblyName); survivingSymbols.UnionWith (cachedSymbols); } else { var assemblySymbols = new HashSet (); - CollectDlfcnSymbolsFromAssembly (assemblyPath, assemblySymbols); + CollectInternalSymbolsFromAssembly (assemblyPath, assemblySymbols); // Write per-assembly cache (sorted for stability). var sortedAssemblySymbols = assemblySymbols.OrderBy (s => s).ToArray (); @@ -71,29 +80,53 @@ void CollectSurvivingNativeSymbols () } } + WriteSymbolsToFile (this, SurvivingNativeSymbolsFile, FilterToDlfcnSymbols (survivingSymbols)); + WriteSymbolsToFile (this, SurvivingClassesFile, FilterToClassSymbols (survivingSymbols)); + } + + public static void WriteSymbolsToFile (XamarinTask task, string file, IEnumerable unsortedSymbols) + { // Write the combined results only if contents changed (sorted for stability). - var sorted = survivingSymbols.OrderBy (s => s).ToArray (); + var sorted = unsortedSymbols.OrderBy (s => s).ToArray (); - if (File.Exists (SurvivingNativeSymbolsFile)) { - var existing = File.ReadAllLines (SurvivingNativeSymbolsFile); - if (existing.SequenceEqual (sorted)) + if (File.Exists (file)) { + var existing = File.ReadAllLines (file); + if (existing.SequenceEqual (sorted)) { + task.Log.LogMessage (MessageImportance.Low, "The file {0} is already up-to-date with {1} symbols", file, sorted.Length); return; + } } - var dir = Path.GetDirectoryName (SurvivingNativeSymbolsFile); - if (!string.IsNullOrEmpty (dir)) - Directory.CreateDirectory (dir); - File.WriteAllLines (SurvivingNativeSymbolsFile, sorted); - Log.LogMessage (MessageImportance.Low, "Found {0} surviving inlined dlfcn symbols", survivingSymbols.Count); + PathUtils.CreateDirectoryForFile (file); + File.WriteAllLines (file, sorted); + task.Log.LogMessage (MessageImportance.Low, "Wrote {0} symbols to {1}", sorted.Length, file); } - static void CollectDlfcnSymbolsFromAssembly (string assemblyPath, HashSet survivingSymbols) + public static IEnumerable FilterToDlfcnSymbols (IEnumerable symbols) { - const string prefix = "xamarin_Dlfcn_"; - const string suffix = "_Native"; + return FilterTo (symbols, "_xamarin_Dlfcn_", "_Native"); + } + public static IEnumerable FilterToClassSymbols (IEnumerable symbols) + { + return FilterTo (symbols, "_xamarin_Class_GetHandle_", "_Native"); + } + + static IEnumerable FilterTo (IEnumerable symbols, string prefix, string suffix) + { + return symbols + .Where (symbol => symbol.StartsWith (prefix, StringComparison.Ordinal) && symbol.EndsWith (suffix, StringComparison.Ordinal)) + .Select (symbol => symbol.Substring (prefix.Length, symbol.Length - prefix.Length - suffix.Length)); + } + + static void CollectInternalSymbolsFromAssembly (string assemblyPath, HashSet survivingSymbols) + { using var assembly = AssemblyDefinition.ReadAssembly (assemblyPath, new ReaderParameters { ReadSymbols = false }); foreach (var module in assembly.Modules) { + if (!module.HasModuleReferences) + continue; + if (!module.ModuleReferences.Any (mr => mr.Name == "__Internal")) + continue; foreach (var type in module.Types) { if (!type.HasMethods) continue; @@ -102,14 +135,7 @@ static void CollectDlfcnSymbolsFromAssembly (string assemblyPath, HashSet public class ComputeNativeAOTSurvivingNativeSymbols : XamarinTask { /// @@ -29,39 +29,17 @@ public class ComputeNativeAOTSurvivingNativeSymbols : XamarinTask { [Required] public string SurvivingNativeSymbolsFile { get; set; } = ""; + /// + /// Output file listing the Class.GetHandle calls that survived trimming. + /// + [Required] + public string SurvivingClassesFile { get; set; } = ""; + public override bool Execute () { - if (!File.Exists (UnresolvedSymbolsFile)) - return !Log.HasLoggedErrors; - - const string prefix = "_xamarin_Dlfcn_"; - const string suffix = "_Native"; - var survivingSymbols = new HashSet (); - - foreach (var sym in File.ReadAllLines (UnresolvedSymbolsFile)) { - if (!sym.StartsWith (prefix) || !sym.EndsWith (suffix)) - continue; - var symbolLength = sym.Length - prefix.Length - suffix.Length; - if (symbolLength <= 0) - continue; - var symbolName = sym.Substring (prefix.Length, symbolLength); - survivingSymbols.Add (symbolName); - } - - var sorted = survivingSymbols.OrderBy (s => s).ToArray (); - - if (File.Exists (SurvivingNativeSymbolsFile)) { - var existing = File.ReadAllLines (SurvivingNativeSymbolsFile); - if (existing.SequenceEqual (sorted)) - return !Log.HasLoggedErrors; - } - - var dir = Path.GetDirectoryName (SurvivingNativeSymbolsFile); - if (!string.IsNullOrEmpty (dir)) - Directory.CreateDirectory (dir); - File.WriteAllLines (SurvivingNativeSymbolsFile, sorted); - Log.LogMessage (MessageImportance.Low, "Found {0} surviving native symbols from NativeAOT", survivingSymbols.Count); - + var unresolvedSymbols = File.Exists (UnresolvedSymbolsFile) ? File.ReadAllLines (UnresolvedSymbolsFile) : []; + CollectPostILTrimInformation.WriteSymbolsToFile (this, SurvivingNativeSymbolsFile, CollectPostILTrimInformation.FilterToDlfcnSymbols (unresolvedSymbols)); + CollectPostILTrimInformation.WriteSymbolsToFile (this, SurvivingClassesFile, CollectPostILTrimInformation.FilterToClassSymbols (unresolvedSymbols)); return !Log.HasLoggedErrors; } } diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/PostTrimmingProcessing.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/PostTrimmingProcessing.cs index 77705a8dd1d7..bb65800df942 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/PostTrimmingProcessing.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/PostTrimmingProcessing.cs @@ -9,13 +9,14 @@ using Microsoft.Build.Framework; using Xamarin.Bundler; +using Xamarin.Utils; #nullable enable namespace Xamarin.MacDev.Tasks { /// /// Performs post-trimming processing, generating native code only for symbols that survived trimming. - /// See docs/code/native-symbols.md for an overview of native symbol handling. + /// See docs/code/native-symbols.md and docs/code/class-handles.md for an overview of native symbol handling. /// public class PostTrimmingProcessing : XamarinTask { [Required] @@ -26,6 +27,13 @@ public class PostTrimmingProcessing : XamarinTask { public ITaskItem [] ReferenceNativeSymbol { get; set; } = []; + /// + /// Files listing calls to Class.GetHandle that survived trimming. Each file contains one symbol name per line. + /// These can come from either ILTrim (CollectPostILTrimInformation) or NativeAOT + /// (ComputeNativeAOTSurvivingNativeSymbols). + /// + public ITaskItem [] SurvivingClassesFiles { get; set; } = []; + /// /// Files listing native symbols that survived trimming. Each file contains one symbol name per line. /// These can come from either ILTrim (CollectPostILTrimInformation) or NativeAOT @@ -33,6 +41,9 @@ public class PostTrimmingProcessing : XamarinTask { /// public ITaskItem [] SurvivingNativeSymbolsFiles { get; set; } = []; + // Type map + public string TypeMapFilePath { get; set; } = ""; + /// /// Output native source files to be compiled and linked. /// @@ -71,41 +82,166 @@ HashSet IgnoredSymbols { public override bool Execute () { - var items = new List (); + var nativeSourceFiles = new List (); + + Directory.CreateDirectory (OutputDirectory); + GenerateInlinedDlfcnNativeCode (nativeSourceFiles); + GenerateInlinedClassGetHandleNativeCode (nativeSourceFiles); - GenerateInlinedDlfcnNativeCode (items); + NativeSourceFiles = nativeSourceFiles.Select (path => { + var item = new Microsoft.Build.Utilities.TaskItem (path); + item.SetMetadata ("Arch", Architecture.ToLowerInvariant ()); + return item; + }).ToArray (); - NativeSourceFiles = items.ToArray (); return !Log.HasLoggedErrors; } - void GenerateInlinedDlfcnNativeCode (List items) + static HashSet ReadUniqueLinesFromFiles (IEnumerable files) { - // Collect all surviving symbols from all input files. - var survivingSymbols = new HashSet (); - foreach (var file in SurvivingNativeSymbolsFiles) { + var lines = new HashSet (); + foreach (var file in files) { var path = file.ItemSpec; if (!File.Exists (path)) continue; - survivingSymbols.UnionWith (File.ReadAllLines (path)); + lines.UnionWith (File.ReadAllLines (path)); + } + return lines; + } + + List FilterOutIgnoredSymbols (HashSet survivingSymbols, bool filterObjectiveCClasses) + { + var rv = new HashSet (survivingSymbols); + + foreach (var rns in ReferenceNativeSymbol) { + var nativeSymbol = rns.ItemSpec; + var symbolMode = rns.GetMetadata ("SymbolMode"); + if (!string.Equals (symbolMode, "Ignore", StringComparison.OrdinalIgnoreCase)) + continue; + + var symbolType = rns.GetMetadata ("SymbolType").ToLowerInvariant (); + switch (symbolType) { + case "objectivecclass": + if (filterObjectiveCClasses && rv.Remove (nativeSymbol)) { + Log.LogMessage (MessageImportance.Low, "Ignoring Objective-C class '{0}'", nativeSymbol); + } + break; + case "function": + case "field": + if (!filterObjectiveCClasses && rv.Remove (nativeSymbol)) { + Log.LogMessage (MessageImportance.Low, "Ignoring native symbol '{0}'", nativeSymbol); + } + break; + default: + Log.LogMessage (MessageImportance.Low, "Ignoring symbol '{0}' with unknown SymbolType '{1}'", nativeSymbol, symbolType); + continue; + } + } + + rv.Remove (""); // no empty symbols + + return rv.OrderBy (v => v).ToList (); + } + + void GenerateInlinedClassGetHandleNativeCode (List items) + { + // Collect all surviving symbols from all input files. + var classes = FilterOutIgnoredSymbols (ReadUniqueLinesFromFiles (SurvivingClassesFiles), filterObjectiveCClasses: true); + + if (classes.Count == 0) { + Log.LogMessage (MessageImportance.Low, "There were no surviving Objective-C classes that require inlined Class.GetHandle native code."); + return; + } + + if (string.IsNullOrEmpty (TypeMapFilePath) || !File.Exists (TypeMapFilePath)) { + Log.LogError ("The type map file '{0}' does not exist. This file is generated by the linker's CoreTypeMapStep. Ensure that trimming ran successfully.", TypeMapFilePath ?? ""); + return; + } + + var typeMapEntries = File.ReadAllLines (TypeMapFilePath) + .Select (line => { + var parts = line.Split ('|'); + string className = ""; + string framework = ""; + string introduced = ""; + bool iswrapper = false; + bool isstubclass = false; + foreach (var part in parts) { + var kvp = part.Split (new char [] { '=' }, 2); + if (kvp.Length != 2) + continue; + var key = kvp [0].Trim (); + var value = kvp [1].Trim (); + switch (key) { + case "Class": + className = value; + break; + case "Framework": + framework = value; + break; + case "Introduced": + introduced = value; + break; + case "IsWrapper": + iswrapper = string.Equals (value, "true", StringComparison.OrdinalIgnoreCase); + break; + case "IsStubClass": + isstubclass = string.Equals (value, "true", StringComparison.OrdinalIgnoreCase); + break; + } + } + return (Class: className, Framework: framework, Introduced: introduced, IsWrapper: iswrapper, IsStubClass: isstubclass); + }) + .ToArray (); + + var typeMap = new Dictionary (); + foreach (var entry in typeMapEntries) { + if (string.IsNullOrEmpty (entry.Class)) + continue; + if (typeMap.ContainsKey (entry.Class)) { + Log.LogError ("Duplicate class '{0}' found in the type map file '{1}'.", entry.Class, TypeMapFilePath); + return; + } + typeMap [entry.Class] = entry; } - var survivingButIgnoredSymbols = survivingSymbols.Intersect (IgnoredSymbols).ToList (); - if (survivingButIgnoredSymbols.Count > 0) { - Log.LogMessage (MessageImportance.Low, "The following symbols survived trimming but are marked as ignored:"); - foreach (var symbol in survivingButIgnoredSymbols) - Log.LogMessage (MessageImportance.Low, " {0}", symbol); - survivingSymbols.ExceptWith (survivingButIgnoredSymbols); + var sb = new StringBuilder (); + sb.AppendLine ($"#include "); + sb.AppendLine ($"#include "); + foreach (var objectiveCClassName in classes) { + // We don't want to import every header under the sun to find the @interface definitions for each class, so we generate + // a forward declaration for each class. To avoid potential issues with missing classes at runtime, we mark each declaration with __attribute__((weak_import)). + // The only exception is that we need to #include Foundation, which means we can't create declarations for Foundation classes. + if (!typeMap.TryGetValue (objectiveCClassName, out var info)) { + sb.AppendLine ($"__attribute__((weak_import)) @interface {objectiveCClassName} : NSObject @end // no objc type found"); + } else if (info.IsWrapper && info.Framework == "Foundation") { + // This is a special case for wrapper classes in the Foundation framework. Since we need to #include Foundation, we can't create a forward declaration for these classes. However, since they are wrappers, we know they won't be missing at runtime, so we don't need to mark them with __attribute__((weak_import)). + sb.AppendLine ($"// The class '{objectiveCClassName}' comes from the Foundation framework, so no generated @interface declaration."); + } else { + if (info.IsStubClass) + sb.AppendLine ("__attribute__((objc_class_stub)) __attribute__((objc_subclassing_restricted))"); + sb.AppendLine ($"__attribute__((weak_import)) @interface {objectiveCClassName} : NSObject @end // is stub: {info.IsStubClass}"); + } + sb.AppendLine ($"Class xamarin_Class_GetHandle_{objectiveCClassName}_Native ();"); + sb.AppendLine ($"Class xamarin_Class_GetHandle_{objectiveCClassName}_Native () {{ return [{objectiveCClassName} class]; }}"); + sb.AppendLine (); } + var outputPath = Path.Combine (OutputDirectory, "inlined-class-gethandle.m"); + FileUtils.WriteIfDifferent (outputPath, sb.ToString (), (msg) => Log.LogMessage (MessageImportance.Low, msg)); + + items.Add (outputPath); + } + + void GenerateInlinedDlfcnNativeCode (List items) + { + var survivingSymbols = FilterOutIgnoredSymbols (ReadUniqueLinesFromFiles (SurvivingNativeSymbolsFiles), filterObjectiveCClasses: false); + if (survivingSymbols.Count == 0) { Log.LogMessage (MessageImportance.Low, "There were no surviving symbols that require inlined dlfcn native code."); return; } - Directory.CreateDirectory (OutputDirectory); - var outputPath = Path.Combine (OutputDirectory, "inlined-dlfcn.c"); - var sb = new StringBuilder (); // The generated C code uses 'extern void*' declarations and returns the address of the symbol. // This is intentional: it allows the native linker to resolve the symbol at link time, which @@ -118,17 +254,10 @@ void GenerateInlinedDlfcnNativeCode (List items) sb.AppendLine (); } - var content = sb.ToString (); - if (File.Exists (outputPath) && File.ReadAllText (outputPath) == content) { - Log.LogMessage (MessageImportance.Low, "Inlined dlfcn native code is up to date"); - } else { - File.WriteAllText (outputPath, content); - Log.LogMessage (MessageImportance.Low, "Generated inlined dlfcn native code with {0} symbols", survivingSymbols.Count); - } + var outputPath = Path.Combine (OutputDirectory, "inlined-dlfcn.c"); + FileUtils.WriteIfDifferent (outputPath, sb.ToString (), (msg) => Log.LogMessage (MessageImportance.Low, msg)); - var item = new Microsoft.Build.Utilities.TaskItem (outputPath); - item.SetMetadata ("Arch", Architecture.ToLowerInvariant ()); - items.Add (item); + items.Add (outputPath); } } } diff --git a/src/ObjCRuntime/Registrar.cs b/src/ObjCRuntime/Registrar.cs index a90eed1e9ffe..3b7c402bd3cf 100644 --- a/src/ObjCRuntime/Registrar.cs +++ b/src/ObjCRuntime/Registrar.cs @@ -1119,7 +1119,7 @@ protected virtual void OnRegisterCategory (ObjCType type, [NotNullIfNotNull (nam protected abstract ConnectAttribute? GetConnectAttribute (TProperty property); // Return null if no attribute is found. Do not consider inherited properties. public abstract ProtocolAttribute? GetProtocolAttribute (TType type); // Return null if no attribute is found. Do not consider base types. protected abstract IEnumerable GetProtocolMemberAttributes (TType type); // Return null if no attributes found. Do not consider base types. - protected virtual Version? GetSdkIntroducedVersion (TType obj, out string? message) { message = null; return null; } // returns the sdk version when the type was introduced for the current platform (null if all supported versions) + public virtual Version? GetSdkIntroducedVersion (TType obj, out string? message) { message = null; return null; } // returns the sdk version when the type was introduced for the current platform (null if all supported versions) protected abstract Version GetSDKVersion (); protected abstract TType? GetProtocolAttributeWrapperType (TType type); // Return null if no attribute is found. Do not consider base types. public abstract BindAsAttribute? GetBindAsAttribute (TMethod method, int parameter_index); // If parameter_index = -1 then get the attribute for the return type. Return null if no attribute is found. Must consider base method. @@ -2639,6 +2639,26 @@ protected string ToSignature (TType type, ObjCMember member, bool forProperty = throw ErrorHelper.CreateError (4101, Errors.MT4101, GetTypeFullName (type)); } + // Gets the Objective-C name for the given type. + // Returns false if the type in question isn't exported to Objective-C, and thus doesn't have an Objective-C name. + public bool TryGetExportedTypeName (TType type, [NotNullWhen (true)] out string? name) + { + name = null; + + var registerAttribute = GetRegisterAttribute (type); + if (registerAttribute is null) + return false; + + if (!registerAttribute.IsWrapper) + return false; + + if (HasProtocolAttribute (type)) + return false; + + name = GetExportedTypeName (type, registerAttribute); + return true; + } + public string GetExportedTypeName (TType type, RegisterAttribute? register_attribute) { string? name = null; diff --git a/src/coreml.cs b/src/coreml.cs index b723bf9afee6..c4d6c7038e82 100644 --- a/src/coreml.cs +++ b/src/coreml.cs @@ -1581,9 +1581,9 @@ interface MLWritable { } #if !XAMCORE_5_0 - [Deprecated (PlatformName.MacOSX, 13, 3, message: "Use Background Assets or 'NSUrlSession' instead.")] - [Deprecated (PlatformName.MacCatalyst, 16, 4, message: "Use Background Assets or 'NSUrlSession' instead.")] - [Deprecated (PlatformName.iOS, 16, 4, message: "Use Background Assets or 'NSUrlSession' instead.")] + [Obsoleted (PlatformName.MacOSX, 13, 3, message: "Use Background Assets or 'NSUrlSession' instead.")] + [Obsoleted (PlatformName.MacCatalyst, 16, 4, message: "Use Background Assets or 'NSUrlSession' instead.")] + [Obsoleted (PlatformName.iOS, 16, 4, message: "Use Background Assets or 'NSUrlSession' instead.")] [iOS (14, 0)] [MacCatalyst (14, 0)] [NoTV] @@ -1613,9 +1613,9 @@ interface MLModelCollection { #endif // !XAMCORE_5_0 #if !XAMCORE_5_0 - [Deprecated (PlatformName.MacOSX, 13, 3, message: "Use Background Assets or 'NSUrlSession' instead.")] - [Deprecated (PlatformName.MacCatalyst, 16, 4, message: "Use Background Assets or 'NSUrlSession' instead.")] - [Deprecated (PlatformName.iOS, 16, 4, message: "Use Background Assets or 'NSUrlSession' instead.")] + [Obsoleted (PlatformName.MacOSX, 13, 3, message: "Use Background Assets or 'NSUrlSession' instead.")] + [Obsoleted (PlatformName.MacCatalyst, 16, 4, message: "Use Background Assets or 'NSUrlSession' instead.")] + [Obsoleted (PlatformName.iOS, 16, 4, message: "Use Background Assets or 'NSUrlSession' instead.")] [iOS (14, 0)] [MacCatalyst (14, 0)] [NoTV] diff --git a/tests/common/test-variations.csproj b/tests/common/test-variations.csproj index ea1da526a288..35762fdeb2dd 100644 --- a/tests/common/test-variations.csproj +++ b/tests/common/test-variations.csproj @@ -10,6 +10,7 @@ + @@ -25,6 +26,8 @@ + + @@ -68,6 +71,21 @@ <_TestVariationApplied>true + + + true + <_IsPublishing>true + full + + strict + $(DefineConstants);STATIC_NATIVE_SYMBOL_LOOKUP + + strict + managed-static + + <_TestVariationApplied>true + + partial <_TestVariationApplied>true @@ -161,6 +179,17 @@ $(DefineConstants);STATIC_NATIVE_SYMBOL_LOOKUP + + compatibility + <_TestVariationApplied>true + + + + strict + managed-static + <_TestVariationApplied>true + + <_InvalidTestVariations Include="$(TestVariation.Split('|'))" Exclude="@(TestVariations)" /> diff --git a/tests/dotnet/UnitTests/RegistrarTest.cs b/tests/dotnet/UnitTests/RegistrarTest.cs index 003efbdc3281..f7aad07cb565 100644 --- a/tests/dotnet/UnitTests/RegistrarTest.cs +++ b/tests/dotnet/UnitTests/RegistrarTest.cs @@ -76,6 +76,7 @@ public void ClassRewriterTest (ApplePlatform platform, bool rewriteHandles) // enable the linker (so that the main assembly is modified) properties ["LinkMode"] = "full"; properties ["MtouchLink"] = "full"; + properties ["InlineClassGetHandle"] = "disabled"; if (rewriteHandles) properties ["MtouchExtraArgs"] = "--optimize=redirect-class-handles"; diff --git a/tests/linker/link all/dotnet/shared.csproj b/tests/linker/link all/dotnet/shared.csproj index f11e80546aac..6b75ec2a2d4e 100644 --- a/tests/linker/link all/dotnet/shared.csproj +++ b/tests/linker/link all/dotnet/shared.csproj @@ -86,4 +86,10 @@ + + + + + + diff --git a/tests/linker/trimmode link/dotnet/shared.csproj b/tests/linker/trimmode link/dotnet/shared.csproj index 525978b17599..48401810a36d 100644 --- a/tests/linker/trimmode link/dotnet/shared.csproj +++ b/tests/linker/trimmode link/dotnet/shared.csproj @@ -88,4 +88,10 @@ + + + + + + diff --git a/tests/monotouch-test/dotnet/shared.csproj b/tests/monotouch-test/dotnet/shared.csproj index 52b2396eff16..989a89db0bef 100644 --- a/tests/monotouch-test/dotnet/shared.csproj +++ b/tests/monotouch-test/dotnet/shared.csproj @@ -54,6 +54,10 @@ + + + + diff --git a/tests/xharness/Jenkins/TestVariationsFactory.cs b/tests/xharness/Jenkins/TestVariationsFactory.cs index 812e53156a69..00c05d4882c6 100644 --- a/tests/xharness/Jenkins/TestVariationsFactory.cs +++ b/tests/xharness/Jenkins/TestVariationsFactory.cs @@ -117,10 +117,12 @@ IEnumerable GetTestData (RunTestTask test) yield return new TestData { Variation = "Debug (interpreter)", TestVariation = "interpreter", Ignored = ignore }; yield return new TestData { Variation = "Release (interpreter)", TestVariation = "release|interpreter", Ignored = ignore }; } + yield return new TestData { Variation = $"Release (compat inline Class.GetHandle)", TestVariation = "inline-class-gethandle-compat|release", Ignored = ignore }; + yield return new TestData { Variation = $"Release (strict inline Class.GetHandle)", TestVariation = "inline-class-gethandle-strict|release", Ignored = ignore }; yield return new TestData { Variation = $"Release (compat inline dlfcn)", TestVariation = "inline-dlfcn-methods-compat|release", Ignored = ignore }; yield return new TestData { Variation = $"Release (strict inline dlfcn, link sdk)", TestVariation = "inline-dlfcn-methods-strict|linksdk|release", Ignored = ignore }; if (mac_supports_arm64) - yield return new TestData { Variation = $"Release (NativeAOT, .NET 11 defaults)", TestVariation = "inline-dlfcn-methods-strict|nativeaot|release", Ignored = ignore, RuntimeIdentifier = arm64_sim_runtime_identifier }; // it's necessary to specify RID, because NativeAOT defaults to building for device + yield return new TestData { Variation = $"Release (NativeAOT, .NET 11 defaults)", TestVariation = "nativeaot-net11-defaults|release", Ignored = ignore, RuntimeIdentifier = arm64_sim_runtime_identifier }; // it's necessary to specify RID, because NativeAOT defaults to building for device break; case "introspection": if (mac_supports_arm64) @@ -156,6 +158,8 @@ IEnumerable GetTestData (RunTestTask test) yield return new TestData { Variation = "Release (NativeAOT)", TestVariation = "release|nativeaot", Ignored = ignore }; yield return new TestData { Variation = "Release (NativeAOT, ARM64)", TestVariation = "release|nativeaot", Ignored = !mac_supports_arm64 ? true : ignore, RuntimeIdentifier = arm64_runtime_identifier }; yield return new TestData { Variation = "Release (NativeAOT, x64)", TestVariation = "release|nativeaot", Ignored = ignore, RuntimeIdentifier = x64_runtime_identifier }; + if (mac_supports_arm64) + yield return new TestData { Variation = $"Release (NativeAOT, .NET 11 defaults)", TestVariation = "release|nativeaot-net11-defaults", Ignored = ignore }; yield return new TestData { Variation = "Release (trimmable static registrar, NativeAOT)", TestVariation = "trimmable-static-registrar|nativeaot|release", Ignored = ignore }; yield return new TestData { Variation = "Release (trimmable static registrar, NativeAOT, ARM64)", TestVariation = "trimmable-static-registrar|nativeaot|release", Ignored = !mac_supports_arm64 ? true : ignore, RuntimeIdentifier = arm64_runtime_identifier }; yield return new TestData { Variation = "Release (trimmable static registrar, NativeAOT, x64)", TestVariation = "trimmable-static-registrar|nativeaot|release", Ignored = ignore, RuntimeIdentifier = x64_runtime_identifier }; @@ -168,6 +172,8 @@ IEnumerable GetTestData (RunTestTask test) yield return new TestData { Variation = "Debug (interpreter)", TestVariation = "interpreter", Ignored = ignore }; yield return new TestData { Variation = "Release (interpreter)", TestVariation = "release|interpreter", Ignored = ignore }; } + yield return new TestData { Variation = $"Release (compat inline dlfcn)", TestVariation = "inline-dlfcn-methods-compat|release", Ignored = ignore }; + yield return new TestData { Variation = $"Release (strict inline dlfcn, link sdk)", TestVariation = "inline-dlfcn-methods-strict|linksdk|release", Ignored = ignore }; break; } break; diff --git a/tools/common/DerivedLinkContext.cs b/tools/common/DerivedLinkContext.cs index 743b1b768461..0f639be2e7b5 100644 --- a/tools/common/DerivedLinkContext.cs +++ b/tools/common/DerivedLinkContext.cs @@ -41,6 +41,9 @@ public class DerivedLinkContext : LinkContext { // true/false = corresponding constant value Dictionary? isdirectbinding_value; + // A map from Objective-C class name to C# type + Dictionary? objectiveCTypeInfo; + // Store interfaces the linker has linked away so that the static registrar can access them. public Dictionary> ProtocolImplementations { get; private set; } = new Dictionary> (); // Store types the linker has linked away so that the static registrar can access them. @@ -73,6 +76,7 @@ public AssemblyDefinition Corlib { return corlib; } } + public HashSet? CachedIsNSObject { get { return cached_isnsobject; } set { cached_isnsobject = value; } @@ -83,6 +87,11 @@ public HashSet? CachedIsNSObject { set { isdirectbinding_value = value; } } + public Dictionary? ObjectiveCTypeInfo { + get { return objectiveCTypeInfo; } + set { objectiveCTypeInfo = value; } + } + public IList DataContract { get { return srs_data_contract; @@ -237,8 +246,11 @@ public void AddLinkedAwayType (TypeDefinition td) } #if !LEGACY_TOOLS - public bool HasAvailabilityAttributesShowingUnavailableInSimulator (ICustomAttributeProvider provider, MethodDefinition? methodForErrorReporting = null) + public bool HasAvailabilityAttributesShowingUnavailableInSimulator (ICustomAttributeProvider? provider, MethodDefinition? methodForErrorReporting = null) { + if (provider is null) + return false; + if (!App.IsSimulatorBuild) { LinkerConfiguration.Report (LinkerConfiguration.Context, ErrorHelper.CreateError (99, "HasAvailabilityAttributesShowingUnavailableInSimulator should not be called when not building for the simulator. Please file an issue at https://github.com/dotnet/macios/issues.")); return false; diff --git a/tools/common/FileUtils.cs b/tools/common/FileUtils.cs index f8b3c617674f..7b406fd24ed6 100644 --- a/tools/common/FileUtils.cs +++ b/tools/common/FileUtils.cs @@ -75,5 +75,19 @@ public static bool UpdateFile (string targetFile, Action createOutput) } } + + public static void WriteIfDifferent (string path, string contents, Action log) + { + if (!File.Exists (path)) { + log ($"File '{path}' contents are not up-to-date, because the file doesn't exist."); + File.WriteAllText (path, contents); + } else if (string.Equals (contents, File.ReadAllText (path), StringComparison.Ordinal)) { + log ($"File '{path}' contents are up-to-date."); + return; + } else { + log ($"File '{path}' contents are not up-to-date."); + File.WriteAllText (path, contents); + } + } } } diff --git a/tools/common/PathUtils.cs b/tools/common/PathUtils.cs index c1675692caef..355253f5c2a7 100644 --- a/tools/common/PathUtils.cs +++ b/tools/common/PathUtils.cs @@ -422,5 +422,25 @@ static bool IsLongPathsEnabledRegistry { } } } + + public static void CreateDirectoryForFile (string? file) + { +#if NET + if (string.IsNullOrEmpty (file)) +#else + if (string.IsNullOrEmpty (file) || file is null) +#endif + return; + + var dir = Path.GetDirectoryName (file); +#if NET + if (string.IsNullOrEmpty (dir)) +#else + if (string.IsNullOrEmpty (dir) || dir is null) +#endif + return; + + Directory.CreateDirectory (dir); + } } } diff --git a/tools/common/StaticRegistrar.cs b/tools/common/StaticRegistrar.cs index 2170362d0c79..9891a0c2ec6e 100644 --- a/tools/common/StaticRegistrar.cs +++ b/tools/common/StaticRegistrar.cs @@ -1591,7 +1591,7 @@ protected override IEnumerable GetProtocolMemberAttribu } #if !LEGACY_TOOLS - bool GetDotNetAvailabilityAttribute (ICustomAttribute ca, ApplePlatform currentPlatform, out Version? sdkVersion, out string? message) + public bool GetDotNetAvailabilityAttribute (ICustomAttribute ca, ApplePlatform currentPlatform, out Version? sdkVersion, out string? message) { var caType = ca.AttributeType; @@ -1601,6 +1601,7 @@ bool GetDotNetAvailabilityAttribute (ICustomAttribute ca, ApplePlatform currentP string supportedPlatformAndVersion; switch (ca.ConstructorArguments.Count) { case 1: + case 2: // second argument can be a message supportedPlatformAndVersion = (string) ca.ConstructorArguments [0].Value; break; default: @@ -1679,7 +1680,7 @@ bool CollectAvailabilityAttributes (IEnumerable attributes, ou return false; } - protected override Version? GetSdkIntroducedVersion (TypeReference obj, out string? message) + public override Version? GetSdkIntroducedVersion (TypeReference obj, out string? message) { TypeDefinition td = obj.Resolve (); @@ -2025,7 +2026,7 @@ public bool IsCustomType (ObjCType type) return false; } - bool IsPlatformType (TypeReference type) + public bool IsPlatformType (TypeReference type) { if (type.IsNested) return false; diff --git a/tools/dotnet-linker/AppBundleRewriter.cs b/tools/dotnet-linker/AppBundleRewriter.cs index 5013997108d1..1b3908f009ea 100644 --- a/tools/dotnet-linker/AppBundleRewriter.cs +++ b/tools/dotnet-linker/AppBundleRewriter.cs @@ -70,6 +70,7 @@ public AssemblyDefinition SystemConsoleAssembly { Dictionary> type_map = new (); Dictionary method_map = new (); Dictionary field_map = new (); + Dictionary created_types = new (); public AppBundleRewriter (LinkerConfiguration configuration) { @@ -1450,6 +1451,7 @@ public void ClearCurrentAssembly () type_map.Clear (); method_map.Clear (); field_map.Clear (); + created_types.Clear (); } public CustomAttribute CreateAttribute (MethodReference constructor) @@ -1676,5 +1678,53 @@ static bool DebugAttributes { return debug_attributes.Value; } } + + public TypeDefinition GetOrCreateType (ModuleDefinition module, string @namespace, string @typename, out bool created) + { + created = false; + + var fullName = @namespace + "." + typename; + if (!created_types.TryGetValue (fullName, out var cachedTypeDefinition)) { + cachedTypeDefinition = module.Types.FirstOrDefault (t => t.Namespace == @namespace && t.Name == typename); + if (cachedTypeDefinition is null) { + cachedTypeDefinition = new TypeDefinition (@namespace, typename, TypeAttributes.Public | TypeAttributes.Sealed, module.TypeSystem.Object); + module.Types.Add (cachedTypeDefinition); + created = true; + } + created_types [fullName] = cachedTypeDefinition; + } + + return cachedTypeDefinition; + } + + public MethodDefinition CreateInternalPInvoke (ModuleDefinition module, string @namespace, string @typename, string methodName, out bool created) + { + var cachedTypeDefinition = GetOrCreateType (module, @namespace, @typename, out _); + var nativeMethod = methodName; + var rv = cachedTypeDefinition.Methods.FirstOrDefault (m => m.Name == methodName); + if (rv is not null) { + created = false; + return rv; // already exists, no need to create it again + } + + // [DllImport ("__Internal")] + // static extern IntPtr {methodName} (); + + rv = new MethodDefinition (methodName, MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.PInvokeImpl, System_IntPtr); + rv.IsPreserveSig = true; + + var mod = module.ModuleReferences.FirstOrDefault (mr => mr.Name == "__Internal"); + if (mod is null) { + mod = new ModuleReference ("__Internal"); + module.ModuleReferences.Add (mod); + } + rv.PInvokeInfo = new PInvokeInfo (PInvokeAttributes.CharSetNotSpec | PInvokeAttributes.CallConvCdecl, nativeMethod, mod); + + cachedTypeDefinition.Methods.Add (rv); + + created = true; + + return rv; + } } } diff --git a/tools/dotnet-linker/LinkerConfiguration.cs b/tools/dotnet-linker/LinkerConfiguration.cs index 8458b9030239..b42db901cbad 100644 --- a/tools/dotnet-linker/LinkerConfiguration.cs +++ b/tools/dotnet-linker/LinkerConfiguration.cs @@ -32,6 +32,7 @@ public class LinkerConfiguration { public bool HybridGlobalization { get; private set; } public InlineDlfcnMethodsMode InlineDlfcnMethods { get; set; } public bool InlineDlfcnMethodsEnabled => InlineDlfcnMethods != InlineDlfcnMethodsMode.Disabled; + public InlineClassGetHandleMode InlineClassGetHandle { get; set; } // Per-assembly field symbols collected by InlineDlfcnMethodsStep, keyed by assembly name. public Dictionary> InlinedDlfcnFields { get; } = new Dictionary> (); // All [Field] symbol names collected by ProcessExportedFields, used in compatibility mode. @@ -45,6 +46,7 @@ public class LinkerConfiguration { public string RelativeAppBundlePath { get; private set; } = string.Empty; public Version? SdkVersion { get; private set; } public string SdkRootDirectory { get; private set; } = string.Empty; + public string TypeMapFilePath { get; set; } = string.Empty; public int Verbosity => Driver.Verbosity; public string XamarinNativeLibraryDirectory { get; private set; } = string.Empty; @@ -210,11 +212,17 @@ public static LinkerConfiguration GetInstance (LinkContext context) case "FrameworkAssembly": FrameworkAssemblies.Add (value); break; + case "InlineClassGetHandle": + if (Enum.TryParse (value, true, out var inlineClassGetHandleMode)) + InlineClassGetHandle = inlineClassGetHandleMode; + else if (string.IsNullOrEmpty (value)) + InlineClassGetHandle = InlineClassGetHandleMode.Disabled; + else + throw new InvalidOperationException ($"Unknown InlineClassGetHandle value: {value}"); + break; case "InlineDlfcnMethods": if (Enum.TryParse (value, true, out var inlineDlfcnMode)) InlineDlfcnMethods = inlineDlfcnMode; - else if (string.Equals (value, "compatibility", StringComparison.OrdinalIgnoreCase)) - InlineDlfcnMethods = InlineDlfcnMethodsMode.Compat; else if (string.IsNullOrEmpty (value)) InlineDlfcnMethods = InlineDlfcnMethodsMode.Disabled; else @@ -377,6 +385,9 @@ public static LinkerConfiguration GetInstance (LinkContext context) case "TypeMapAssemblyName": Application.TypeMapAssemblyName = value; break; + case "TypeMapFilePath": + TypeMapFilePath = value; + break; case "TypeMapOutputDirectory": Application.TypeMapOutputDirectory = value; break; @@ -567,6 +578,7 @@ public void Write () Console.WriteLine ($" SdkRootDirectory: {SdkRootDirectory}"); Console.WriteLine ($" SdkVersion: {SdkVersion}"); Console.WriteLine ($" TypeMapAssemblyName: {Application.TypeMapAssemblyName}"); + Console.WriteLine ($" TypeMapFilePath: {TypeMapFilePath}"); Console.WriteLine ($" TypeMapOutputDirectory: {Application.TypeMapOutputDirectory}"); Console.WriteLine ($" UseInterpreter: {Application.UseInterpreter}"); Console.WriteLine ($" UseLlvm: {Application.IsLLVM}"); @@ -665,4 +677,12 @@ public enum InlineDlfcnMethodsMode { Disabled, Strict, Compat, + Compatibility = Compat, +} + +public enum InlineClassGetHandleMode { + Disabled, + Strict, + Compat, + Compatibility = Compat, } diff --git a/tools/dotnet-linker/Steps/InlineClassGetHandleStep.cs b/tools/dotnet-linker/Steps/InlineClassGetHandleStep.cs new file mode 100644 index 000000000000..1a48cba7b3b0 --- /dev/null +++ b/tools/dotnet-linker/Steps/InlineClassGetHandleStep.cs @@ -0,0 +1,264 @@ +using System.Linq; +using System.Text; + +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Linker; +using Mono.Tuner; + +using Xamarin.Bundler; +using Xamarin.Utils; + +#nullable enable + +namespace Xamarin.Linker.Steps; + +// See docs/code/class-handles.md for an overview of class handle handling. + +// Find all the references to Objective-C classes for each assembly. +// * If an assembly is trimmed, we replace calls to (inline) Class.GetHandle with a P/Invoke that fetches the class in question. +// * If an assembly is not trimmed, we do not inline Class.GetHandle, but we keep a list of all the Objective-C classes that are used, so that we can tell the native linker about them so they're not linked away by the native linker. +// +// The sticky problem is that we can't inspect the managed output from NativeAOT, which means +// we can't list the Objective-C classes NativeAOT-compiled assemblies using. To get around this +// problem, we convert calls to Class.GetHandle to a P/Invoke which fetches the native Objective-C +// class handle directly - because we _can_ list the P/Invoke calls from NativeAOT-compiled code. +// +// For non-trimmed assemblies, we don't need to do this, because we know nothing from the assembly +// will be trimmed away. This has the added advantage of being Hot Reload compatible, because we +// can't modify assemblies when we're doing Hot Reload. + +public class InlineClassGetHandleStep : AssemblyModifierStep { + + protected override string Name { get; } = "Inline Class GetHandle"; + protected override int ErrorCode { get; } = 2262; + + bool strictMode; + bool? inlining_enabled; + + Dictionary objectiveCTypeMap = new (); + + public const string PInvokePrefix = "xamarin_Class_GetHandle_"; + public const string PInvokeSuffix = "_Native"; + + protected override void TryProcess () + { + strictMode = Configuration.InlineClassGetHandle == InlineClassGetHandleMode.Strict; + + if (strictMode && Configuration.Application.Registrar == Bundler.RegistrarMode.Dynamic) { + Report (ErrorHelper.CreateError (Configuration.Application, 2262, null, Errors.MX2262)); + } + + objectiveCTypeMap = DerivedLinkContext.StaticRegistrar.Types.ToDictionary (v => v.Value.ExportedName, v => v.Value); + + if (!string.IsNullOrEmpty (Configuration.TypeMapFilePath)) { + var sb = new StringBuilder (); + foreach (var info in objectiveCTypeMap.Values.OrderBy (v => v.ExportedName)) { + var td = info.Type.Resolve (); + if (td is null) + continue; + var introduced = DerivedLinkContext.StaticRegistrar.GetSdkIntroducedVersion (td, out _); + Frameworks.TryGetFramework (App, td, out string? framework); + sb.AppendLine ($"Class={info.ExportedName}|Framework={framework}|Introduced={introduced}|IsWrapper={info.IsWrapper}|IsStubClass={info.IsStubClass}"); + } + Driver.WriteIfDifferent (Configuration.TypeMapFilePath, sb.ToString ()); + } + + base.TryProcess (); + } + + protected override bool IsActiveFor (AssemblyDefinition assembly) + { + if (!Configuration.Profile.IsOrReferencesProductAssembly (assembly)) + return false; + + // we have to process both trimmed and non-trimmed assemblies. + + return true; + } + + protected override bool ModifyAssembly (AssemblyDefinition assembly) + { + inlining_enabled = Annotations.GetAction (assembly) == AssemblyAction.Link; + var modified = base.ModifyAssembly (assembly); + inlining_enabled = null; + return modified; + } + + protected override bool ProcessType (TypeDefinition type) + { + var modified = false; + + if (inlining_enabled == true) { + modified |= ProcessMethods (type); + } else { + if (ListExportedSymbols.TryGetRequiredObjectiveCType (DerivedLinkContext, type, out var exportedName)) { + DerivedLinkContext.RequiredSymbols.AddObjectiveCClass (exportedName).AddMember (type); + } + } + return modified; + } + + MethodDefinition GetOrCreatePInvokeMethod (MethodDefinition callingMethod, string objectiveCClassName) + { + // [DllImport ("__Internal")] + // static extern IntPtr xamarin_Class_GetClassHandle_{objectiveCClassName}_Native (); + + return abr.CreateInternalPInvoke (callingMethod.Module, "ObjCRuntime", "Class", $"{PInvokePrefix}{objectiveCClassName}{PInvokeSuffix}", out _); + } + + protected override bool ProcessMethod (MethodDefinition method) + { + var modified = false; + + if (!method.HasBody) + return modified; + + if (method.DeclaringType.Name == "Class" && method.DeclaringType.Namespace == "ObjCRuntime") + return modified; // don't process the Class methods themselves + + bool isOurOwnCode () + { + // Don't show warnings for a few places in our own code where we call Class.GetHandle in un-inlinable ways. + switch (method.DeclaringType.Namespace) { + case "Registrar": + switch (method.DeclaringType.Name) { + case "DynamicRegistrar": + switch (method.Name) { + case "OnReloadType": + case "OnRegisterType": + return true; + } + break; + } + break; + } + return false; + } + + foreach (var instr in method.Body.Instructions) { + if (instr.Operand is not MethodReference mr) + continue; + if (mr.DeclaringType.Name != "Class" || mr.DeclaringType.Namespace != "ObjCRuntime") + continue; + if (mr.Name != "GetHandle" && mr.Name != "GetHandleIntrinsic") + continue; + if (mr.Parameters.Count != 1) + continue; + if (!mr.Parameters [0].ParameterType.Is ("System", "String")) + continue; + if (!mr.ReturnType.Is ("ObjCRuntime", "NativeHandle")) + continue; + + var ldstr = instr.Previous; + if (ldstr.OpCode != OpCodes.Ldstr) { + if (!isOurOwnCode ()) + Driver.Log (3, "Unknown or unsupported pattern in call to Class.GetHandle in '{0}': {1}. The call will not be inlined.", FormatMethod (method), ldstr); + continue; + } + if (ldstr.Operand is not string objectiveCClassName) { + if (!isOurOwnCode ()) + Driver.Log (3, "Unknown or unsupported pattern in call to Class.GetHandle in '{0}': {1}. The call will not be inlined.", FormatMethod (method), ldstr.Operand); + continue; + } + + if (!objectiveCTypeMap.TryGetValue (objectiveCClassName, out var objCType)) { + Driver.Log (3, "Could not find a managed type for the Objective-C type '{1}' in the call to Class.GetHandle in '{0}', assuming the Objective-C type is available in the simulator.", FormatMethod (method), objectiveCClassName); + } + + if (!strictMode) { + if (objCType is not null && objCType.Type == method.DeclaringType) { + // This is a call to Class.GetHandle for the same class that it's being called from, this is OK. + } else if (ListExportedSymbols.TryGetRequiredObjectiveCType (DerivedLinkContext, method.DeclaringType, out var exportedName)) { + if (exportedName != objectiveCClassName) { + Driver.Log (3, "The call to Class.GetHandle in '{0}' is trying to get the handle for the Objective-C class '{1}', but the declaring type's exported name is '{2}', not '{1}'. Since we're in compat mode, we're assuming the class should not be preserved.", FormatMethod (method), objectiveCClassName, exportedName); + continue; + } + } else { + if (App.StaticRegistrar.GetCategoryAttribute (method.DeclaringType) is not null) + Driver.Log (3, "The call to Class.GetHandle in '{0}' is trying to get the handle for the Objective-C class '{1}', but we couldn't determine whether this class should be statically preserved or not. Since we're in compat mode, we're assuming the class should not be statically preserved.", FormatMethod (method), objectiveCClassName); + continue; + } + } + + // Check if the Objective-C class is listed as a ReferenceNativeSymbol with Ignore mode, and if so, don't inline the call to Class.GetHandle (because the native symbol won't be available at link time) + var existingSymbol = DerivedLinkContext.RequiredSymbols.Find (Symbol.ObjectiveCPrefix + objectiveCClassName); + if (existingSymbol is not null && existingSymbol.Type == SymbolType.ObjectiveCClass && existingSymbol.Mode == SymbolMode.Ignore) { + Driver.Log (3, "Not inlining the call to Class.GetHandle (\"{0}\") in method {1} because the class is listed as a ReferenceNativeSymbol with Ignore mode.", objectiveCClassName, FormatMethod (method)); + continue; + } + + if (objCType is not null) { + if (DerivedLinkContext.App.IsSimulatorBuild) { + if (DerivedLinkContext.HasAvailabilityAttributesShowingUnavailableInSimulator (objCType.Type.Resolve (), method)) { + Driver.Log (3, "Not inlining the call to Class.GetHandle (\"{0}\") in method {1} because the type is marked with an attribute indicating it's not available in the simulator.", objectiveCClassName, FormatMethod (method)); + continue; + } + } + + if (IsUnsupported (objCType.Type.Resolve ())) { + Driver.Log (3, "Not inlining the call to Class.GetHandle (\"{0}\") in method {1} because the type is marked with an [UnsupportedOSPlatform] attribute.", objectiveCClassName, FormatMethod (method)); + continue; + } + + if (objCType.Methods is null && DerivedLinkContext.StaticRegistrar.IsPlatformType (objCType.Type) && !objCType.IsProtocol && !objCType.IsCategory && objCType.IsModel) { + // The static registrar skips generating code for this type, so we shouldn't inline calls to Class.GetHandle for it, because the P/Invoke we generate won't be able to find the native symbol for it. + continue; + } + + if (Frameworks.TryGetFramework (App, objCType.Type.Resolve (), out Framework? framework) && framework.IsFrameworkUnavailable (App)) { + Driver.Log (3, "Not inlining the call to Class.GetHandle (\"{0}\") in method {1} because the framework {2} is unavailable.", objectiveCClassName, FormatMethod (method), framework.Name); + continue; + } + + if (objCType.Type.Is ("UIKit", "UITitlebar")) { + // UITitlebar is a weird special case, the class exists in the headers, and it's documented online, but it's not possible to link with it (not even in an Xcode project, it's not in any .tbd files). + continue; + } + } + + ldstr.OpCode = OpCodes.Call; + ldstr.Operand = GetOrCreatePInvokeMethod (method, objectiveCClassName); + + instr.OpCode = OpCodes.Call; + instr.Operand = abr.NativeObject_op_Implicit_NativeHandle; + + modified = true; + } + + return modified; + } + + bool IsUnsupported (TypeDefinition? type) + { + if (type is null) + return false; + + if (!type.HasCustomAttributes) + return false; + + foreach (var ca in type.CustomAttributes) { + if (!ca.AttributeType.Is ("System.Runtime.Versioning", "UnsupportedOSPlatformAttribute")) + continue; + + if (!DerivedLinkContext.StaticRegistrar.GetDotNetAvailabilityAttribute (ca, App.Platform, out var sdkVersion, out _)) + continue; + + if (sdkVersion is null) + return true; // if there's no version, then it's always unavailable + + return sdkVersion <= App.SdkVersion; + } + + return false; + } + + static string FormatMethod (MethodReference method) + { + var rv = method.FullName; + var idx = rv.IndexOf (' '); + if (idx > 0) + rv = rv.Substring (idx + 1); + return rv; + } +} diff --git a/tools/dotnet-linker/Steps/InlineDlfcnMethodsStep.cs b/tools/dotnet-linker/Steps/InlineDlfcnMethodsStep.cs index d6b214986d02..99b67d4a9984 100644 --- a/tools/dotnet-linker/Steps/InlineDlfcnMethodsStep.cs +++ b/tools/dotnet-linker/Steps/InlineDlfcnMethodsStep.cs @@ -28,6 +28,9 @@ public class InlineDlfcnMethodsStep : AssemblyModifierStep { bool strictMode; + public const string PInvokePrefix = "xamarin_Dlfcn_"; + public const string PInvokeSuffix = "_Native"; + protected override void TryProcess () { strictMode = Configuration.InlineDlfcnMethods == InlineDlfcnMethodsMode.Strict; @@ -99,18 +102,15 @@ TypeDefinition GetDlfcnType (ModuleDefinition module, string @namespace, string? { var frameworkOverride = !string.IsNullOrEmpty (fieldLibraryName) ? fieldLibraryName : current_framework; var ns = string.IsNullOrEmpty (frameworkOverride) ? @namespace : frameworkOverride; - var dlfcn = module.Types.FirstOrDefault (t => t.Namespace == ns && t.Name == "Dlfcn"); - if (dlfcn is null) { - dlfcn = new TypeDefinition (ns, "Dlfcn", TypeAttributes.NotPublic | TypeAttributes.Sealed, module.TypeSystem.Object); - module.Types.Add (dlfcn); - + var rv = abr.GetOrCreateType (module, ns, "Dlfcn", out var created); + if (created) { if (!string.IsNullOrEmpty (frameworkOverride)) { - var attrib = new CustomAttribute (abr.ObjectiveCFrameworkAttribute_ctor_String); + var attrib = abr.CreateAttribute (abr.ObjectiveCFrameworkAttribute_ctor_String); attrib.ConstructorArguments.Add (new CustomAttributeArgument (abr.System_String, frameworkOverride)); - dlfcn.CustomAttributes.Add (attrib); + rv.CustomAttributes.Add (attrib); } } - return dlfcn; + return rv; } void AddField (string assemblyName, string symbolName) @@ -124,30 +124,13 @@ void AddField (string assemblyName, string symbolName) MethodDefinition GetOrCreatePInvokeMethod (MethodDefinition callingMethod, string symbolName) { - var dlfcn = GetDlfcnType (callingMethod); - var methodName = $"xamarin_Dlfcn_{symbolName}_Native"; - var nativeMethod = methodName; - var rv = dlfcn.Methods.FirstOrDefault (m => m.Name == methodName); - if (rv is not null) - return rv; // already exists, no need to create it again - // [DllImport ("__Internal")] // static extern IntPtr xamarin_Dlfcn_{symbolName}_Native (); - rv = new MethodDefinition (methodName, MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.PInvokeImpl, abr.System_IntPtr); - rv.IsPreserveSig = true; - - var mod = callingMethod.Module.ModuleReferences.FirstOrDefault (mr => mr.Name == "__Internal"); - if (mod is null) { - mod = new ModuleReference ("__Internal"); - callingMethod.Module.ModuleReferences.Add (mod); - } - rv.PInvokeInfo = new PInvokeInfo (PInvokeAttributes.CharSetNotSpec | PInvokeAttributes.CallConvCdecl, nativeMethod, mod); - - dlfcn.Methods.Add (rv); - - AddField (callingMethod.Module.Assembly.Name.Name, symbolName); - + var methodName = $"{PInvokePrefix}{symbolName}{PInvokeSuffix}"; + var rv = abr.CreateInternalPInvoke (callingMethod.Module, callingMethod.DeclaringType.Namespace, "Dlfcn", methodName, out var created); + if (created) + AddField (callingMethod.Module.Assembly.Name.Name, symbolName); return rv; } diff --git a/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs b/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs index eda79a994f73..b74d18cddca7 100644 --- a/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs +++ b/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs @@ -18,7 +18,6 @@ namespace Xamarin.Linker.Steps { public class ListExportedSymbols : BaseStep { PInvokeWrapperGenerator? state; - bool is_product_assembly; PInvokeWrapperGenerator? State { get { @@ -80,8 +79,6 @@ protected override void ProcessAssembly (AssemblyDefinition assembly) if (!hasSymbols) return; - is_product_assembly = Configuration.Profile.IsProductAssembly (assembly); - var modified = false; foreach (var type in assembly.MainModule.Types) modified |= ProcessType (type); @@ -114,40 +111,41 @@ bool ProcessType (TypeDefinition type) void AddRequiredObjectiveCType (TypeDefinition type) { + if (TryGetRequiredObjectiveCType (DerivedLinkContext, type, out var exportedName)) + DerivedLinkContext.RequiredSymbols.AddObjectiveCClass (exportedName).AddMember (type); + } + + // Returns true if the specified type represents an Objective-C class that should be referenced as a required symbol, so that the native linker doesn't link it away. + public static bool TryGetRequiredObjectiveCType (DerivedLinkContext derivedLinkContext, TypeDefinition type, [NotNullWhen (true)] out string? exportedName) + { + exportedName = null; + // The product assembly only has one type we may need to keep: XamarinSwiftFunctions - if (is_product_assembly) { + if (derivedLinkContext.LinkerConfiguration.Profile.IsProductAssembly (type.Module.Assembly)) { switch (type.Name) { case "XamarinSwiftFunctions": break; default: - return; + return false; } } - var staticRegistrar = DerivedLinkContext.StaticRegistrar; + var staticRegistrar = derivedLinkContext.StaticRegistrar; if (staticRegistrar is null) - return; + return false; - var registerAttribute = staticRegistrar.GetRegisterAttribute (type); - if (registerAttribute is null) - return; + if (!staticRegistrar.TryGetExportedTypeName (type, out exportedName)) + return false; - if (!registerAttribute.IsWrapper) - return; - - if (staticRegistrar.HasProtocolAttribute (type)) - return; - - if (DerivedLinkContext.App.RequireLinkWithAttributeForObjectiveCClassSearch) { + if (derivedLinkContext.App.RequireLinkWithAttributeForObjectiveCClassSearch) { var has_linkwith_attributes = false; - if (DerivedLinkContext.App.Assemblies.TryGetValue (type.Module.Assembly, out var asm)) + if (derivedLinkContext.App.Assemblies.TryGetValue (type.Module.Assembly, out var asm)) has_linkwith_attributes = asm.HasLinkWithAttributes; if (!has_linkwith_attributes) - return; + return false; } - var exportedName = staticRegistrar.GetExportedTypeName (type, registerAttribute); - DerivedLinkContext.RequiredSymbols.AddObjectiveCClass (exportedName).AddMember (type); + return true; } bool ProcessMethod (MethodDefinition method) @@ -191,13 +189,18 @@ bool ProcessMethod (MethodDefinition method) switch (pinfo.Module.Name) { case "__Internal": - // For NativeAOT builds, don't add inlined dlfcn P/Invoke wrappers as - // required symbols: only the surviving ones will have native code generated, - // so force-referencing all of them causes linker errors for symbols that - // NativeAOT trimmed away. For non-NativeAOT builds, the wrappers are resolved - // via dlsym and need the -u flags to be exported from the binary. - if (Configuration.InlineDlfcnMethodsEnabled && Configuration.Application.XamarinRuntime == XamarinRuntime.NativeAOT && pinfo.EntryPoint.StartsWith ("xamarin_Dlfcn_", StringComparison.Ordinal)) - break; + if (Configuration.Application.XamarinRuntime == XamarinRuntime.NativeAOT) { + // For NativeAOT builds, don't add inlined dlfcn P/Invoke wrappers as + // required symbols: only the surviving ones will have native code generated, + // so force-referencing all of them causes linker errors for symbols that + // NativeAOT trimmed away. For non-NativeAOT builds, the wrappers are resolved + // via dlsym and need the -u flags to be exported from the binary. + if (Configuration.InlineDlfcnMethodsEnabled && pinfo.EntryPoint.StartsWith (InlineDlfcnMethodsStep.PInvokePrefix, StringComparison.Ordinal)) + break; + // Same goes for inlined Class.GetHandle calls. + if (Configuration.InlineClassGetHandle != InlineClassGetHandleMode.Disabled && pinfo.EntryPoint.StartsWith (InlineClassGetHandleStep.PInvokePrefix, StringComparison.Ordinal)) + break; + } Driver.Log (4, "Adding native reference to {0} in {1} because it's referenced by {2} in {3}.", pinfo.EntryPoint, pinfo.Module.Name, method.FullName, method.Module.Name); DerivedLinkContext.RequiredSymbols.AddFunction (pinfo.EntryPoint).AddMember (method); break; diff --git a/tools/mtouch/Errors.designer.cs b/tools/mtouch/Errors.designer.cs index ee92d9580d81..7c3f1861b4ab 100644 --- a/tools/mtouch/Errors.designer.cs +++ b/tools/mtouch/Errors.designer.cs @@ -3596,6 +3596,15 @@ public static string MX2261 { } } + /// + /// Looks up a localized string similar to The 'InlineClassGetHandle' option is set to 'Strict', but we're using the dynamic registrar. This is not a supported configuration, because 'Strict' mode requires exported Objective-C classes to be available at compile time, but the dynamic registrar will create them at runtime. Please either change the 'InlineClassGetHandle' option to 'Disabled' or 'Compat', or switch to using the static registrar.. + /// + public static string MX2262 { + get { + return ResourceManager.GetString("MX2262", resourceCulture); + } + } + /// /// Looks up a localized string similar to Could not {0} the assembly '{1}'. /// diff --git a/tools/mtouch/Errors.resx b/tools/mtouch/Errors.resx index 987d7c75e479..cc89125d1ff1 100644 --- a/tools/mtouch/Errors.resx +++ b/tools/mtouch/Errors.resx @@ -1131,6 +1131,12 @@ '{0}' has multiple SupportedSimulator attributes for the '{1}' platform. Please file an issue at https://github.com/dotnet/macios/issues/new + + The 'InlineClassGetHandle' option is set to 'Strict', but we're using the dynamic registrar. This is not a supported configuration, because 'Strict' mode requires exported Objective-C classes to be available at compile time, but the dynamic registrar will create them at runtime. Please either change the 'InlineClassGetHandle' option to 'Disabled' or 'Compat', or switch to using the static registrar. + + + + From 9dc9c389760c3f001bbc086611b562593206a40d Mon Sep 17 00:00:00 2001 From: Alex Soto Date: Wed, 27 May 2026 02:06:01 -0400 Subject: [PATCH 15/79] Make NSUrlSessionHandler handle redirects like the .NET handlers do (#25529) This change updates NSUrlSessionHandler redirect auth handling so it matches the .NET handlers more closely, this is just for .NET compatibility and for correctness; there's no security enforcement here. After an automatic redirect credentials are now resolved against the current redirect target Url instead of the orginal request Url. Also, non CredentialCache credentials are not reused across redirects by default, which is consistent with `SocketsHttpHandler` and `WinHttpHandler`. This avoids sending credentials that were scoped, or only meant for the original origin, to a redirected destination. CredentialCache entries still work when they are explicitly scoped for the redirect target. --- src/Foundation/NSUrlSessionHandler.cs | 98 +++++- .../System.Net.Http/MessageHandlers.cs | 284 ++++++++++++++++++ 2 files changed, 379 insertions(+), 3 deletions(-) diff --git a/src/Foundation/NSUrlSessionHandler.cs b/src/Foundation/NSUrlSessionHandler.cs index fc35793204e2..e34081682faa 100644 --- a/src/Foundation/NSUrlSessionHandler.cs +++ b/src/Foundation/NSUrlSessionHandler.cs @@ -134,6 +134,15 @@ static NSUrlSessionConfiguration CreateConfig () // Double.MaxValue does not work, so default to 24 hours config.TimeoutIntervalForRequest = 24 * 60 * 60; config.TimeoutIntervalForResource = 24 * 60 * 60; + + // Disable shared credential storage so credentials we pass with UseCredential in DidReceiveChallenge dont get saved in + // the SharedCredentialStorage so (native) NSUrlSession can't try to authenticate later requests by itself using old credentials + // incluiding redirects, and then our managed DidReceiveChallenge delegate may not get called at all. We already manage + // the credential flow in DidReceiveChallenge and the Credentials property. The switch is just a compat in case we + // someone needs to go back to the old behaviour. + var useSharedCredentialStorage = AppContext.TryGetSwitch ("Foundation.NSUrlSessionHandler.UseSharedCredentialStorage", out var useSharedStorage) && useSharedStorage; + if (!useSharedCredentialStorage) + config.URLCredentialStorage = null; return config; } @@ -1029,7 +1038,30 @@ void WillCacheResponseImpl (NSUrlSession session, NSUrlSessionDataTask dataTask, [Preserve (Conditional = true)] public override void WillPerformHttpRedirection (NSUrlSession session, NSUrlSessionTask task, NSHttpUrlResponse response, NSUrlRequest newRequest, Action completionHandler) { - completionHandler (sessionHandler.AllowAutoRedirect ? newRequest : null!); + if (!sessionHandler.AllowAutoRedirect) { + completionHandler (null!); + return; + } + + var inflight = GetInflightData (task); + + if (inflight is null) { + completionHandler (null!); + return; + } + + inflight.HasRedirected = true; + + if (newRequest.Url?.AbsoluteString is string redirectUrl) { + inflight.CurrentRequestUrl = redirectUrl; + + if (Uri.TryCreate (redirectUrl, UriKind.Absolute, out var redirectUri)) + inflight.HasCrossOriginRedirect |= IsCrossOriginRedirect (inflight.RequestUrl, redirectUri); + else + inflight.HasCrossOriginRedirect = true; + } + + completionHandler (newRequest); } [Preserve (Conditional = true)] @@ -1127,6 +1159,22 @@ void DidReceiveChallengeImpl (NSUrlSession session, NSUrlSessionTask task, NSUrl } } + // Detect redirect from the task as a fallback in case + // WillPerformHttpRedirection has not updated infligth state yet + if (!inflight.HasRedirected) { + var originalUrl = task.OriginalRequest?.Url?.AbsoluteString; + var currentUrl = task.CurrentRequest?.Url?.AbsoluteString; + if (originalUrl is not null && currentUrl is not null + && !string.Equals (originalUrl, currentUrl, StringComparison.Ordinal)) { + inflight.HasRedirected = true; + inflight.CurrentRequestUrl = currentUrl; + if (Uri.TryCreate (currentUrl, UriKind.Absolute, out var redirectUri)) + inflight.HasCrossOriginRedirect |= IsCrossOriginRedirect (inflight.RequestUrl, redirectUri); + else + inflight.HasCrossOriginRedirect = true; + } + } + if (sessionHandler.Credentials is not null && TryGetAuthenticationType (challenge.ProtectionSpace, out var authType)) { NetworkCredential? credentialsToUse = null; if (authType != RejectProtectionSpaceAuthType) { @@ -1147,8 +1195,9 @@ void DidReceiveChallengeImpl (NSUrlSession session, NSUrlSessionTask task, NSUrl var nsurlRespose = challenge.FailureResponse as NSHttpUrlResponse; var responseIsUnauthorized = (nsurlRespose is null) ? false : nsurlRespose.StatusCode == (int) HttpStatusCode.Unauthorized && challenge.PreviousFailureCount > 0; if (!responseIsUnauthorized) { - var uri = inflight.Request.RequestUri!; - credentialsToUse = sessionHandler.Credentials.GetCredential (uri, authType); + var uri = GetCredentialLookupUri (task, inflight); + if (ShouldLookupCredentials (sessionHandler.Credentials, inflight)) + credentialsToUse = sessionHandler.Credentials.GetCredential (uri, authType); } } @@ -1165,6 +1214,45 @@ void DidReceiveChallengeImpl (NSUrlSession session, NSUrlSessionTask task, NSUrl } } + static Uri GetCredentialLookupUri (NSUrlSessionTask task, InflightData inflight) + { + var currentRequestUrl = task.CurrentRequest?.Url?.AbsoluteString; + if (currentRequestUrl is not null && Uri.TryCreate (currentRequestUrl, UriKind.Absolute, out var currentRequestUri)) + return currentRequestUri; + + if (Uri.TryCreate (inflight.CurrentRequestUrl, UriKind.Absolute, out var inflightCurrentRequestUri)) + return inflightCurrentRequestUri; + + return inflight.Request.RequestUri!; + } + + static bool ShouldLookupCredentials (ICredentials credentials, InflightData inflight) + { + if (credentials is CredentialCache) + return true; + + if (!inflight.HasRedirected) + return true; + + // We are now matching .NET handlers (SocketsHttpHandler and WinHttpHandler) redirect behavior by dropping non CredentialCache credentials after a redirect + // Ref: + // https://github.com/dotnet/runtime/blob/eb5503a1f0dc40ee7b73eb79a039eb143ee25038/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs#L541-L547 + // https://github.com/dotnet/runtime/blob/eb5503a1f0dc40ee7b73eb79a039eb143ee25038/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RedirectHandler.cs#L52-L87 + // Provide a way for customers to opt into the old behavior for same origin redirects. + var allowSameOriginRedirectCredentials = AppContext.TryGetSwitch ("Foundation.NSUrlSessionHandler.AllowSameOriginRedirectCredentials", out var allowRedirectCred) && allowRedirectCred; + return allowSameOriginRedirectCredentials && !inflight.HasCrossOriginRedirect; + } + + static bool IsCrossOriginRedirect (string originalRequestUrl, Uri currentRequestUri) + { + if (!Uri.TryCreate (originalRequestUrl, UriKind.Absolute, out var originalRequestUri)) + return true; + + return !string.Equals (originalRequestUri.Scheme, currentRequestUri.Scheme, StringComparison.OrdinalIgnoreCase) + || !string.Equals (originalRequestUri.IdnHost, currentRequestUri.IdnHost, StringComparison.OrdinalIgnoreCase) + || originalRequestUri.Port != currentRequestUri.Port; + } + static readonly string RejectProtectionSpaceAuthType = "reject"; static bool TryGetAuthenticationType (NSUrlProtectionSpace protectionSpace, [NotNullWhen (true)] out string? authenticationType) @@ -1196,6 +1284,9 @@ static bool TryGetAuthenticationType (NSUrlProtectionSpace protectionSpace, [Not class InflightData { public readonly object Lock = new object (); public string RequestUrl { get; set; } + public string CurrentRequestUrl { get; set; } + public bool HasRedirected { get; set; } + public bool HasCrossOriginRedirect { get; set; } public TaskCompletionSource CompletionSource { get; } = new TaskCompletionSource (TaskCreationOptions.RunContinuationsAsynchronously); public CancellationToken CancellationToken { get; set; } @@ -1214,6 +1305,7 @@ class InflightData { public InflightData (string requestUrl, CancellationToken cancellationToken, HttpRequestMessage request) { RequestUrl = requestUrl; + CurrentRequestUrl = requestUrl; CancellationToken = cancellationToken; Request = request; } diff --git a/tests/monotouch-test/System.Net.Http/MessageHandlers.cs b/tests/monotouch-test/System.Net.Http/MessageHandlers.cs index 2accb1397400..b159fab8c8c5 100644 --- a/tests/monotouch-test/System.Net.Http/MessageHandlers.cs +++ b/tests/monotouch-test/System.Net.Http/MessageHandlers.cs @@ -3,6 +3,7 @@ // using System.Collections; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using System.Net; @@ -25,6 +26,9 @@ namespace MonoTests.System.Net.Http { [TestFixture] [Preserve (AllMembers = true)] public class MessageHandlerTest { + const string AllowSameOriginRedirectCredentialsSwitch = "Foundation.NSUrlSessionHandler.AllowSameOriginRedirectCredentials"; + const string UseSharedCredentialStorageSwitch = "Foundation.NSUrlSessionHandler.UseSharedCredentialStorage"; + public MessageHandlerTest () { // Https seems broken on our macOS 10.9 bot, so skip this test. @@ -468,6 +472,138 @@ public void RedirectionWithAuthorizationHeaders (Type handlerType) } } + [TestCase (true, false, HttpStatusCode.Unauthorized, false, TestName = "NSUrlSessionHandlerOriginCredentialCacheNotSentToCrossOriginRedirectTarget")] + [TestCase (true, true, HttpStatusCode.OK, true, TestName = "NSUrlSessionHandlerTargetCredentialCacheSentToCrossOriginRedirectTarget")] + [TestCase (false, false, HttpStatusCode.Unauthorized, false, TestName = "NSUrlSessionHandlerNetworkCredentialNotSentToCrossOriginRedirectTarget")] + public void NSUrlSessionHandlerCredentialsCrossOriginRedirectTarget (bool useCredentialCache, bool cacheRedirectTarget, HttpStatusCode expectedStatusCode, bool expectAuthorizationHeader) + { + if (!HttpListener.IsSupported) { + Assert.Inconclusive ("HttpListener is not supported"); + } + + using var server = new RedirectBasicAuthServer (crossOrigin: true); + using var handler = new NSUrlSessionHandler (); + var username = "origin-user"; + var password = "origin-password"; + + if (useCredentialCache) { + var cache = new CredentialCache (); + var credentialUri = cacheRedirectTarget ? new Uri (server.TargetUri, "protected") : server.OriginUri; + cache.Add (credentialUri, "basic", new NetworkCredential (username, password)); + handler.Credentials = cache; + } else { + handler.Credentials = new NetworkCredential (username, password); + } + + HttpStatusCode statusCode = HttpStatusCode.NotFound; + var done = TestRuntime.TryRunAsync (TimeSpan.FromSeconds (30), async () => { + using var client = new HttpClient (handler); + using var response = await client.GetAsync (new Uri (server.OriginUri, "start")); + statusCode = response.StatusCode; + }, out var ex); + + Assert.That (done, Is.True, "Request timed out."); + Assert.That (ex, Is.Null, "Exception"); + Assert.That (statusCode, Is.EqualTo (expectedStatusCode), "StatusCode"); + Assert.That (server.TargetRequestCount, Is.GreaterThanOrEqualTo (1), "Target request count"); + Assert.That (server.TargetAuthorizationHeaders.Length > 0, Is.EqualTo (expectAuthorizationHeader), "Authorization header presence."); + } + + [TestCase (false, HttpStatusCode.Unauthorized, TestName = "NSUrlSessionHandlerNetworkCredentialNotSentAfterSameOriginRedirectByDefault")] + [TestCase (true, HttpStatusCode.OK, TestName = "NSUrlSessionHandlerNetworkCredentialSentAfterSameOriginRedirectWithAppContextSwitch")] + public void NSUrlSessionHandlerNetworkCredentialSameOriginRedirectCredentials (bool allowSameOriginRedirectCredentials, HttpStatusCode expectedStatusCode) + { + if (!HttpListener.IsSupported) { + Assert.Inconclusive ("HttpListener is not supported"); + } + + AppContext.TryGetSwitch (AllowSameOriginRedirectCredentialsSwitch, out var originalValue); + try { + AppContext.SetSwitch (AllowSameOriginRedirectCredentialsSwitch, allowSameOriginRedirectCredentials); + + using var server = new RedirectBasicAuthServer (crossOrigin: false); + using var handler = new NSUrlSessionHandler { + Credentials = new NetworkCredential ("origin-user", "origin-password"), + }; + + HttpStatusCode statusCode = HttpStatusCode.NotFound; + var done = TestRuntime.TryRunAsync (TimeSpan.FromSeconds (30), async () => { + using var client = new HttpClient (handler); + using var response = await client.GetAsync (new Uri (server.OriginUri, "start")); + statusCode = response.StatusCode; + }, out var ex); + + Assert.That (done, Is.True, "Request timed out."); + Assert.That (ex, Is.Null, "Exception"); + Assert.That (statusCode, Is.EqualTo (expectedStatusCode), "StatusCode"); + Assert.That (server.TargetRequestCount, Is.GreaterThanOrEqualTo (1), "Target request count"); + Assert.That (server.TargetAuthorizationHeaders.Length > 0, Is.EqualTo (allowSameOriginRedirectCredentials), "Authorization header presence."); + } finally { + AppContext.SetSwitch (AllowSameOriginRedirectCredentialsSwitch, originalValue); + } + } + + [TestCase (false, HttpStatusCode.Unauthorized, false, TestName = "NSUrlSessionHandlerNetworkCredentialNotSentToCrossOriginRedirectWithDefaultCredentialStorage")] + [TestCase (true, HttpStatusCode.OK, true, TestName = "NSUrlSessionHandlerNetworkCredentialSentToCrossOriginRedirectWithSharedCredentialStorage")] + public void NSUrlSessionHandlerUseSharedCredentialStorage (bool useSharedCredentialStorage, HttpStatusCode expectedStatusCode, bool expectSecondRequestAuthorizationHeader) + { + if (!HttpListener.IsSupported) { + Assert.Inconclusive ("HttpListener is not supported"); + } + + AppContext.TryGetSwitch (UseSharedCredentialStorageSwitch, out var originalValue); + try { + AppContext.SetSwitch (UseSharedCredentialStorageSwitch, useSharedCredentialStorage); + + using var server = new RedirectBasicAuthServer (crossOrigin: true); + + // First, prime the shared credential storage by making a successful auth request + // using a CredentialCache with the target URI. When shared storage is enabled, + // NSUrlSession stores the credential with ForSession persistence in SharedCredentialStorage, + // making it available to subsequent handlers targeting the same host:port. + var cache = new CredentialCache (); + cache.Add (new Uri (server.TargetUri, "protected"), "basic", new NetworkCredential ("origin-user", "origin-password")); + + using (var primingHandler = new NSUrlSessionHandler { Credentials = cache }) { + var primingDone = TestRuntime.TryRunAsync (TimeSpan.FromSeconds (30), async () => { + using var client = new HttpClient (primingHandler); + using var response = await client.GetAsync (new Uri (server.OriginUri, "start")); + }, out var primingEx); + + Assert.That (primingDone, Is.True, "Priming request timed out."); + Assert.That (primingEx, Is.Null, "Priming exception"); + } + + // Record how many auth headers the server received from the priming request + var authHeadersAfterPriming = server.TargetAuthorizationHeaders.Length; + + // Now make a second request with a NetworkCredential (not CredentialCache) to the same server. + // With shared storage enabled, NSUrlSession finds cached credentials in SharedCredentialStorage + // and pre-emptively authenticates the redirect target without calling DidReceiveChallenge. + // With shared storage disabled (default), no stored credentials exist, and the + // DidReceiveChallenge delegate blocks the NetworkCredential after the cross-origin redirect. + using var handler = new NSUrlSessionHandler { + Credentials = new NetworkCredential ("origin-user", "origin-password"), + }; + + HttpStatusCode statusCode = HttpStatusCode.NotFound; + var done = TestRuntime.TryRunAsync (TimeSpan.FromSeconds (30), async () => { + using var client = new HttpClient (handler); + using var response = await client.GetAsync (new Uri (server.OriginUri, "start")); + statusCode = response.StatusCode; + }, out var ex); + + Assert.That (done, Is.True, "Request timed out."); + Assert.That (ex, Is.Null, "Exception"); + Assert.That (statusCode, Is.EqualTo (expectedStatusCode), "StatusCode"); + + var authHeadersFromSecondRequest = server.TargetAuthorizationHeaders.Length - authHeadersAfterPriming; + Assert.That (authHeadersFromSecondRequest > 0, Is.EqualTo (expectSecondRequestAuthorizationHeader), "Authorization header on second request."); + } finally { + AppContext.SetSwitch (UseSharedCredentialStorageSwitch, originalValue); + } + } + [TestCase (typeof (SocketsHttpHandler))] [TestCase (typeof (NSUrlSessionHandler))] public void RejectSslCertificatesServicePointManager (Type handlerType) @@ -867,6 +1003,154 @@ static NWListener CreateNWTlsListener (bool requireClientCert) return (cert.Export (X509ContentType.Pfx, password), password); } + sealed class RedirectBasicAuthServer : IDisposable { + readonly bool crossOrigin; + readonly HttpListener originListener; + readonly HttpListener? targetListener; + readonly Task originTask; + readonly Task? targetTask; + readonly object targetAuthorizationHeadersLock = new object (); + readonly List targetAuthorizationHeaders = new List (); + readonly string expectedBasicAuth; + int targetRequestCount; + + public Uri OriginUri { get; } + public Uri TargetUri { get; } + public int TargetRequestCount => Volatile.Read (ref targetRequestCount); + + public string [] TargetAuthorizationHeaders { + get { + lock (targetAuthorizationHeadersLock) + return targetAuthorizationHeaders.ToArray (); + } + } + + public RedirectBasicAuthServer (bool crossOrigin, string username = "origin-user", string password = "origin-password") + { + this.crossOrigin = crossOrigin; + expectedBasicAuth = "Basic " + Convert.ToBase64String (global::System.Text.Encoding.UTF8.GetBytes ($"{username}:{password}")); + originListener = CreateStartedHttpListener (out var originUri); + OriginUri = originUri; + + if (crossOrigin) { + targetListener = CreateStartedHttpListener (out var targetUri); + TargetUri = targetUri; + } else { + TargetUri = OriginUri; + } + + originTask = Task.Run (RunOrigin); + if (targetListener is not null) + targetTask = Task.Run (RunTarget); + } + + async Task RunOrigin () + { + while (true) { + var context = await GetContextAsync (originListener); + if (context is null) + return; + + if (!crossOrigin && string.Equals (context.Request.Url?.AbsolutePath, "/protected", StringComparison.Ordinal)) { + RespondToProtectedResource (context); + } else { + RespondWithRedirect (context); + } + } + } + + async Task RunTarget () + { + if (targetListener is null) + return; + + while (true) { + var context = await GetContextAsync (targetListener); + if (context is null) + return; + + RespondToProtectedResource (context); + } + } + + void RespondWithRedirect (HttpListenerContext context) + { + var response = context.Response; + response.StatusCode = (int) HttpStatusCode.Redirect; + response.RedirectLocation = new Uri (TargetUri, "protected").AbsoluteUri; + response.Close (); + } + + void RespondToProtectedResource (HttpListenerContext context) + { + Interlocked.Increment (ref targetRequestCount); + + var authorization = context.Request.Headers ["Authorization"]; + if (!string.IsNullOrEmpty (authorization)) { + lock (targetAuthorizationHeadersLock) + targetAuthorizationHeaders.Add (authorization); + } + + var response = context.Response; + if (string.Equals (authorization, expectedBasicAuth, StringComparison.Ordinal)) { + response.StatusCode = (int) HttpStatusCode.OK; + } else { + response.StatusCode = (int) HttpStatusCode.Unauthorized; + response.AddHeader ("WWW-Authenticate", "Basic realm=\"redirect-target\""); + } + response.Close (); + } + + static async Task GetContextAsync (HttpListener listener) + { + try { + return await listener.GetContextAsync (); + } catch (HttpListenerException) { + return null; + } catch (ObjectDisposedException) { + return null; + } catch (InvalidOperationException) { + return null; + } + } + + static HttpListener CreateStartedHttpListener (out Uri uri) + { + const int MinPort = 49215; + const int MaxPort = 65535; + + for (var port = MinPort; port < MaxPort; port++) { + var listener = new HttpListener (); + var url = $"http://127.0.0.1:{port}/"; + listener.Prefixes.Add (url); + try { + listener.Start (); + uri = new Uri (url); + return listener; + } catch { + listener.Close (); + } + } + + throw new InvalidOperationException ("Could not start a local HTTP listener."); + } + + public void Dispose () + { + originListener.Close (); + targetListener?.Close (); + + try { + if (targetTask is null) + Task.WaitAll (new [] { originTask }, TimeSpan.FromSeconds (1)); + else + Task.WaitAll (new [] { originTask, targetTask }, TimeSpan.FromSeconds (1)); + } catch { + // Listener disposal wakes the request loops. + } + } + } + [Test] public void AssertDefaultValuesNSUrlSessionHandler () { From 7a656b332e82e218fe8e8c1c0a327d6250024d22 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 27 May 2026 08:33:03 +0200 Subject: [PATCH 16/79] [bgen] Remove support for ZeroCopyStrings (#25515) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the dead zero-copy string marshaling code from the binding generator. This feature was never fully working, because it never really worked well (it ran into a number of bugs in other places, causing crashes because APIs would retain or copy NSStrings when they shouldn't and this optimization would run head-first into those). Note that this option never did anything in .NET, it was always forcefully disabled if someone tried to enable it. ## Changes - Remove `ZeroCopyStrings` field, `type_wants_zero_copy`, `ZeroCopyStringMarshal`, `CollectFastStringMarshalParameters`, and all zero-copy code paths from the generator - Simplify `GenerateMarshalString`/`GenerateDisposeString` (always use the copy path) - Keep `ZeroCopyStringsAttribute` and `DisableZeroCopyAttribute` as no-op stubs wrapped in `#if !XAMCORE_5_0` with `[Obsolete]` for source compatibility - Guard `--use-zero-copy` CLI option with `#if !XAMCORE_5_0` (emits warning BI1027) - Mark `ObjCBindings.Property.DisableZeroCopy` enum value as `[Obsolete]` - Remove `[DisableZeroCopy]` usages from binding sources (glkit, corebluetooth) - Update documentation to mark both attributes as obsolete 🤖 Pull request created by Copilot --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/website/binding_types_reference_guide.md | 70 ++------------ docs/website/generator-errors.md | 2 +- src/ObjCBindings/ExportTag.cs | 15 +-- src/Resources.Designer.cs | 2 +- src/Resources.resx | 2 +- src/bgen/Attributes.cs | 40 ++------ src/bgen/BindingTouch.cs | 5 +- src/bgen/Generator.cs | 91 ++----------------- src/bgen/Models/BindingTouchConfig.cs | 1 - src/bgen/Models/MarshalInfo.cs | 7 -- src/corebluetooth.cs | 1 - src/glkit.cs | 2 - .../Validators/FieldValidator.cs | 2 + .../DataModel/Property.Generator.cs | 4 +- .../AttributesNames.cs | 3 +- .../Validators/ArrayValidatorTests.cs | 1 + .../Validators/FieldValidatorTests.cs | 1 + .../PropertyOrFieldValidatorTests.cs | 1 + 18 files changed, 44 insertions(+), 206 deletions(-) diff --git a/docs/website/binding_types_reference_guide.md b/docs/website/binding_types_reference_guide.md index 78b90f26cbca..ace56aab5bc3 100644 --- a/docs/website/binding_types_reference_guide.md +++ b/docs/website/binding_types_reference_guide.md @@ -1306,28 +1306,9 @@ This should map to Objective-C/clang use of `__attribute__((objc_designated_init ### DisableZeroCopyAttribute -This attribute is applied to string parameters or string properties and -instructs the code generator to not use the zero-copy string marshaling for -this parameter, and instead create a new NSString instance from the C# string. -This attribute is only required on strings if you instruct the generator to use -zero-copy string marshaling using either the `--zero-copy` command -line option or setting the assembly-level attribute `ZeroCopyStringsAttribute`. - -This is necessary in cases where the property is declared in Objective-C to -be a `retain` or `assign` property instead of a `copy` property. These typically -happen in third-party libraries that have been wrongly "optimized" by -developers. In general, `retain` or `assign` `NSString` properties are incorrect -since `NSMutableString` or user-derived classes of `NSString` might alter the -contents of the strings without the knowledge of the library code, subtly -breaking the application. Typically this happens due to premature -optimization. - -The following shows two such properties in Objective-C: - -```csharp -@property(nonatomic,retain) NSString *name; -@property(nonatomic,assign) NSString *name2; -``` +> **Note:** This attribute is obsolete and has no effect. Zero-copy string +> marshaling is no longer supported. The attribute is preserved for source +> compatibility but can be safely removed from binding definitions. @@ -2435,47 +2416,10 @@ This corresponds to `clang` [`__attribute__((objc_requires_super))`](https://cla ### ZeroCopyStringsAttribute -Only available in Xamarin.iOS 5.4 and newer. - -This attribute instructs the generator that the binding for this specific -library (if applied with `[assembly:]`) or type should use the fast -zero-copy string marshaling. This attribute is equivalent to passing the -command line option `--zero-copy` to the generator. - -When using zero-copy for strings, the generator effectively uses the same C# -string as the string that Objective-C consumes without incurring the creation of -a new `NSString` object and avoiding copying the data from the C# strings to the -Objective-C string. The only drawback of using Zero Copy strings is that you -must ensure that any string property that you wrap that happens to be flagged as -`retain` or `copy` has the `[DisableZeroCopy]` attribute set. This is -require because the handle for zero-copy strings is allocated on the stack and -is invalid upon the function return. - -Example: - -```csharp -[ZeroCopyStrings] -[BaseType (typeof (NSObject))] -interface MyBinding { - [Export ("name")] - string Name { get; set; } - - [Export ("domain"), NullAllowed] - string Domain { get; set; } - - [DisablZeroCopy] - [Export ("someRetainedNSString")] - string RetainedProperty { get; set; } -} - -``` - -You can also apply the attribute at the assembly level, and it will apply to -all the types of the assembly: - -```csharp -[assembly:ZeroCopyStrings] -``` +> **Note:** This attribute is obsolete and has no effect. Zero-copy string +> marshaling is no longer supported. The `--use-zero-copy` command line option +> is also no longer supported. The attribute is preserved for source +> compatibility but can be safely removed from binding definitions. ## Strongly-typed dictionaries diff --git a/docs/website/generator-errors.md b/docs/website/generator-errors.md index 1d135ecb476f..365a8964309d 100644 --- a/docs/website/generator-errors.md +++ b/docs/website/generator-errors.md @@ -128,7 +128,7 @@ Please go to [[FieldAttribute]](https://developer.xamarin.com/guides/cross-platf ### BI1026: `*`: Enums attributed with [\*] must have an underlying type of `long` or `ulong` -### BI1027: Support for ZeroCopy strings is not implemented. Strings will be marshalled as NSStrings. +### BI1027: Support for ZeroCopy strings is not implemented. The --use-zero-copy option is not supported and will be ignored. ### BI1028: Internal sanity check failed, please file a bug report (https://github.com/dotnet/macios/issues/new) with a test case. diff --git a/src/ObjCBindings/ExportTag.cs b/src/ObjCBindings/ExportTag.cs index 7172cc573a55..ca3d9b7d0b13 100644 --- a/src/ObjCBindings/ExportTag.cs +++ b/src/ObjCBindings/ExportTag.cs @@ -1,3 +1,4 @@ +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; #nullable enable @@ -169,18 +170,10 @@ public enum Property : Int64 { CustomMarshalDirective = 1 << 5, /// - /// Apply to strings parameters that are merely retained or assigned, - /// not copied this is an exception as it is advised in the coding - /// standard for Objective-C to avoid this, but a few properties do use - /// this. Use this falg for properties flagged with `retain' or - /// `assign', which look like this: - /// - /// @property (retain) NSString foo; - /// @property (assign) NSString assigned; - /// - /// This forced the generator to create an NSString before calling the - /// API instead of using the fast string marshalling code. + /// This flag is obsolete and has no effect. Zero-copy string marshaling is no longer supported. /// + [EditorBrowsable (EditorBrowsableState.Never)] + [Obsolete ("Zero-copy string marshaling is no longer supported. This flag has no effect.")] DisableZeroCopy = 1 << 6, /// diff --git a/src/Resources.Designer.cs b/src/Resources.Designer.cs index 5cd19312d3ee..db627089d6d1 100644 --- a/src/Resources.Designer.cs +++ b/src/Resources.Designer.cs @@ -440,7 +440,7 @@ internal static string BI1026 { } /// - /// Looks up a localized string similar to Support for ZeroCopy strings is not implemented. Strings will be marshalled as NSStrings.. + /// Looks up a localized string similar to The --use-zero-copy option is not supported and will be ignored.. /// internal static string BI1027 { get { diff --git a/src/Resources.resx b/src/Resources.resx index acd9d535c624..3e6aace23529 100644 --- a/src/Resources.resx +++ b/src/Resources.resx @@ -294,7 +294,7 @@ - Support for ZeroCopy strings is not implemented. Strings will be marshalled as NSStrings. + The --use-zero-copy option is not supported and will be ignored. diff --git a/src/bgen/Attributes.cs b/src/bgen/Attributes.cs index a55108690d3c..8e47763419eb 100644 --- a/src/bgen/Attributes.cs +++ b/src/bgen/Attributes.cs @@ -517,20 +517,14 @@ public class NoDefaultValueAttribute : Attribute { public class IgnoredInDelegateAttribute : Attribute { } -// Apply to strings parameters that are merely retained or assigned, -// not copied this is an exception as it is advised in the coding -// standard for Objective-C to avoid this, but a few properties do use -// this. Use this attribtue for properties flagged with `retain' or -// `assign', which look like this: -// -// @property (retain) NSString foo; -// @property (assign) NSString assigned; -// -// This forced the generator to create an NSString before calling the -// API instead of using the fast string marshalling code. +#if !XAMCORE_5_0 +// This attribute is obsolete and has no effect. Zero-copy string marshaling is no longer supported. +[Obsolete ("Zero-copy string marshaling is no longer supported. This attribute has no effect.")] +[AttributeUsage (AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = true)] public class DisableZeroCopyAttribute : Attribute { public DisableZeroCopyAttribute () { } } +#endif // Apply this attribute to methods that need a custom binding method. // @@ -561,29 +555,13 @@ public class MarshalDirectiveAttribute : Attribute { public string? Library { get; set; } } -// -// By default, the generator will not do Zero Copying of strings, as most -// third party libraries do not follow Apple's design guidelines of making -// string properties and parameters copy parameters, instead many libraries -// "retain" as a broken optimization [1]. -// -// The consumer of the generator can force this by passing -// --use-zero-copy or setting the [assembly:ZeroCopyStrings] attribute. -// When these are set, the generator assumes the library perform -// copies over any NSStrings it keeps instead of retains/assigns and -// that any property that happens to be a retain/assign has the -// [DisableZeroCopyAttribute] attribute applied. -// -// [1] It is broken because consumer code can pass an NSMutableString, the -// library retains the value, but does not have a way of noticing changes -// that might happen to the mutable string behind its back. -// -// In the ZeroCopy case it is a problem because we pass handles to stack-allocated -// strings that stop existing after the invocation is over. -// +#if !XAMCORE_5_0 +// This attribute is obsolete and has no effect. Zero-copy string marshaling is no longer supported. +[Obsolete ("Zero-copy string marshaling is no longer supported. This attribute has no effect.")] [AttributeUsage (AttributeTargets.Assembly | AttributeTargets.Method | AttributeTargets.Interface, AllowMultiple = true)] public class ZeroCopyStringsAttribute : Attribute { } +#endif [AttributeUsage (AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)] public class SnippetAttribute : Attribute { diff --git a/src/bgen/BindingTouch.cs b/src/bgen/BindingTouch.cs index 2766217756fa..5afd87e41b01 100644 --- a/src/bgen/BindingTouch.cs +++ b/src/bgen/BindingTouch.cs @@ -163,7 +163,9 @@ public bool TryCreateOptionSet (BindingTouchConfig config, string [] args) { "p", "Sets private mode", v => config.IsPublicMode = false }, { "baselib=", "Sets the base library", v => config.Baselibdll = v }, { "attributelib=", "Sets the attribute library", v => config.Attributedll = v }, - { "use-zero-copy", v=> config.UseZeroCopy = true }, +#if !XAMCORE_5_0 + { "use-zero-copy", v=> ErrorHelper.Warning (1027) }, +#endif { "nostdlib", "Does not reference mscorlib.dll library", l => config.OmitStandardLibrary = true }, #if !XAMCORE_5_0 { "no-mono-path", "Launches compiler with empty MONO_PATH", l => { }, true }, @@ -364,7 +366,6 @@ bool TryGenerate (BindingTouchConfig config, Api api) try { var g = new Generator (this, api, config.IsPublicMode, config.IsExternal, config.IsDebug) { BaseDir = config.BindingFilesOutputDirectory ?? config.TemporaryFileDirectory!, - ZeroCopyStrings = config.UseZeroCopy, InlineSelectors = config.InlineSelectors ?? (CurrentPlatform != PlatformName.MacOSX), }; diff --git a/src/bgen/Generator.cs b/src/bgen/Generator.cs index cf581d75be9b..f5559c942d26 100644 --- a/src/bgen/Generator.cs +++ b/src/bgen/Generator.cs @@ -109,9 +109,6 @@ Nomenclator Nomenclator { string? is_direct_binding_value; // An expression that calculates the IsDirectBinding value. Might not be a constant expression. This will be added to every constructor for a type. bool? is_direct_binding; // If a constant value for IsDirectBinding is known, it's stored here. Will be null if no constant value is known. - // Whether to use ZeroCopy for strings, defaults to false - public bool ZeroCopyStrings; - public bool BindThirdPartyLibrary { get { return BindingTouch.BindThirdPartyLibrary; } } public bool InlineSelectors; public string BaseDir { get { return basedir; } set { basedir = value; } } @@ -124,11 +121,6 @@ Nomenclator Nomenclator { // bool type_needs_thread_checks; - // - // If set, the members of this type will get zero copy - // - internal bool type_wants_zero_copy; - // // Used by the public binding generator to populate the // class with types that do not exist @@ -843,16 +835,7 @@ public TrampolineInfo MakeTrampoline (Type t) if (mai.PlainString) return safe_name; else { - bool allow_null = null_allowed_override || AttributeManager.IsNullable (pi); - - if (mai.ZeroCopyStringMarshal) { - if (allow_null) - return String.Format ("{0} is null ? IntPtr.Zero : (IntPtr)(&_s{0})", pi.Name); - else - return String.Format ("(IntPtr)(&_s{0})", pi.Name); - } else { - return "ns" + pi.Name; - } + return "ns" + pi.Name; } } @@ -3097,13 +3080,8 @@ void GenerateInvoke (bool stret, bool supercall, MethodInfo mi, MemberInformatio if (mai.PlainString) ErrorHelper.Warning (1101); - if (mai.ZeroCopyStringMarshal) { - target_name = "(IntPtr)(&_s" + pi.Name + ")"; - handle = ""; - } else { - target_name = "ns" + pi.Name; - handle = ""; - } + target_name = "ns" + pi.Name; + handle = ""; } else target_name = pi.Name.GetSafeParamName (); break; @@ -3248,48 +3226,16 @@ bool IsOptimizable (MemberInfo method) // @probe_null: determines whether null is allowed, and // whether we need to generate code to handle this // - // @must_copy: determines whether to create a new NSString, necessary - // for NSString properties that are flagged with "retain" instead of "copy" - // - // @prefix: prefix to prepend on each line - // // @property: the name of the property // - public string GenerateMarshalString (bool probe_null, bool must_copy) - { - if (must_copy) { - return "var ns{0} = CFString.CreateNative ({1});\n"; - } - return - "ObjCRuntime.NSStringStruct _s{0}; Console.WriteLine (\"" + CurrentMethod + ": Marshalling: {{1}}\", {1}); \n" + - "_s{0}.ClassPtr = ObjCRuntime.NSStringStruct.ReferencePtr;\n" + - "_s{0}.Flags = 0x010007d1; // RefCount=1, Unicode, InlineContents = 0, DontFreeContents\n" + - "_s{0}.UnicodePtr = _p{0};\n" + - "_s{0}.Length = " + (probe_null ? "{1} is null ? 0 : {1}.Length;" : "{1}.Length;\n"); - } - - public string GenerateDisposeString (bool probe_null, bool must_copy) + public string GenerateMarshalString (bool probe_null) { - if (must_copy) { - return "CFString.ReleaseNative (ns{0});\n"; - } else - return "if (_s{0}.Flags != 0x010007d1) throw new Exception (\"String was retained, not copied\");"; + return "var ns{0} = CFString.CreateNative ({1});\n"; } - List? CollectFastStringMarshalParameters (MethodInfo mi) + public string GenerateDisposeString (bool probe_null) { - List? stringParameters = null; - - foreach (var pi in mi.GetParameters ()) { - var mai = new MarshalInfo (this, mi, pi); - - if (mai.ZeroCopyStringMarshal) { - if (stringParameters is null) - stringParameters = new List (); - stringParameters.Add (pi.Name.GetSafeParamName () ?? ""); - } - } - return stringParameters; + return "CFString.ReleaseNative (ns{0});\n"; } AvailabilityBaseAttribute? GetIntroduced (Type? type, string methodName) @@ -3371,8 +3317,8 @@ void GenerateTypeLowering (MethodInfo mi, bool null_allowed_override, out String if (mai.Type == TypeCache.System_String && !mai.PlainString) { bool probe_null = null_allowed_override || AttributeManager.IsNullable (pi); - convs.AppendFormat (GenerateMarshalString (probe_null, !mai.ZeroCopyStringMarshal), pi.Name, pi.Name.GetSafeParamName ()); - disposes.AppendFormat (GenerateDisposeString (probe_null, !mai.ZeroCopyStringMarshal), pi.Name); + convs.AppendFormat (GenerateMarshalString (probe_null), pi.Name, pi.Name.GetSafeParamName ()); + disposes.AppendFormat (GenerateDisposeString (probe_null), pi.Name); } else if (mai.Type.TryIsArray (out var etype)) { if (HasBindAsAttribute (pi)) { convs.AppendFormat ("using var nsb_{0} = {1}\n", pi.Name, GetToBindAsWrapper (mi, null, pi)); @@ -3615,9 +3561,6 @@ public void GenerateMethodBody (MemberInformation minfo, MethodInfo mi, string? GenerateArgumentChecks (mi, false, propInfo, out bool needsGCKeepAlives); - // Collect all strings that can be fast-marshalled - var stringParameters = CollectFastStringMarshalParameters (mi); - GenerateTypeLowering (mi, null_allowed_override, out var args, out var convs, out var disposes, out var by_ref_processing, out var by_ref_init, out var post_return, propInfo); if (minfo.is_protocol_member && minfo.is_static) { @@ -3629,12 +3572,6 @@ public void GenerateMethodBody (MemberInformation minfo, MethodInfo mi, string? if (by_ref_init.Length > 0) print (by_ref_init.ToString ()); - if (stringParameters is not null) { - print ("fixed (char * {0}){{", - stringParameters.Select (name => "_p" + name + " = " + name).Aggregate ((first, second) => first + ", " + second)); - indent++; - } - if (propInfo is not null && IsSetter (mi) && HasBindAsAttribute (propInfo)) { convs.AppendFormat ("using var nsb_{0} = {1}\n", propInfo.Name, GetToBindAsWrapper (mi, minfo, null)); } @@ -3834,10 +3771,6 @@ public void GenerateMethodBody (MemberInformation minfo, MethodInfo mi, string? WriteMarkDirtyIfDerived (sw, mi.DeclaringType!); if (post_return?.Length > 0) print (post_return.ToString ()); - if (stringParameters is not null) { - indent--; - print ("}"); - } indent--; } @@ -5769,12 +5702,6 @@ public void Generate (Type type) if (is_rgen_type) return; - if (ZeroCopyStrings) { - ErrorHelper.Warning (1027); - ZeroCopyStrings = false; - } - - type_wants_zero_copy = AttributeManager.HasAttribute (type) || ZeroCopyStrings; var tsa = AttributeManager.GetCustomAttribute (type); // if we're inside a special namespace then default is non-thread safe, otherwise default is thread safe if (NamespaceCache.UINamespaces.Contains (type.Namespace!)) { diff --git a/src/bgen/Models/BindingTouchConfig.cs b/src/bgen/Models/BindingTouchConfig.cs index 0aa0d50b614f..4cad9cbbba2f 100644 --- a/src/bgen/Models/BindingTouchConfig.cs +++ b/src/bgen/Models/BindingTouchConfig.cs @@ -5,7 +5,6 @@ public class BindingTouchConfig { public bool ShowHelp = false; - public bool UseZeroCopy = false; public string? BindingFilesOutputDirectory = null; public string? TemporaryFileDirectory = null; public string? HelperClassNamespace = null; diff --git a/src/bgen/Models/MarshalInfo.cs b/src/bgen/Models/MarshalInfo.cs index 27c4a52c6fea..091b1dc32ffd 100644 --- a/src/bgen/Models/MarshalInfo.cs +++ b/src/bgen/Models/MarshalInfo.cs @@ -13,10 +13,6 @@ public class MarshalInfo { public Type Type { get; } public bool IsOut { get; } - // This is set on a string parameter if the argument parameters are set to - // Copy. This means that we can do fast string passing. - public bool ZeroCopyStringMarshal { get; set; } - public bool IsAligned; // Used for parameters @@ -25,9 +21,6 @@ public MarshalInfo (Generator generator, MethodInfo mi, ParameterInfo pi) this.Generator = generator; PlainString = Generator.AttributeManager.HasAttribute (pi); Type = pi.ParameterType; - ZeroCopyStringMarshal = (Type == Generator.TypeCache.System_String) && PlainString == false && !Generator.AttributeManager.HasAttribute (pi) && generator.type_wants_zero_copy; - if (ZeroCopyStringMarshal && Generator.AttributeManager.HasAttribute (mi)) - ZeroCopyStringMarshal = false; IsOut = pi.IsOut; } diff --git a/src/corebluetooth.cs b/src/corebluetooth.cs index b5d8f22930bb..3b1fc5133c5c 100644 --- a/src/corebluetooth.cs +++ b/src/corebluetooth.cs @@ -791,7 +791,6 @@ interface CBPeripheral : NSCopying { /// To be added. /// To be added. [Export ("name", ArgumentSemantic.Retain)] - [DisableZeroCopy] [NullAllowed] string Name { get; } diff --git a/src/glkit.cs b/src/glkit.cs index 9d70fa9313a6..609394d61ab9 100644 --- a/src/glkit.cs +++ b/src/glkit.cs @@ -171,7 +171,6 @@ interface GLKBaseEffect : GLKNamedEffect { /// /// To be added. [Export ("label", ArgumentSemantic.Copy)] - [DisableZeroCopy] [NullAllowed] // default is null on iOS 5.1.1 string Label { get; set; } @@ -583,7 +582,6 @@ interface GLKSkyboxEffect : GLKNamedEffect { /// To be added. [NullAllowed] // by default this property is null [Export ("label", ArgumentSemantic.Copy)] - [DisableZeroCopy] string Label { get; set; } /// To be added. diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Validators/FieldValidator.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Validators/FieldValidator.cs index 6bc47067a431..3bffa00f9f39 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Validators/FieldValidator.cs +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Validators/FieldValidator.cs @@ -76,6 +76,7 @@ internal static bool FlagsAreValid (Property property, RootContext context, // there are a number of flags that have no effect in a field property, we are not raise a warning for each // of them since they are ignored by rgen. The user that has warnings as errors will have to deal with them. +#pragma warning disable CS0618 // DisableZeroCopy is obsolete var ignoredFlags = new [] { PropertyFlag.IsThreadStatic, PropertyFlag.MarshalNativeExceptions, @@ -93,6 +94,7 @@ internal static bool FlagsAreValid (Property property, RootContext context, PropertyFlag.Optional, PropertyFlag.CreateEvents, }; +#pragma warning restore CS0618 var builder = ImmutableArray.CreateBuilder (); foreach (var flag in ignoredFlags) { diff --git a/src/rgen/Microsoft.Macios.Generator/DataModel/Property.Generator.cs b/src/rgen/Microsoft.Macios.Generator/DataModel/Property.Generator.cs index 5c1dc20fae17..623bca12c849 100644 --- a/src/rgen/Microsoft.Macios.Generator/DataModel/Property.Generator.cs +++ b/src/rgen/Microsoft.Macios.Generator/DataModel/Property.Generator.cs @@ -99,10 +99,12 @@ public bool MarshalNativeExceptions public bool IsTransient => IsProperty && ExportPropertyData.Flags.HasFlag (ObjCBindings.Property.Transient); /// - /// True if the property was marked to DisableZeroCopy. + /// True if the property was marked to DisableZeroCopy. This flag is obsolete and has no effect. /// +#pragma warning disable CS0618 // Type or member is obsolete public bool DisableZeroCopy => IsProperty && ExportPropertyData.Flags.HasFlag (ObjCBindings.Property.DisableZeroCopy); +#pragma warning restore CS0618 /// /// True if the generator should not use a NSString for marshalling. diff --git a/src/rgen/Microsoft.Macios.Transformer/AttributesNames.cs b/src/rgen/Microsoft.Macios.Transformer/AttributesNames.cs index e09a98b9115f..5d35b03547f6 100644 --- a/src/rgen/Microsoft.Macios.Transformer/AttributesNames.cs +++ b/src/rgen/Microsoft.Macios.Transformer/AttributesNames.cs @@ -90,8 +90,7 @@ static class AttributesNames { public const string DisableDefaultCtorAttribute = "DisableDefaultCtorAttribute"; /// - /// This attribute is applied to string parameters or string properties and instructs the code generator to not - /// use the zero-copy string marshaling for this parameter, and instead create a new NSString instance from the C# string + /// This attribute is obsolete and has no effect. Zero-copy string marshaling is no longer supported. /// [BindingFlag (AttributeTargets.Parameter | AttributeTargets.Property)] public const string DisableZeroCopyAttribute = "DisableZeroCopyAttribute"; diff --git a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/Validators/ArrayValidatorTests.cs b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/Validators/ArrayValidatorTests.cs index 5d265504094a..74b3a943027b 100644 --- a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/Validators/ArrayValidatorTests.cs +++ b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/Validators/ArrayValidatorTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma warning disable APL0003 +#pragma warning disable CS0618 // DisableZeroCopy is obsolete using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; diff --git a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/Validators/FieldValidatorTests.cs b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/Validators/FieldValidatorTests.cs index 7d9c84e75903..9571b1eb590f 100644 --- a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/Validators/FieldValidatorTests.cs +++ b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/Validators/FieldValidatorTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma warning disable APL0003 +#pragma warning disable CS0618 // DisableZeroCopy is obsolete using Microsoft.Macios.Bindings.Analyzer.Validators; using Xunit; diff --git a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/Validators/PropertyOrFieldValidatorTests.cs b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/Validators/PropertyOrFieldValidatorTests.cs index 738eded7d1a6..ca019c36bc7e 100644 --- a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/Validators/PropertyOrFieldValidatorTests.cs +++ b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/Validators/PropertyOrFieldValidatorTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma warning disable APL0003 +#pragma warning disable CS0618 // DisableZeroCopy is obsolete using Microsoft.Macios.Bindings.Analyzer.Validators; using Xunit; From fc4f229bf04c8bf44065792994b7532031b08236 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 07:02:49 +0000 Subject: [PATCH 17/79] [main] Update dependencies from dotnet/dotnet (#25533) This pull request updates the following dependencies ## From https://github.com/dotnet/dotnet - **Subscription**: [da09b56a-0fb1-439a-b894-def14d2ec0a4](https://maestro.dot.net/subscriptions?search=da09b56a-0fb1-439a-b894-def14d2ec0a4) - **Build**: [20260526.20](https://dev.azure.com/dnceng/internal/_build/results?buildId=2984799) ([315942](https://maestro.dot.net/channel/10307/github:dotnet:dotnet/build/315942)) - **Date Produced**: May 27, 2026 1:23:24 AM UTC - **Commit**: [c9b6c9515ff95716e797906a3eff7179baa272b2](https://github.com/dotnet/dotnet/commit/c9b6c9515ff95716e797906a3eff7179baa272b2) - **Branch**: [release/10.0.4xx](https://github.com/dotnet/dotnet/tree/release/10.0.4xx) - **Dependency Updates**: - From [10.0.0-beta.26270.102 to 10.0.0-beta.26276.120][1] - Microsoft.DotNet.Arcade.Sdk - Microsoft.DotNet.Build.Tasks.Feed - Microsoft.DotNet.SharedFramework.Sdk - From [10.0.301-servicing.26270.102 to 10.0.400-preview.0.26276.120][1] - Microsoft.NET.Sdk - From [10.0.301 to 10.0.400-preview.26276.120][1] - Microsoft.TemplateEngine.Authoring.Tasks [1]: https://github.com/dotnet/dotnet/compare/2970f74d53...c9b6c9515f --- NuGet.config | 1 - eng/Version.Details.props | 10 +++++----- eng/Version.Details.xml | 20 ++++++++++---------- global.json | 6 +++--- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/NuGet.config b/NuGet.config index 31b55049d0f8..1d436b8dfad1 100644 --- a/NuGet.config +++ b/NuGet.config @@ -10,7 +10,6 @@ - diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 2580fca25dba..f05bc8e722c7 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -6,16 +6,16 @@ This file should be imported by eng/Versions.props - 10.0.0-beta.26270.102 - 10.0.0-beta.26270.102 + 10.0.0-beta.26276.120 + 10.0.0-beta.26276.120 0.11.5-alpha.26070.104 - 10.0.0-beta.26270.102 + 10.0.0-beta.26276.120 10.0.3-servicing.26070.104 10.0.3 10.0.3 - 10.0.301-servicing.26270.102 + 10.0.400-preview.0.26276.120 10.0.3 - 10.0.301 + 10.0.400-preview.26276.120 26.0.11017 18.5.9227 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 21e29be673c0..8532af929144 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,8 +1,8 @@ - + https://github.com/dotnet/dotnet - 2970f74d535c31d8ad587c8127796c72f29bfcee + c9b6c9515ff95716e797906a3eff7179baa272b2 https://github.com/dotnet/dotnet @@ -95,25 +95,25 @@ - + https://github.com/dotnet/dotnet - 2970f74d535c31d8ad587c8127796c72f29bfcee + c9b6c9515ff95716e797906a3eff7179baa272b2 - + https://github.com/dotnet/dotnet - 2970f74d535c31d8ad587c8127796c72f29bfcee + c9b6c9515ff95716e797906a3eff7179baa272b2 - + https://github.com/dotnet/dotnet - 2970f74d535c31d8ad587c8127796c72f29bfcee + c9b6c9515ff95716e797906a3eff7179baa272b2 https://github.com/dotnet/xharness 51ca379106cfd749a498cb0822210ef1aa926e41 - + https://github.com/dotnet/dotnet - 2970f74d535c31d8ad587c8127796c72f29bfcee + c9b6c9515ff95716e797906a3eff7179baa272b2 diff --git a/global.json b/global.json index 40583aca3c5a..e96f69ff8e58 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "10.0.301-servicing.26270.102", + "version": "10.0.400-preview.0.26276.120", "paths": [ "builds/downloads/dotnet", "$host$" @@ -8,9 +8,9 @@ "errorMessage": "The .NET SDK could not be found, please run 'make dotnet -C builds'." }, "tools": { - "dotnet": "10.0.301-servicing.26270.102" + "dotnet": "10.0.400-preview.0.26276.120" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26270.102" + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26276.120" } } From b8e66aaf8c0670c81f7535e4477569500c3d7918 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 27 May 2026 09:16:18 +0200 Subject: [PATCH 18/79] [tests] Improve new BasicAuthWorksWhenBearerIsAdvertisedFirst test. (#25503) - Remove unused 'using System.Net.Http.Headers' directive. - Track request counts server-side and assert that at least one unauthenticated request was received before the authenticated retry, ensuring the test validates the actual challenge/fallback flow. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../System.Net.Http/NSUrlSessionHandlerTest.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/monotouch-test/System.Net.Http/NSUrlSessionHandlerTest.cs b/tests/monotouch-test/System.Net.Http/NSUrlSessionHandlerTest.cs index f8bf44a1035c..8b1fe66f384a 100644 --- a/tests/monotouch-test/System.Net.Http/NSUrlSessionHandlerTest.cs +++ b/tests/monotouch-test/System.Net.Http/NSUrlSessionHandlerTest.cs @@ -5,7 +5,6 @@ using System; using System.Net; using System.Net.Http; -using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -234,6 +233,9 @@ public void BasicAuthWorksWhenBearerIsAdvertisedFirst () var expectedBasicValue = Convert.ToBase64String (Encoding.UTF8.GetBytes ($"{username}:{password}")); var serverReady = new SemaphoreSlim (0, 1); + int requestIndex = 0; + int firstUnauthenticatedIndex = -1; + int firstAuthenticatedIndex = -1; var httpListener = StartListenerOnAvailablePort (out var listeningPort); if (httpListener is null) { @@ -250,14 +252,17 @@ public void BasicAuthWorksWhenBearerIsAdvertisedFirst () var response = context.Response; var authHeader = request.Headers ["Authorization"]; + var currentIndex = Interlocked.Increment (ref requestIndex); if (authHeader is not null && authHeader == $"Basic {expectedBasicValue}") { // Authenticated - return success + Interlocked.CompareExchange (ref firstAuthenticatedIndex, currentIndex, -1); response.StatusCode = 200; var body = Encoding.UTF8.GetBytes ("authenticated"); response.ContentLength64 = body.Length; response.OutputStream.Write (body, 0, body.Length); } else { // Return 401 with Bearer first, then Basic + Interlocked.CompareExchange (ref firstUnauthenticatedIndex, currentIndex, -1); response.StatusCode = 401; response.AddHeader ("WWW-Authenticate", "Bearer realm=\"test\", charset=\"UTF-8\""); response.AppendHeader ("WWW-Authenticate", "Basic realm=\"test\", charset=\"UTF-8\""); @@ -291,6 +296,9 @@ public void BasicAuthWorksWhenBearerIsAdvertisedFirst () Assert.That (ex, Is.Null, $"Exception: {ex}"); Assert.That (statusCode, Is.EqualTo (HttpStatusCode.OK), "Expected 200 OK after Basic auth negotiation"); Assert.That (responseBody, Is.EqualTo ("authenticated"), "Response body"); + Assert.That (firstUnauthenticatedIndex, Is.GreaterThan (0), "Server should have received an unauthenticated request"); + Assert.That (firstAuthenticatedIndex, Is.GreaterThan (0), "Server should have received an authenticated request"); + Assert.That (firstUnauthenticatedIndex, Is.LessThan (firstAuthenticatedIndex), "Unauthenticated request should have arrived before the authenticated retry"); if (serverTask.IsFaulted) Assert.Fail ($"Server task failed: {serverTask.Exception}"); From 90f96d56ea4cfdfd3390367f7a65fd9475bd5f21 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 07:29:59 +0000 Subject: [PATCH 19/79] [main] Update dependencies from dotnet/macios (#25520) This pull request updates the following dependencies ## From https://github.com/dotnet/macios - **Subscription**: [c0371266-dd6f-4959-822b-decc72d2d668](https://maestro.dot.net/subscriptions?search=c0371266-dd6f-4959-822b-decc72d2d668) - **Build**: [20260525.2](https://dev.azure.com/devdiv/DevDiv/_build/results?buildId=14185729) ([315724](https://maestro.dot.net/channel/3884/github:dotnet:macios/build/315724)) - **Date Produced**: May 25, 2026 9:48:07 AM UTC - **Commit**: [336ee8588b15a460a3ff34c3129d5d359e9d27aa](https://github.com/dotnet/macios/commit/336ee8588b15a460a3ff34c3129d5d359e9d27aa) - **Branch**: [release/9.0.1xx](https://github.com/dotnet/macios/tree/release/9.0.1xx) - **Dependency Updates**: - From [26.5.9003 to 26.5.9004][1] - Microsoft.iOS.Sdk.net9.0_26.5 - Microsoft.MacCatalyst.Sdk.net9.0_26.5 - Microsoft.macOS.Sdk.net9.0_26.5 - Microsoft.tvOS.Sdk.net9.0_26.5 [1]: https://github.com/dotnet/macios/compare/5a29bbfbaa...336ee8588b --- NuGet.config | 2 +- eng/Version.Details.props | 8 ++++---- eng/Version.Details.xml | 16 ++++++++-------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/NuGet.config b/NuGet.config index 1d436b8dfad1..128c4a3f5b21 100644 --- a/NuGet.config +++ b/NuGet.config @@ -12,7 +12,7 @@ - + diff --git a/eng/Version.Details.props b/eng/Version.Details.props index f05bc8e722c7..7cda8864708c 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -19,16 +19,16 @@ This file should be imported by eng/Versions.props 26.0.11017 18.5.9227 - 26.5.9003 + 26.5.9004 26.0.11017 18.5.9227 - 26.5.9003 + 26.5.9004 26.0.11017 15.5.9227 - 26.5.9003 + 26.5.9004 26.0.11017 18.5.9227 - 26.5.9003 + 26.5.9004 11.0.0-prerelease.26264.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 8532af929144..f875d42c06e2 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -60,21 +60,21 @@ 797d30720e5e629d23eb146935da94cb1b61047e - + https://github.com/dotnet/macios - 5a29bbfbaac5941a8b229c91f1b606081b49ea10 + 336ee8588b15a460a3ff34c3129d5d359e9d27aa - + https://github.com/dotnet/macios - 5a29bbfbaac5941a8b229c91f1b606081b49ea10 + 336ee8588b15a460a3ff34c3129d5d359e9d27aa - + https://github.com/dotnet/macios - 5a29bbfbaac5941a8b229c91f1b606081b49ea10 + 336ee8588b15a460a3ff34c3129d5d359e9d27aa - + https://github.com/dotnet/macios - 5a29bbfbaac5941a8b229c91f1b606081b49ea10 + 336ee8588b15a460a3ff34c3129d5d359e9d27aa From 04c0a3143e271aafd806a3eb3e91e0c83bddd748 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 27 May 2026 09:47:27 +0200 Subject: [PATCH 20/79] [devops] If SYSTEM_PULLREQUEST_PULLREQUESTNUMBER is set, always use that as the PR number. (#25517) There's no need to try to figure out which PR we're working on, if we're told by using the SYSTEM_PULLREQUEST_PULLREQUESTNUMBER environment variable. --- tools/devops/automation/scripts/GitHub.psm1 | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/devops/automation/scripts/GitHub.psm1 b/tools/devops/automation/scripts/GitHub.psm1 index 82436b73540d..c4b001ee52d1 100644 --- a/tools/devops/automation/scripts/GitHub.psm1 +++ b/tools/devops/automation/scripts/GitHub.psm1 @@ -306,7 +306,7 @@ class GitHubComments { return $true } else { # we might have gotten here because of the trigger type. This means that we are in a PR BUT - # we did not get the PR ids, but those can be found in the diff evirtoment vars + # we did not get the PR ids, but those can be found in the diff environment vars if ($Env:BUILD_REASON -eq "PullRequest") { # set the PR ids to the PR we have in the VSTS env vars $this.PRIds = @($Env:SYSTEM_PULLREQUEST_PULLREQUESTNUMBER) @@ -1118,6 +1118,13 @@ function Get-GitHubPRsForHash { Write-Host "Getting related PR ids for commit $Hash" $prs = [System.Collections.ArrayList]@() + + if ($Env:SYSTEM_PULLREQUEST_PULLREQUESTNUMBER) { + Write-Host "Found PR in environment: $Env:SYSTEM_PULLREQUEST_PULLREQUESTNUMBER" + $prs.Add($Env:SYSTEM_PULLREQUEST_PULLREQUESTNUMBER) > $null + return $prs + } + if ($Env:IS_PR -eq "false") { Write-Host "This isn't a PR, IS_PR=false" return $prs From 70bac4ad23286a94ec5ca3a12971b6f388f24acd Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 27 May 2026 07:19:42 -0400 Subject: [PATCH 21/79] [aw] Align Code Radiator base-branch policy with safe-output glob matching (#25440) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code Radiator was failing in `safe_outputs` when processing `create_pull_request` for `xcode26.5`, causing code-push output cancellation and masking subsequent `missing_tool` reporting. The root issue was branch-allowlist pattern semantics not matching runtime glob behavior. - **Safe-output base branch policy update** - Updated `safe-outputs.create-pull-request.allowed-base-branches` in `.github/workflows/code-radiator.md` to runtime-compatible globs: - `net*.0` - `xcode*` - `xcode*.*` - This preserves support for both major-only and dotted Xcode branches (for example `xcode26` and `xcode26.5`). - **Workflow lock regeneration** - Recompiled `.github/workflows/code-radiator.lock.yml` so generated `create_pull_request.allowed_base_branches` matches source frontmatter. - **Prompt/body pattern alignment** - Updated the workflow’s documented “Target Branch Patterns” in the markdown body to match the same glob set and avoid config/prompt drift. ```yaml safe-outputs: create-pull-request: allowed-base-branches: - "net*.0" - "xcode*" - "xcode*.*" ``` --------- Co-authored-by: rolfbjarne <249268+rolfbjarne@users.noreply.github.com> --- .github/workflows/code-radiator.lock.yml | 34 ++++++++++++------------ .github/workflows/code-radiator.md | 12 ++++----- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/code-radiator.lock.yml b/.github/workflows/code-radiator.lock.yml index cd32f396e575..3db0cc9188d5 100644 --- a/.github/workflows/code-radiator.lock.yml +++ b/.github/workflows/code-radiator.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"a665debda1a902047622496aa625b015b0b9f053d3892454a63ac7b73cc61808","compiler_version":"v0.74.8","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.5"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"c4be7d8ac2692259a94c4639bb109ba34737aa41d51fcd6cc2948df40f5a1bb4","compiler_version":"v0.74.8","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.5"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"efa55847f72aadb03490d955263ff911bf758700","version":"v0.74.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.49"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.9","digest":"sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -190,24 +190,24 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_0170a78c8d56645a_EOF' + cat << 'GH_AW_PROMPT_f2376eda3a4bbca0_EOF' - GH_AW_PROMPT_0170a78c8d56645a_EOF + GH_AW_PROMPT_f2376eda3a4bbca0_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_0170a78c8d56645a_EOF' + cat << 'GH_AW_PROMPT_f2376eda3a4bbca0_EOF' Tools: add_comment(max:10), create_pull_request(max:10), update_pull_request(max:10), add_labels(max:10), push_to_pull_request_branch(max:10), missing_tool, missing_data, noop - GH_AW_PROMPT_0170a78c8d56645a_EOF + GH_AW_PROMPT_f2376eda3a4bbca0_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_push_to_pr_branch.md" - cat << 'GH_AW_PROMPT_0170a78c8d56645a_EOF' + cat << 'GH_AW_PROMPT_f2376eda3a4bbca0_EOF' - GH_AW_PROMPT_0170a78c8d56645a_EOF + GH_AW_PROMPT_f2376eda3a4bbca0_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_0170a78c8d56645a_EOF' + cat << 'GH_AW_PROMPT_f2376eda3a4bbca0_EOF' The following GitHub context information is available for this workflow: {{#if github.actor}} @@ -239,12 +239,12 @@ jobs: - **Note**: If a branch you need is not in the list above and is not listed as an additional fetched ref, it has NOT been checked out. For private repositories you cannot fetch it without proper authentication. If the branch is required and not available, exit with an error and ask the user to add it to the `fetch:` option of the `checkout:` configuration (e.g., `fetch: ["refs/pulls/open/*"]` for all open PR refs, or `fetch: ["main", "feature/my-branch"]` for specific branches). - GH_AW_PROMPT_0170a78c8d56645a_EOF + GH_AW_PROMPT_f2376eda3a4bbca0_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_0170a78c8d56645a_EOF' + cat << 'GH_AW_PROMPT_f2376eda3a4bbca0_EOF' {{#runtime-import .github/workflows/code-radiator.md}} - GH_AW_PROMPT_0170a78c8d56645a_EOF + GH_AW_PROMPT_f2376eda3a4bbca0_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -451,9 +451,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_1344cecc882c8d36_EOF' - {"add_comment":{"max":10,"target":"*"},"add_labels":{"max":10,"target":"*"},"create_pull_request":{"allowed_base_branches":["net[0-9]*.0","xcode[0-9]*","xcode[0-9]*.[0-9]*"],"max":10,"max_patch_files":100,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"]},"create_report_incomplete_issue":{},"merge_pull_request":{"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_to_pull_request_branch":{"if_no_changes":"warn","max":10,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"target":"*","title_prefix":"🤖 Merge 'main' =\u003e '"},"report_incomplete":{},"update_pull_request":{"allow_body":true,"allow_title":true,"max":10,"update_branch":false}} - GH_AW_SAFE_OUTPUTS_CONFIG_1344cecc882c8d36_EOF + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_c85768df61d14c26_EOF' + {"add_comment":{"max":10,"target":"*"},"add_labels":{"max":10,"target":"*"},"create_pull_request":{"allowed_base_branches":["net*.0","xcode*","xcode*.*"],"max":10,"max_patch_files":100,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"]},"create_report_incomplete_issue":{},"merge_pull_request":{"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_to_pull_request_branch":{"if_no_changes":"warn","max":10,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"target":"*","title_prefix":"🤖 Merge 'main' =\u003e '"},"report_incomplete":{},"update_pull_request":{"allow_body":true,"allow_title":true,"max":10,"update_branch":false}} + GH_AW_SAFE_OUTPUTS_CONFIG_c85768df61d14c26_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -796,7 +796,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_2bd87db1366a659d_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_be302d958a95e8cb_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -840,7 +840,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_2bd87db1366a659d_EOF + GH_AW_MCP_CONFIG_be302d958a95e8cb_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1563,7 +1563,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10,\"target\":\"*\"},\"add_labels\":{\"max\":10,\"target\":\"*\"},\"create_pull_request\":{\"allowed_base_branches\":[\"net[0-9]*.0\",\"xcode[0-9]*\",\"xcode[0-9]*.[0-9]*\"],\"max\":10,\"max_patch_files\":100,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"]},\"create_report_incomplete_issue\":{},\"merge_pull_request\":{\"max\":10},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max\":10,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"target\":\"*\",\"title_prefix\":\"🤖 Merge 'main' =\\u003e '\"},\"report_incomplete\":{},\"update_pull_request\":{\"allow_body\":true,\"allow_title\":true,\"max\":10,\"update_branch\":false}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10,\"target\":\"*\"},\"add_labels\":{\"max\":10,\"target\":\"*\"},\"create_pull_request\":{\"allowed_base_branches\":[\"net*.0\",\"xcode*\",\"xcode*.*\"],\"max\":10,\"max_patch_files\":100,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"]},\"create_report_incomplete_issue\":{},\"merge_pull_request\":{\"max\":10},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max\":10,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"target\":\"*\",\"title_prefix\":\"🤖 Merge 'main' =\\u003e '\"},\"report_incomplete\":{},\"update_pull_request\":{\"allow_body\":true,\"allow_title\":true,\"max\":10,\"update_branch\":false}}" GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }} with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/code-radiator.md b/.github/workflows/code-radiator.md index 9fec3bff181d..496e72c3da55 100644 --- a/.github/workflows/code-radiator.md +++ b/.github/workflows/code-radiator.md @@ -28,9 +28,9 @@ safe-outputs: create-pull-request: max: 10 allowed-base-branches: - - "net[0-9]*.0" - - "xcode[0-9]*" - - "xcode[0-9]*.[0-9]*" + - "net*.0" + - "xcode*" + - "xcode*.*" add-comment: max: 10 target: "*" @@ -54,9 +54,9 @@ Merge code from `main` into active target branches, creating pull requests for e ## Target Branch Patterns Only consider remote branches matching these patterns: -- `net[0-9]*.0` (e.g., `net11.0`, `net10.0`) -- `xcode[0-9]*` (e.g., `xcode26`) -- `xcode[0-9]*.[0-9]*` (e.g., `xcode26.4`) +- `net*.0` (e.g., `net11.0`, `net10.0`) +- `xcode*` (e.g., `xcode26`) +- `xcode*.*` (e.g., `xcode26.4`) Only process branches that have had commits in the last 30 days. From 3a676cc64dc5e56980e943ca745502df90eeaad9 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Wed, 27 May 2026 08:53:47 -0700 Subject: [PATCH 22/79] Localized file check-in by OneLocBuild Task: Build definition ID 14411: Build ID 14187915 (#25518) This is the pull request automatically created by the OneLocBuild task in the build process to check-in localized files generated based upon translation source files (.lcl files) handed-back from the downstream localization pipeline. If there are issues in translations, visit https://aka.ms/icxLocBug and log bugs for fixes. The OneLocBuild wiki is https://aka.ms/onelocbuild and the localization process in general is documented at https://aka.ms/AllAboutLoc. --- .../TranslatedAssemblies/MSBStrings.cs.resx | 12 ++++++++++++ .../TranslatedAssemblies/MSBStrings.de.resx | 12 ++++++++++++ .../TranslatedAssemblies/MSBStrings.es.resx | 12 ++++++++++++ .../TranslatedAssemblies/MSBStrings.fr.resx | 12 ++++++++++++ .../TranslatedAssemblies/MSBStrings.it.resx | 12 ++++++++++++ .../TranslatedAssemblies/MSBStrings.ja.resx | 12 ++++++++++++ .../TranslatedAssemblies/MSBStrings.ko.resx | 12 ++++++++++++ .../TranslatedAssemblies/MSBStrings.pl.resx | 12 ++++++++++++ .../TranslatedAssemblies/MSBStrings.pt-BR.resx | 12 ++++++++++++ .../TranslatedAssemblies/MSBStrings.ru.resx | 12 ++++++++++++ .../TranslatedAssemblies/MSBStrings.tr.resx | 12 ++++++++++++ .../TranslatedAssemblies/MSBStrings.zh-Hans.resx | 12 ++++++++++++ .../TranslatedAssemblies/MSBStrings.zh-Hant.resx | 12 ++++++++++++ 13 files changed, 156 insertions(+) diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.cs.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.cs.resx index 0714444b8e01..f3125770202e 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.cs.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.cs.resx @@ -1284,4 +1284,16 @@ The task '{0}' requires the property '{1}' to be set. Please file an issue at https://github.com/dotnet/macios/issues/new/choose. + + The environment variable '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The environment variable '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.de.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.de.resx index 0714444b8e01..f3125770202e 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.de.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.de.resx @@ -1284,4 +1284,16 @@ The task '{0}' requires the property '{1}' to be set. Please file an issue at https://github.com/dotnet/macios/issues/new/choose. + + The environment variable '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The environment variable '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.es.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.es.resx index 0714444b8e01..f3125770202e 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.es.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.es.resx @@ -1284,4 +1284,16 @@ The task '{0}' requires the property '{1}' to be set. Please file an issue at https://github.com/dotnet/macios/issues/new/choose. + + The environment variable '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The environment variable '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.fr.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.fr.resx index 0714444b8e01..f3125770202e 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.fr.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.fr.resx @@ -1284,4 +1284,16 @@ The task '{0}' requires the property '{1}' to be set. Please file an issue at https://github.com/dotnet/macios/issues/new/choose. + + The environment variable '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The environment variable '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.it.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.it.resx index 0714444b8e01..f3125770202e 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.it.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.it.resx @@ -1284,4 +1284,16 @@ The task '{0}' requires the property '{1}' to be set. Please file an issue at https://github.com/dotnet/macios/issues/new/choose. + + The environment variable '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The environment variable '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ja.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ja.resx index 0714444b8e01..f3125770202e 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ja.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ja.resx @@ -1284,4 +1284,16 @@ The task '{0}' requires the property '{1}' to be set. Please file an issue at https://github.com/dotnet/macios/issues/new/choose. + + The environment variable '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The environment variable '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ko.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ko.resx index 0714444b8e01..f3125770202e 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ko.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ko.resx @@ -1284,4 +1284,16 @@ The task '{0}' requires the property '{1}' to be set. Please file an issue at https://github.com/dotnet/macios/issues/new/choose. + + The environment variable '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The environment variable '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.pl.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.pl.resx index 0714444b8e01..f3125770202e 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.pl.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.pl.resx @@ -1284,4 +1284,16 @@ The task '{0}' requires the property '{1}' to be set. Please file an issue at https://github.com/dotnet/macios/issues/new/choose. + + The environment variable '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The environment variable '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.pt-BR.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.pt-BR.resx index 0714444b8e01..f3125770202e 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.pt-BR.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.pt-BR.resx @@ -1284,4 +1284,16 @@ The task '{0}' requires the property '{1}' to be set. Please file an issue at https://github.com/dotnet/macios/issues/new/choose. + + The environment variable '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The environment variable '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ru.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ru.resx index 0714444b8e01..f3125770202e 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ru.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ru.resx @@ -1284,4 +1284,16 @@ The task '{0}' requires the property '{1}' to be set. Please file an issue at https://github.com/dotnet/macios/issues/new/choose. + + The environment variable '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The environment variable '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.tr.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.tr.resx index 0714444b8e01..f3125770202e 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.tr.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.tr.resx @@ -1284,4 +1284,16 @@ The task '{0}' requires the property '{1}' to be set. Please file an issue at https://github.com/dotnet/macios/issues/new/choose. + + The environment variable '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The environment variable '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.zh-Hans.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.zh-Hans.resx index 0714444b8e01..f3125770202e 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.zh-Hans.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.zh-Hans.resx @@ -1284,4 +1284,16 @@ The task '{0}' requires the property '{1}' to be set. Please file an issue at https://github.com/dotnet/macios/issues/new/choose. + + The environment variable '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The environment variable '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.zh-Hant.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.zh-Hant.resx index 0714444b8e01..f3125770202e 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.zh-Hant.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.zh-Hant.resx @@ -1284,4 +1284,16 @@ The task '{0}' requires the property '{1}' to be set. Please file an issue at https://github.com/dotnet/macios/issues/new/choose. + + The environment variable '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The environment variable '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored in .NET 11+. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + \ No newline at end of file From 0ab164c66ef86aef81c72e2cf0485610fdc6df5c Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 27 May 2026 18:16:33 +0200 Subject: [PATCH 23/79] [src] NSBindingSelectionMarker is in AppKit. (#25523) --- src/foundation.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/foundation.cs b/src/foundation.cs index 915fe5d0c9f7..8ceba04a14c1 100644 --- a/src/foundation.cs +++ b/src/foundation.cs @@ -13062,6 +13062,7 @@ interface NSObject2 : NSObjectProtocol { [NoTV] [NoiOS] [NoMacCatalyst] + [ObjectiveCFramework ("AppKit")] interface NSBindingSelectionMarker : NSCopying { /// To be added. /// To be added. From ca43e4a6252db60596a8287d892e9acaed230769 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 27 May 2026 18:57:29 +0200 Subject: [PATCH 24/79] [bgen] Replace XPath lookups with Dictionary in DocumentationManager (#25521) DocumentationManager.TryGetDocumentation was calling `doc.SelectSingleNode("/doc/members/member[@name='...']")` for every member (~30K calls for tvOS). Each XPath query traverses the entire XML DOM, making this O(n*m) where n=members queried and m=total members. Replace with a `Dictionary` built once at construction time, giving O(1) lookups thereafter. Performance (tvOS, wall clock time): - Before: 270s, peak RSS 363MB - After: 28s, peak RSS 358MB - Speedup: 9.6x (242s saved) Generated code: verified identical (git diff = empty). --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/bgen/DocumentationManager.cs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/bgen/DocumentationManager.cs b/src/bgen/DocumentationManager.cs index 8b8a29851b37..bd9f3e8c18e2 100644 --- a/src/bgen/DocumentationManager.cs +++ b/src/bgen/DocumentationManager.cs @@ -10,14 +10,25 @@ public class DocumentationManager { string xml; - XmlDocument? doc; + Dictionary? memberLookup; public DocumentationManager (string assembly) { this.xml = Path.ChangeExtension (assembly, ".xml"); if (File.Exists (xml)) { - doc = new XmlDocument (); + var doc = new XmlDocument (); doc.LoadWithoutNetworkAccess (xml); + // Pre-build a dictionary for O(1) member lookups instead of + // O(n) XPath queries on every TryGetDocumentation call. + var members = doc.SelectNodes ("/doc/members/member[@name]"); + if (members is not null) { + memberLookup = new Dictionary (members.Count); + foreach (XmlNode member in members) { + var name = member.Attributes? ["name"]?.Value; + if (name is not null) + memberLookup [name] = member; + } + } } } @@ -38,14 +49,13 @@ public bool TryGetDocumentation (MemberInfo member, [NotNullWhen (true)] out str { documentation = null; - if (doc is null) + if (memberLookup is null) return false; if (!TryGetId (member, out var id)) return false; - var node = doc.SelectSingleNode ($"/doc/members/member[@name='{id}']"); - if (node is null) + if (!memberLookup.TryGetValue (id, out var node)) return false; if (transformNode is not null) From 5092c587b33f4b2a6d043d7b4dd6b00087091ce9 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Wed, 27 May 2026 09:57:31 -0700 Subject: [PATCH 25/79] Localized file check-in by OneLocBuild Task: Build definition ID 14411: Build ID 14204921 (#25538) This is the pull request automatically created by the OneLocBuild task in the build process to check-in localized files generated based upon translation source files (.lcl files) handed-back from the downstream localization pipeline. If there are issues in translations, visit https://aka.ms/icxLocBug and log bugs for fixes. The OneLocBuild wiki is https://aka.ms/onelocbuild and the localization process in general is documented at https://aka.ms/AllAboutLoc. --- macios/src/TranslatedAssemblies/Resources.cs.resx | 2 +- macios/src/TranslatedAssemblies/Resources.de.resx | 2 +- macios/src/TranslatedAssemblies/Resources.es.resx | 2 +- macios/src/TranslatedAssemblies/Resources.fr.resx | 2 +- macios/src/TranslatedAssemblies/Resources.it.resx | 2 +- macios/src/TranslatedAssemblies/Resources.ja.resx | 2 +- macios/src/TranslatedAssemblies/Resources.ko.resx | 2 +- macios/src/TranslatedAssemblies/Resources.pl.resx | 2 +- macios/src/TranslatedAssemblies/Resources.pt-BR.resx | 2 +- macios/src/TranslatedAssemblies/Resources.ru.resx | 2 +- macios/src/TranslatedAssemblies/Resources.tr.resx | 2 +- macios/src/TranslatedAssemblies/Resources.zh-Hans.resx | 2 +- macios/src/TranslatedAssemblies/Resources.zh-Hant.resx | 2 +- macios/tools/mtouch/TranslatedAssemblies/Errors.cs.resx | 3 +++ macios/tools/mtouch/TranslatedAssemblies/Errors.de.resx | 3 +++ macios/tools/mtouch/TranslatedAssemblies/Errors.es.resx | 3 +++ macios/tools/mtouch/TranslatedAssemblies/Errors.fr.resx | 3 +++ macios/tools/mtouch/TranslatedAssemblies/Errors.it.resx | 3 +++ macios/tools/mtouch/TranslatedAssemblies/Errors.ja.resx | 3 +++ macios/tools/mtouch/TranslatedAssemblies/Errors.ko.resx | 3 +++ macios/tools/mtouch/TranslatedAssemblies/Errors.pl.resx | 3 +++ macios/tools/mtouch/TranslatedAssemblies/Errors.pt-BR.resx | 3 +++ macios/tools/mtouch/TranslatedAssemblies/Errors.ru.resx | 3 +++ macios/tools/mtouch/TranslatedAssemblies/Errors.tr.resx | 3 +++ macios/tools/mtouch/TranslatedAssemblies/Errors.zh-Hans.resx | 3 +++ macios/tools/mtouch/TranslatedAssemblies/Errors.zh-Hant.resx | 3 +++ 26 files changed, 52 insertions(+), 13 deletions(-) diff --git a/macios/src/TranslatedAssemblies/Resources.cs.resx b/macios/src/TranslatedAssemblies/Resources.cs.resx index f4c1d83d2521..7b439f46e642 100644 --- a/macios/src/TranslatedAssemblies/Resources.cs.resx +++ b/macios/src/TranslatedAssemblies/Resources.cs.resx @@ -250,7 +250,7 @@ `{0}`: Enums attributed with [{1}] must have an underlying type of `long` or `ulong` - Support for ZeroCopy strings is not implemented. Strings will be marshalled as NSStrings. + The --use-zero-copy option is not supported and will be ignored. Internal sanity check failed, please file a bug report (https://github.com/dotnet/macios/issues/new) with a test case. diff --git a/macios/src/TranslatedAssemblies/Resources.de.resx b/macios/src/TranslatedAssemblies/Resources.de.resx index f4c1d83d2521..7b439f46e642 100644 --- a/macios/src/TranslatedAssemblies/Resources.de.resx +++ b/macios/src/TranslatedAssemblies/Resources.de.resx @@ -250,7 +250,7 @@ `{0}`: Enums attributed with [{1}] must have an underlying type of `long` or `ulong` - Support for ZeroCopy strings is not implemented. Strings will be marshalled as NSStrings. + The --use-zero-copy option is not supported and will be ignored. Internal sanity check failed, please file a bug report (https://github.com/dotnet/macios/issues/new) with a test case. diff --git a/macios/src/TranslatedAssemblies/Resources.es.resx b/macios/src/TranslatedAssemblies/Resources.es.resx index f4c1d83d2521..7b439f46e642 100644 --- a/macios/src/TranslatedAssemblies/Resources.es.resx +++ b/macios/src/TranslatedAssemblies/Resources.es.resx @@ -250,7 +250,7 @@ `{0}`: Enums attributed with [{1}] must have an underlying type of `long` or `ulong` - Support for ZeroCopy strings is not implemented. Strings will be marshalled as NSStrings. + The --use-zero-copy option is not supported and will be ignored. Internal sanity check failed, please file a bug report (https://github.com/dotnet/macios/issues/new) with a test case. diff --git a/macios/src/TranslatedAssemblies/Resources.fr.resx b/macios/src/TranslatedAssemblies/Resources.fr.resx index f4c1d83d2521..7b439f46e642 100644 --- a/macios/src/TranslatedAssemblies/Resources.fr.resx +++ b/macios/src/TranslatedAssemblies/Resources.fr.resx @@ -250,7 +250,7 @@ `{0}`: Enums attributed with [{1}] must have an underlying type of `long` or `ulong` - Support for ZeroCopy strings is not implemented. Strings will be marshalled as NSStrings. + The --use-zero-copy option is not supported and will be ignored. Internal sanity check failed, please file a bug report (https://github.com/dotnet/macios/issues/new) with a test case. diff --git a/macios/src/TranslatedAssemblies/Resources.it.resx b/macios/src/TranslatedAssemblies/Resources.it.resx index f4c1d83d2521..7b439f46e642 100644 --- a/macios/src/TranslatedAssemblies/Resources.it.resx +++ b/macios/src/TranslatedAssemblies/Resources.it.resx @@ -250,7 +250,7 @@ `{0}`: Enums attributed with [{1}] must have an underlying type of `long` or `ulong` - Support for ZeroCopy strings is not implemented. Strings will be marshalled as NSStrings. + The --use-zero-copy option is not supported and will be ignored. Internal sanity check failed, please file a bug report (https://github.com/dotnet/macios/issues/new) with a test case. diff --git a/macios/src/TranslatedAssemblies/Resources.ja.resx b/macios/src/TranslatedAssemblies/Resources.ja.resx index f4c1d83d2521..7b439f46e642 100644 --- a/macios/src/TranslatedAssemblies/Resources.ja.resx +++ b/macios/src/TranslatedAssemblies/Resources.ja.resx @@ -250,7 +250,7 @@ `{0}`: Enums attributed with [{1}] must have an underlying type of `long` or `ulong` - Support for ZeroCopy strings is not implemented. Strings will be marshalled as NSStrings. + The --use-zero-copy option is not supported and will be ignored. Internal sanity check failed, please file a bug report (https://github.com/dotnet/macios/issues/new) with a test case. diff --git a/macios/src/TranslatedAssemblies/Resources.ko.resx b/macios/src/TranslatedAssemblies/Resources.ko.resx index f4c1d83d2521..7b439f46e642 100644 --- a/macios/src/TranslatedAssemblies/Resources.ko.resx +++ b/macios/src/TranslatedAssemblies/Resources.ko.resx @@ -250,7 +250,7 @@ `{0}`: Enums attributed with [{1}] must have an underlying type of `long` or `ulong` - Support for ZeroCopy strings is not implemented. Strings will be marshalled as NSStrings. + The --use-zero-copy option is not supported and will be ignored. Internal sanity check failed, please file a bug report (https://github.com/dotnet/macios/issues/new) with a test case. diff --git a/macios/src/TranslatedAssemblies/Resources.pl.resx b/macios/src/TranslatedAssemblies/Resources.pl.resx index f4c1d83d2521..7b439f46e642 100644 --- a/macios/src/TranslatedAssemblies/Resources.pl.resx +++ b/macios/src/TranslatedAssemblies/Resources.pl.resx @@ -250,7 +250,7 @@ `{0}`: Enums attributed with [{1}] must have an underlying type of `long` or `ulong` - Support for ZeroCopy strings is not implemented. Strings will be marshalled as NSStrings. + The --use-zero-copy option is not supported and will be ignored. Internal sanity check failed, please file a bug report (https://github.com/dotnet/macios/issues/new) with a test case. diff --git a/macios/src/TranslatedAssemblies/Resources.pt-BR.resx b/macios/src/TranslatedAssemblies/Resources.pt-BR.resx index f4c1d83d2521..7b439f46e642 100644 --- a/macios/src/TranslatedAssemblies/Resources.pt-BR.resx +++ b/macios/src/TranslatedAssemblies/Resources.pt-BR.resx @@ -250,7 +250,7 @@ `{0}`: Enums attributed with [{1}] must have an underlying type of `long` or `ulong` - Support for ZeroCopy strings is not implemented. Strings will be marshalled as NSStrings. + The --use-zero-copy option is not supported and will be ignored. Internal sanity check failed, please file a bug report (https://github.com/dotnet/macios/issues/new) with a test case. diff --git a/macios/src/TranslatedAssemblies/Resources.ru.resx b/macios/src/TranslatedAssemblies/Resources.ru.resx index f4c1d83d2521..7b439f46e642 100644 --- a/macios/src/TranslatedAssemblies/Resources.ru.resx +++ b/macios/src/TranslatedAssemblies/Resources.ru.resx @@ -250,7 +250,7 @@ `{0}`: Enums attributed with [{1}] must have an underlying type of `long` or `ulong` - Support for ZeroCopy strings is not implemented. Strings will be marshalled as NSStrings. + The --use-zero-copy option is not supported and will be ignored. Internal sanity check failed, please file a bug report (https://github.com/dotnet/macios/issues/new) with a test case. diff --git a/macios/src/TranslatedAssemblies/Resources.tr.resx b/macios/src/TranslatedAssemblies/Resources.tr.resx index f4c1d83d2521..7b439f46e642 100644 --- a/macios/src/TranslatedAssemblies/Resources.tr.resx +++ b/macios/src/TranslatedAssemblies/Resources.tr.resx @@ -250,7 +250,7 @@ `{0}`: Enums attributed with [{1}] must have an underlying type of `long` or `ulong` - Support for ZeroCopy strings is not implemented. Strings will be marshalled as NSStrings. + The --use-zero-copy option is not supported and will be ignored. Internal sanity check failed, please file a bug report (https://github.com/dotnet/macios/issues/new) with a test case. diff --git a/macios/src/TranslatedAssemblies/Resources.zh-Hans.resx b/macios/src/TranslatedAssemblies/Resources.zh-Hans.resx index f4c1d83d2521..7b439f46e642 100644 --- a/macios/src/TranslatedAssemblies/Resources.zh-Hans.resx +++ b/macios/src/TranslatedAssemblies/Resources.zh-Hans.resx @@ -250,7 +250,7 @@ `{0}`: Enums attributed with [{1}] must have an underlying type of `long` or `ulong` - Support for ZeroCopy strings is not implemented. Strings will be marshalled as NSStrings. + The --use-zero-copy option is not supported and will be ignored. Internal sanity check failed, please file a bug report (https://github.com/dotnet/macios/issues/new) with a test case. diff --git a/macios/src/TranslatedAssemblies/Resources.zh-Hant.resx b/macios/src/TranslatedAssemblies/Resources.zh-Hant.resx index f4c1d83d2521..7b439f46e642 100644 --- a/macios/src/TranslatedAssemblies/Resources.zh-Hant.resx +++ b/macios/src/TranslatedAssemblies/Resources.zh-Hant.resx @@ -250,7 +250,7 @@ `{0}`: Enums attributed with [{1}] must have an underlying type of `long` or `ulong` - Support for ZeroCopy strings is not implemented. Strings will be marshalled as NSStrings. + The --use-zero-copy option is not supported and will be ignored. Internal sanity check failed, please file a bug report (https://github.com/dotnet/macios/issues/new) with a test case. diff --git a/macios/tools/mtouch/TranslatedAssemblies/Errors.cs.resx b/macios/tools/mtouch/TranslatedAssemblies/Errors.cs.resx index 3cbbaf109f2a..ce4b85b8b369 100644 --- a/macios/tools/mtouch/TranslatedAssemblies/Errors.cs.resx +++ b/macios/tools/mtouch/TranslatedAssemblies/Errors.cs.resx @@ -857,6 +857,9 @@ '{0}' has multiple SupportedSimulator attributes for the '{1}' platform. Please file an issue at https://github.com/dotnet/macios/issues/new + + The 'InlineClassGetHandle' option is set to 'Strict', but we're using the dynamic registrar. This is not a supported configuration, because 'Strict' mode requires exported Objective-C classes to be available at compile time, but the dynamic registrar will create them at runtime. Please either change the 'InlineClassGetHandle' option to 'Disabled' or 'Compat', or switch to using the static registrar. + The linker step '{0}' failed during processing: {1} diff --git a/macios/tools/mtouch/TranslatedAssemblies/Errors.de.resx b/macios/tools/mtouch/TranslatedAssemblies/Errors.de.resx index 3cbbaf109f2a..ce4b85b8b369 100644 --- a/macios/tools/mtouch/TranslatedAssemblies/Errors.de.resx +++ b/macios/tools/mtouch/TranslatedAssemblies/Errors.de.resx @@ -857,6 +857,9 @@ '{0}' has multiple SupportedSimulator attributes for the '{1}' platform. Please file an issue at https://github.com/dotnet/macios/issues/new + + The 'InlineClassGetHandle' option is set to 'Strict', but we're using the dynamic registrar. This is not a supported configuration, because 'Strict' mode requires exported Objective-C classes to be available at compile time, but the dynamic registrar will create them at runtime. Please either change the 'InlineClassGetHandle' option to 'Disabled' or 'Compat', or switch to using the static registrar. + The linker step '{0}' failed during processing: {1} diff --git a/macios/tools/mtouch/TranslatedAssemblies/Errors.es.resx b/macios/tools/mtouch/TranslatedAssemblies/Errors.es.resx index 3cbbaf109f2a..ce4b85b8b369 100644 --- a/macios/tools/mtouch/TranslatedAssemblies/Errors.es.resx +++ b/macios/tools/mtouch/TranslatedAssemblies/Errors.es.resx @@ -857,6 +857,9 @@ '{0}' has multiple SupportedSimulator attributes for the '{1}' platform. Please file an issue at https://github.com/dotnet/macios/issues/new + + The 'InlineClassGetHandle' option is set to 'Strict', but we're using the dynamic registrar. This is not a supported configuration, because 'Strict' mode requires exported Objective-C classes to be available at compile time, but the dynamic registrar will create them at runtime. Please either change the 'InlineClassGetHandle' option to 'Disabled' or 'Compat', or switch to using the static registrar. + The linker step '{0}' failed during processing: {1} diff --git a/macios/tools/mtouch/TranslatedAssemblies/Errors.fr.resx b/macios/tools/mtouch/TranslatedAssemblies/Errors.fr.resx index 3cbbaf109f2a..ce4b85b8b369 100644 --- a/macios/tools/mtouch/TranslatedAssemblies/Errors.fr.resx +++ b/macios/tools/mtouch/TranslatedAssemblies/Errors.fr.resx @@ -857,6 +857,9 @@ '{0}' has multiple SupportedSimulator attributes for the '{1}' platform. Please file an issue at https://github.com/dotnet/macios/issues/new + + The 'InlineClassGetHandle' option is set to 'Strict', but we're using the dynamic registrar. This is not a supported configuration, because 'Strict' mode requires exported Objective-C classes to be available at compile time, but the dynamic registrar will create them at runtime. Please either change the 'InlineClassGetHandle' option to 'Disabled' or 'Compat', or switch to using the static registrar. + The linker step '{0}' failed during processing: {1} diff --git a/macios/tools/mtouch/TranslatedAssemblies/Errors.it.resx b/macios/tools/mtouch/TranslatedAssemblies/Errors.it.resx index 3cbbaf109f2a..ce4b85b8b369 100644 --- a/macios/tools/mtouch/TranslatedAssemblies/Errors.it.resx +++ b/macios/tools/mtouch/TranslatedAssemblies/Errors.it.resx @@ -857,6 +857,9 @@ '{0}' has multiple SupportedSimulator attributes for the '{1}' platform. Please file an issue at https://github.com/dotnet/macios/issues/new + + The 'InlineClassGetHandle' option is set to 'Strict', but we're using the dynamic registrar. This is not a supported configuration, because 'Strict' mode requires exported Objective-C classes to be available at compile time, but the dynamic registrar will create them at runtime. Please either change the 'InlineClassGetHandle' option to 'Disabled' or 'Compat', or switch to using the static registrar. + The linker step '{0}' failed during processing: {1} diff --git a/macios/tools/mtouch/TranslatedAssemblies/Errors.ja.resx b/macios/tools/mtouch/TranslatedAssemblies/Errors.ja.resx index 3cbbaf109f2a..ce4b85b8b369 100644 --- a/macios/tools/mtouch/TranslatedAssemblies/Errors.ja.resx +++ b/macios/tools/mtouch/TranslatedAssemblies/Errors.ja.resx @@ -857,6 +857,9 @@ '{0}' has multiple SupportedSimulator attributes for the '{1}' platform. Please file an issue at https://github.com/dotnet/macios/issues/new + + The 'InlineClassGetHandle' option is set to 'Strict', but we're using the dynamic registrar. This is not a supported configuration, because 'Strict' mode requires exported Objective-C classes to be available at compile time, but the dynamic registrar will create them at runtime. Please either change the 'InlineClassGetHandle' option to 'Disabled' or 'Compat', or switch to using the static registrar. + The linker step '{0}' failed during processing: {1} diff --git a/macios/tools/mtouch/TranslatedAssemblies/Errors.ko.resx b/macios/tools/mtouch/TranslatedAssemblies/Errors.ko.resx index 3cbbaf109f2a..ce4b85b8b369 100644 --- a/macios/tools/mtouch/TranslatedAssemblies/Errors.ko.resx +++ b/macios/tools/mtouch/TranslatedAssemblies/Errors.ko.resx @@ -857,6 +857,9 @@ '{0}' has multiple SupportedSimulator attributes for the '{1}' platform. Please file an issue at https://github.com/dotnet/macios/issues/new + + The 'InlineClassGetHandle' option is set to 'Strict', but we're using the dynamic registrar. This is not a supported configuration, because 'Strict' mode requires exported Objective-C classes to be available at compile time, but the dynamic registrar will create them at runtime. Please either change the 'InlineClassGetHandle' option to 'Disabled' or 'Compat', or switch to using the static registrar. + The linker step '{0}' failed during processing: {1} diff --git a/macios/tools/mtouch/TranslatedAssemblies/Errors.pl.resx b/macios/tools/mtouch/TranslatedAssemblies/Errors.pl.resx index 3cbbaf109f2a..ce4b85b8b369 100644 --- a/macios/tools/mtouch/TranslatedAssemblies/Errors.pl.resx +++ b/macios/tools/mtouch/TranslatedAssemblies/Errors.pl.resx @@ -857,6 +857,9 @@ '{0}' has multiple SupportedSimulator attributes for the '{1}' platform. Please file an issue at https://github.com/dotnet/macios/issues/new + + The 'InlineClassGetHandle' option is set to 'Strict', but we're using the dynamic registrar. This is not a supported configuration, because 'Strict' mode requires exported Objective-C classes to be available at compile time, but the dynamic registrar will create them at runtime. Please either change the 'InlineClassGetHandle' option to 'Disabled' or 'Compat', or switch to using the static registrar. + The linker step '{0}' failed during processing: {1} diff --git a/macios/tools/mtouch/TranslatedAssemblies/Errors.pt-BR.resx b/macios/tools/mtouch/TranslatedAssemblies/Errors.pt-BR.resx index 3cbbaf109f2a..ce4b85b8b369 100644 --- a/macios/tools/mtouch/TranslatedAssemblies/Errors.pt-BR.resx +++ b/macios/tools/mtouch/TranslatedAssemblies/Errors.pt-BR.resx @@ -857,6 +857,9 @@ '{0}' has multiple SupportedSimulator attributes for the '{1}' platform. Please file an issue at https://github.com/dotnet/macios/issues/new + + The 'InlineClassGetHandle' option is set to 'Strict', but we're using the dynamic registrar. This is not a supported configuration, because 'Strict' mode requires exported Objective-C classes to be available at compile time, but the dynamic registrar will create them at runtime. Please either change the 'InlineClassGetHandle' option to 'Disabled' or 'Compat', or switch to using the static registrar. + The linker step '{0}' failed during processing: {1} diff --git a/macios/tools/mtouch/TranslatedAssemblies/Errors.ru.resx b/macios/tools/mtouch/TranslatedAssemblies/Errors.ru.resx index 3cbbaf109f2a..ce4b85b8b369 100644 --- a/macios/tools/mtouch/TranslatedAssemblies/Errors.ru.resx +++ b/macios/tools/mtouch/TranslatedAssemblies/Errors.ru.resx @@ -857,6 +857,9 @@ '{0}' has multiple SupportedSimulator attributes for the '{1}' platform. Please file an issue at https://github.com/dotnet/macios/issues/new + + The 'InlineClassGetHandle' option is set to 'Strict', but we're using the dynamic registrar. This is not a supported configuration, because 'Strict' mode requires exported Objective-C classes to be available at compile time, but the dynamic registrar will create them at runtime. Please either change the 'InlineClassGetHandle' option to 'Disabled' or 'Compat', or switch to using the static registrar. + The linker step '{0}' failed during processing: {1} diff --git a/macios/tools/mtouch/TranslatedAssemblies/Errors.tr.resx b/macios/tools/mtouch/TranslatedAssemblies/Errors.tr.resx index 3cbbaf109f2a..ce4b85b8b369 100644 --- a/macios/tools/mtouch/TranslatedAssemblies/Errors.tr.resx +++ b/macios/tools/mtouch/TranslatedAssemblies/Errors.tr.resx @@ -857,6 +857,9 @@ '{0}' has multiple SupportedSimulator attributes for the '{1}' platform. Please file an issue at https://github.com/dotnet/macios/issues/new + + The 'InlineClassGetHandle' option is set to 'Strict', but we're using the dynamic registrar. This is not a supported configuration, because 'Strict' mode requires exported Objective-C classes to be available at compile time, but the dynamic registrar will create them at runtime. Please either change the 'InlineClassGetHandle' option to 'Disabled' or 'Compat', or switch to using the static registrar. + The linker step '{0}' failed during processing: {1} diff --git a/macios/tools/mtouch/TranslatedAssemblies/Errors.zh-Hans.resx b/macios/tools/mtouch/TranslatedAssemblies/Errors.zh-Hans.resx index 3cbbaf109f2a..ce4b85b8b369 100644 --- a/macios/tools/mtouch/TranslatedAssemblies/Errors.zh-Hans.resx +++ b/macios/tools/mtouch/TranslatedAssemblies/Errors.zh-Hans.resx @@ -857,6 +857,9 @@ '{0}' has multiple SupportedSimulator attributes for the '{1}' platform. Please file an issue at https://github.com/dotnet/macios/issues/new + + The 'InlineClassGetHandle' option is set to 'Strict', but we're using the dynamic registrar. This is not a supported configuration, because 'Strict' mode requires exported Objective-C classes to be available at compile time, but the dynamic registrar will create them at runtime. Please either change the 'InlineClassGetHandle' option to 'Disabled' or 'Compat', or switch to using the static registrar. + The linker step '{0}' failed during processing: {1} diff --git a/macios/tools/mtouch/TranslatedAssemblies/Errors.zh-Hant.resx b/macios/tools/mtouch/TranslatedAssemblies/Errors.zh-Hant.resx index 3cbbaf109f2a..ce4b85b8b369 100644 --- a/macios/tools/mtouch/TranslatedAssemblies/Errors.zh-Hant.resx +++ b/macios/tools/mtouch/TranslatedAssemblies/Errors.zh-Hant.resx @@ -857,6 +857,9 @@ '{0}' has multiple SupportedSimulator attributes for the '{1}' platform. Please file an issue at https://github.com/dotnet/macios/issues/new + + The 'InlineClassGetHandle' option is set to 'Strict', but we're using the dynamic registrar. This is not a supported configuration, because 'Strict' mode requires exported Objective-C classes to be available at compile time, but the dynamic registrar will create them at runtime. Please either change the 'InlineClassGetHandle' option to 'Disabled' or 'Compat', or switch to using the static registrar. + The linker step '{0}' failed during processing: {1} From 9be4da423f3930bd33a004de6c2abca8fa8e0498 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 27 May 2026 18:57:39 +0200 Subject: [PATCH 26/79] [tests] Misc improvements to the app size tests (#25522) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Make assertion failure messages context-aware: in CI, mention the build artifact; locally, just suggest WRITE_KNOWN_FAILURES=1. - Gate the PublishPipelineArtifact step on the directory actually existing to avoid noisy 'succeeded with issues' when tests pass. - Fix misleading assertion messages for preserved-API checks: 'No added APIs.' → 'Unexpected APIs were added to the preserved set.' - Fix SKILL.md documentation to show actual artifact naming pattern (including uploadPrefix) and realistic file name examples. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../skills/update-expected-app-size/SKILL.md | 14 +++++++------- tests/dotnet/UnitTests/AppSizeTest.cs | 18 +++++++++++++----- .../automation/templates/tests/run-tests.yml | 9 ++++++++- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/.github/skills/update-expected-app-size/SKILL.md b/.github/skills/update-expected-app-size/SKILL.md index 88d888c17a0e..1537c6cc59b2 100644 --- a/.github/skills/update-expected-app-size/SKILL.md +++ b/.github/skills/update-expected-app-size/SKILL.md @@ -21,13 +21,13 @@ Download updated expected app size files from Azure DevOps artifacts for the cur The app size tests (`tests/dotnet/UnitTests/AppSizeTest.cs`) compare the built app's size and preserved APIs against expected files stored in `tests/dotnet/UnitTests/expected/`. When the test detects a difference and `WRITE_KNOWN_FAILURES` is not set, it writes the updated expected file to `$(Build.ArtifactStagingDirectory)/updated-expected-sizes/`, and a pipeline step publishes this directory as a build artifact. -The artifact name follows the pattern `updated-expected-sizes-{testPrefix}-{attempt}` (e.g., `updated-expected-sizes-dotnettests_ios-1`). Inside the artifact, files are named: -- `{Platform}-{Runtime}-size.txt` — e.g., `iOS-MonoVM-size.txt` -- `{Platform}-{Runtime}-preservedapis.txt` — e.g., `iOS-MonoVM-preservedapis.txt` +The artifact name follows the pattern `{uploadPrefix}updated-expected-sizes-{testPrefix}-{attempt}` (e.g., `updated-expected-sizes-dotnettests_ios-1`). Inside the artifact, files are named after the test variant: +- `{Platform}-{Variant}-size.txt` — e.g., `iOS-MonoVM-size.txt`, `iOS-MonoVM-interpreter-size.txt`, `iOS-NativeAOT-TrimmableStatic-size.txt`, `MacOSX-CoreCLR-Interpreter-size.txt` +- `{Platform}-{Variant}-preservedapis.txt` — e.g., `iOS-MonoVM-preservedapis.txt`, `MacCatalyst-MonoVM-interpreter-preservedapis.txt` The expected files on disk are at: -- `tests/dotnet/UnitTests/expected/{Platform}-{Runtime}-size.txt` -- `tests/dotnet/UnitTests/expected/{Platform}-{Runtime}-preservedapis.txt` +- `tests/dotnet/UnitTests/expected/{Platform}-{Variant}-size.txt` +- `tests/dotnet/UnitTests/expected/{Platform}-{Variant}-preservedapis.txt` ## Workflow @@ -111,8 +111,8 @@ After placing the files: If automated download fails (auth issues, etc.), provide the user with: 1. The Azure DevOps build URL 2. Instructions to navigate to the build → Summary → Artifacts section -3. Look for individual artifacts whose names match the patterns above -4. Download each file and place it as `tests/dotnet/UnitTests/expected/{artifactName}.txt` +3. Look for individual artifacts whose names contain `updated-expected-sizes` +4. Download the artifact zip, extract it, and copy the `.txt` files (e.g., `iOS-MonoVM-interpreter-size.txt`) into `tests/dotnet/UnitTests/expected/` ## Fallback: Run Locally diff --git a/tests/dotnet/UnitTests/AppSizeTest.cs b/tests/dotnet/UnitTests/AppSizeTest.cs index db6d4d432dbf..701aed8bbf74 100644 --- a/tests/dotnet/UnitTests/AppSizeTest.cs +++ b/tests/dotnet/UnitTests/AppSizeTest.cs @@ -201,15 +201,16 @@ static void AssertAppSize (ApplePlatform platform, string name, string appPath, Console.WriteLine ($" Updated expected file: {expectedSizeReportPath}"); } else if (hasDifferences) { UploadUpdatedExpectedFile (expectedSizeReportPath, report.ToString ()); + var updateHint = GetUpdateHint (); if (hasFileDifferences) { var details = new List (); foreach (var key in filesAdded) details.Add ($"added: '{key}'"); foreach (var key in filesRemoved) details.Add ($"removed: '{key}'"); - Assert.Fail ($"The app bundle's file list changed ({string.Join (", ", details)}). The updated expected file is available as a build artifact (set WRITE_KNOWN_FAILURES=1 to update locally)."); + Assert.Fail ($"The app bundle's file list changed ({string.Join (", ", details)}). {updateHint}"); } - Assert.Fail ($"{msg} The updated expected file is available as a build artifact (set WRITE_KNOWN_FAILURES=1 to update locally)."); + Assert.Fail ($"{msg} {updateHint}"); } } @@ -251,9 +252,9 @@ void AssertAssemblyReport (ApplePlatform platform, string name, string appPath, if (!update) { if (addedAPIs.Count > 0 || removedAPIs.Count > 0) { UploadUpdatedExpectedFile (expectedFile, string.Join ('\n', preservedAPIs) + "\n"); - var updateMsg = " The updated expected file is available as a build artifact (set WRITE_KNOWN_FAILURES=1 to update locally)."; - Assert.That (addedAPIs, Is.Empty, "No added APIs." + updateMsg); - Assert.That (removedAPIs, Is.Empty, "No removed APIs." + updateMsg); + var updateHint = " " + GetUpdateHint (); + Assert.That (addedAPIs, Is.Empty, "Unexpected APIs were added to the preserved set." + updateHint); + Assert.That (removedAPIs, Is.Empty, "APIs were unexpectedly removed from the preserved set." + updateHint); } } } @@ -274,6 +275,13 @@ static void UploadUpdatedExpectedFile (string expectedFilePath, string content) Console.WriteLine ($" Updated expected file written to: {outputFile}"); } + static string GetUpdateHint () + { + if (IsInCI) + return "The updated expected file is available as a build artifact (set WRITE_KNOWN_FAILURES=1 to update locally)."; + return "Set WRITE_KNOWN_FAILURES=1 to update the expected files in-place."; + } + static string FormatBytes (long bytes, bool alwaysShowSign = false) { return $"{(alwaysShowSign && bytes > 0 ? "+" : "")}{bytes:N0} bytes ({bytes / 1024.0:N1} KB = {bytes / (1024.0 * 1024.0):N1} MB)"; diff --git a/tools/devops/automation/templates/tests/run-tests.yml b/tools/devops/automation/templates/tests/run-tests.yml index 40c85db23a1a..483f3afbb30c 100644 --- a/tools/devops/automation/templates/tests/run-tests.yml +++ b/tools/devops/automation/templates/tests/run-tests.yml @@ -173,13 +173,20 @@ steps: condition: succeededOrFailed() # Upload updated expected app size files if the app size tests produced any. +- bash: | + if [ -d "$(Build.ArtifactStagingDirectory)/updated-expected-sizes" ]; then + echo "##vso[task.setvariable variable=HAS_UPDATED_EXPECTED_SIZES]true" + fi + displayName: 'Check for updated expected app size files' + condition: succeededOrFailed() + - task: PublishPipelineArtifact@1 displayName: 'Publish Artifact: Updated expected app size files' inputs: targetPath: '$(Build.ArtifactStagingDirectory)/updated-expected-sizes' artifactName: '${{ parameters.uploadPrefix }}updated-expected-sizes-${{ parameters.testPrefix }}-$(System.JobAttempt)' continueOnError: true - condition: succeededOrFailed() + condition: and(succeededOrFailed(), eq(variables['HAS_UPDATED_EXPECTED_SIZES'], 'true')) - pwsh: | $summaryName = "TestSummary-${{ parameters.testPrefix }}.md" From 963bc34671abab98f25aef3ef39922d65a70fc4c Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Wed, 27 May 2026 09:59:03 -0700 Subject: [PATCH 27/79] Localized file check-in by OneLocBuild Task: Build definition ID 14411: Build ID 14204473 (#25535) This is the pull request automatically created by the OneLocBuild task in the build process to check-in localized files generated based upon translation source files (.lcl files) handed-back from the downstream localization pipeline. If there are issues in translations, visit https://aka.ms/icxLocBug and log bugs for fixes. The OneLocBuild wiki is https://aka.ms/onelocbuild and the localization process in general is documented at https://aka.ms/AllAboutLoc. From 90bc0339e695715f7e8f8b6314119f7039a04987 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Wed, 27 May 2026 09:59:59 -0700 Subject: [PATCH 28/79] Localized file check-in by OneLocBuild Task: Build definition ID 14411: Build ID 14204987 (#25539) This is the pull request automatically created by the OneLocBuild task in the build process to check-in localized files generated based upon translation source files (.lcl files) handed-back from the downstream localization pipeline. If there are issues in translations, visit https://aka.ms/icxLocBug and log bugs for fixes. The OneLocBuild wiki is https://aka.ms/onelocbuild and the localization process in general is documented at https://aka.ms/AllAboutLoc. From eeb0a42d3781a9f19035d5816e67a9a166c97695 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Wed, 27 May 2026 10:01:06 -0700 Subject: [PATCH 29/79] Localized file check-in by OneLocBuild Task: Build definition ID 14411: Build ID 14204841 (#25537) This is the pull request automatically created by the OneLocBuild task in the build process to check-in localized files generated based upon translation source files (.lcl files) handed-back from the downstream localization pipeline. If there are issues in translations, visit https://aka.ms/icxLocBug and log bugs for fixes. The OneLocBuild wiki is https://aka.ms/onelocbuild and the localization process in general is documented at https://aka.ms/AllAboutLoc. From 1becd48487877965deb60e9748cc06579702e103 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 27 May 2026 19:07:25 +0200 Subject: [PATCH 30/79] [dotnet] Don't enable InlineClassGetHandle if we're using the static registrar as a custom trimmer step. (#25524) The InlineClassGetHandle step runs before marking, and the StaticRegistrar step runs after sweeping, which causes a problem when the InlineClassGetHandle step needs the registrar to run first. This won't be a problem once we've moved out of custom trimmer steps. --- dotnet/targets/Xamarin.Shared.Sdk.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/targets/Xamarin.Shared.Sdk.props b/dotnet/targets/Xamarin.Shared.Sdk.props index 532e7e2a44cb..ce84e27e899b 100644 --- a/dotnet/targets/Xamarin.Shared.Sdk.props +++ b/dotnet/targets/Xamarin.Shared.Sdk.props @@ -108,7 +108,7 @@ - + strict compatibility From 8eb8f4bead24fae55229f6e244c12175847a09cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 19:32:22 +0200 Subject: [PATCH 31/79] Bump actions/upload-artifact from 4.6.2 to 7.0.1 (#25550) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.2 to 7.0.1. --- .github/workflows/autoformat-v2.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/autoformat-v2.yml b/.github/workflows/autoformat-v2.yml index 695354c78d1a..4fb4d4a234df 100644 --- a/.github/workflows/autoformat-v2.yml +++ b/.github/workflows/autoformat-v2.yml @@ -91,7 +91,7 @@ jobs: - name: 'Upload patch' if: steps.patch.outputs.changes == 'true' - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: autoformat path: autoformat-output/ From 16053556bda02e8bb8a8bd14e4f26055a0ce6e79 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 27 May 2026 20:46:06 +0200 Subject: [PATCH 32/79] [runtime] Show the error code if calling coreclr_initialize fails. (#25544) --- runtime/coreclr-bridge.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/runtime/coreclr-bridge.m b/runtime/coreclr-bridge.m index e55c125461c8..1368766a6040 100644 --- a/runtime/coreclr-bridge.m +++ b/runtime/coreclr-bridge.m @@ -507,6 +507,10 @@ LOG_CORECLR (stderr, "xamarin_vm_initialize (%i, %p, %p): rv: %i domainId: %i handle: %p\n", combinedPropertyCount, combinedPropertyKeys, combinedPropertyValues, rv, coreclr_domainId, coreclr_handle); + if (rv != 0) { + LOG (PRODUCT ": The call to 'coreclr_initialize' failed: %i (%p)\n", rv, rv); + } + return rv == 0; } #endif // !defined (NATIVEAOT) From cad4a4b8c378c10ae47a51c0dac4a3577a8d45c5 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 27 May 2026 20:48:01 +0200 Subject: [PATCH 33/79] [github] Fix workflows to pin actions & drop credentials when possible. (#25545) --- .github/workflows/copilot-setup-steps.yml | 4 +++- .github/workflows/inter-branch-merge-flow.yml | 2 +- .github/workflows/update-single-platform-branches.yml | 3 ++- .github/workflows/yamllint.yml | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index f4841a250f76..451d17b79bd5 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -19,7 +19,9 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Install gh-aw extension uses: github/gh-aw-actions/setup-cli@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 with: diff --git a/.github/workflows/inter-branch-merge-flow.yml b/.github/workflows/inter-branch-merge-flow.yml index b6b979ddad46..7c95ddd17a32 100644 --- a/.github/workflows/inter-branch-merge-flow.yml +++ b/.github/workflows/inter-branch-merge-flow.yml @@ -30,4 +30,4 @@ permissions: jobs: Merge: - uses: dotnet/arcade/.github/workflows/inter-branch-merge-base.yml@main + uses: dotnet/arcade/.github/workflows/inter-branch-merge-base.yml@b36a3594d11b8d56bf7d3dbce919e0688715d5ec # main diff --git a/.github/workflows/update-single-platform-branches.yml b/.github/workflows/update-single-platform-branches.yml index 0944fe689a68..bb0e62bcf29d 100644 --- a/.github/workflows/update-single-platform-branches.yml +++ b/.github/workflows/update-single-platform-branches.yml @@ -19,9 +19,10 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 + persist-credentials: true # need to push changes - name: 'Update branches' run: | diff --git a/.github/workflows/yamllint.yml b/.github/workflows/yamllint.yml index d2fa268c38a3..ccef5822206a 100644 --- a/.github/workflows/yamllint.yml +++ b/.github/workflows/yamllint.yml @@ -13,10 +13,11 @@ jobs: steps: - name: 'Checkout' - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: token: ${{ secrets.GITHUB_TOKEN }} fetch-depth: 0 + persist-credentials: false - name: Install yamllint run: pip install yamllint From b2159abc9e08563146d184abcbd23269359c1492 Mon Sep 17 00:00:00 2001 From: Alex Soto Date: Thu, 28 May 2026 10:01:22 -0400 Subject: [PATCH 34/79] [CI] Add retry count on task failure for AzureCLI step in provision.yml (#25565) Tries to mitigate: https://devdiv.visualstudio.com/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_apis/build/builds/14219070/logs/948 --- tools/devops/automation/templates/common/provision.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/devops/automation/templates/common/provision.yml b/tools/devops/automation/templates/common/provision.yml index 6424ffd2103d..b60e12e2fb84 100644 --- a/tools/devops/automation/templates/common/provision.yml +++ b/tools/devops/automation/templates/common/provision.yml @@ -35,6 +35,7 @@ steps: - task: AzureCLI@2 displayName: 'Generate BosStorageMirror SAS tokens' condition: and(succeeded(), ${{ parameters.enabled }}) + retryCountOnTaskFailure: 3 inputs: azureSubscription: 'Xamarin - RelEng (BosStorageMirror-Contributor-MI)' scriptType: 'bash' From 52b5249e851eb98a46a6c7dbbd6b7b0d1f33a3ef Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 28 May 2026 16:52:21 +0200 Subject: [PATCH 35/79] [github] Add zizmor GitHub Actions security linter workflow (#25560) Fixes https://github.com/dotnet/macios/issues/25496. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/zizmor.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/zizmor.yml diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 000000000000..a27c501d6710 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,34 @@ +name: zizmor 🌈 + +on: + push: + branches: ["main"] + pull_request: + branches: ["**"] + +permissions: {} + +jobs: + zizmor: + name: zizmor 🌈 + runs-on: ubuntu-latest + permissions: + security-events: write + contents: read + actions: read + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + - name: Find workflow files + id: find-files + run: | + files=$(find .github/workflows -name '*.yml' ! -name '*.lock.yml' | sort | tr '\n' ' ') + echo "files=$files" >> "$GITHUB_OUTPUT" + + - name: Run zizmor + uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6 + with: + inputs: ${{ steps.find-files.outputs.files }} From a584d6e224930279d9a4c0bc98a3a7a019348853 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 28 May 2026 12:01:43 -0400 Subject: [PATCH 36/79] [github] Fix Code Radiator: prevent upstream unsetting that breaks safeoutputs PR creation. Fixes #25555. (#25563) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Code Radiator workflow consistently failed with `"No changes to commit - no commits found"` from `safeoutputs create_pull_request` despite the agent having verified commits via `git log origin/base..HEAD`. ## Root Cause The safeoutputs `create_pull_request` tool detects pushable commits using `@{upstream}..HEAD`. The agent was unsetting the upstream tracking branch after creating the merge branch (to avoid accidentally pushing to the target branch via plain `git push`), which caused the tool to find no commits. The manual `git push origin ""` instruction was also broken — the agent job only has `contents: read` permission. ## Changes (`code-radiator.md`) - **Remove manual `git push` instruction** — the agent cannot push (read-only token); safeoutputs handles all pushing - **Explicit `do NOT unset upstream` warning** — explains that `@{upstream}` is how the tool detects commits; unsetting it silently breaks PR creation - **Require `base` parameter** when calling `safeoutputs create_pull_request` as a backup reference for commit detection - **Update Important Notes** — replace ambiguous "use `gh` CLI for PR operations" with clear guidance to use safeoutputs for push/create and `gh`/MCP tools only for non-push operations Fixes https://github.com/dotnet/macios/issues/25555. --------- Co-authored-by: rolfbjarne <249268+rolfbjarne@users.noreply.github.com> --- .github/aw/actions-lock.json | 6 +- .github/workflows/code-radiator.lock.yml | 149 +++++++++++++---------- .github/workflows/code-radiator.md | 31 ++--- 3 files changed, 103 insertions(+), 83 deletions(-) diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json index 06276c7f1e4c..25662b0eea48 100644 --- a/.github/aw/actions-lock.json +++ b/.github/aw/actions-lock.json @@ -25,10 +25,10 @@ "version": "v7.0.1", "sha": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" }, - "github/gh-aw-actions/setup@v0.74.8": { + "github/gh-aw-actions/setup@v0.76.1": { "repo": "github/gh-aw-actions/setup", - "version": "v0.74.8", - "sha": "efa55847f72aadb03490d955263ff911bf758700" + "version": "v0.76.1", + "sha": "46d564922b082d0db93244972e8005ea6904ee5f" } }, "containers": { diff --git a/.github/workflows/code-radiator.lock.yml b/.github/workflows/code-radiator.lock.yml index 3db0cc9188d5..abcb3b886b9c 100644 --- a/.github/workflows/code-radiator.lock.yml +++ b/.github/workflows/code-radiator.lock.yml @@ -1,5 +1,5 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"c4be7d8ac2692259a94c4639bb109ba34737aa41d51fcd6cc2948df40f5a1bb4","compiler_version":"v0.74.8","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.5"} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"efa55847f72aadb03490d955263ff911bf758700","version":"v0.74.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.49"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.9","digest":"sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"abaf2819115d39131e4028d6937c23ce759047f7a526bee9eebb76b928ca1e0c","compiler_version":"v0.76.1","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.5"} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"46d564922b082d0db93244972e8005ea6904ee5f","version":"v0.76.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.55"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.55"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.19"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4","digest":"sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4"},{"image":"node:lts-alpine","digest":"sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14","pinned_image":"node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14"}]} # ___ _ _ # / _ \ | | (_) # | |_| | __ _ ___ _ __ | |_ _ ___ @@ -14,7 +14,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.74.8). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.76.1). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -36,15 +36,15 @@ # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 # - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 -# - github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 +# - github/gh-aw-actions/setup@46d564922b082d0db93244972e8005ea6904ee5f # v0.76.1 # # Container images used: -# - ghcr.io/github/gh-aw-firewall/agent:0.25.49 -# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49 -# - ghcr.io/github/gh-aw-firewall/squid:0.25.49 -# - ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388 -# - ghcr.io/github/github-mcp-server:v1.0.4 -# - node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f +# - ghcr.io/github/gh-aw-firewall/agent:0.25.55 +# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55 +# - ghcr.io/github/gh-aw-firewall/squid:0.25.55 +# - ghcr.io/github/gh-aw-mcpg:v0.3.19 +# - ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4 +# - node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14 name: "Code Radiator" on: @@ -91,14 +91,15 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 + uses: github/gh-aw-actions/setup@46d564922b082d0db93244972e8005ea6904ee5f # v0.76.1 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} env: GH_AW_SETUP_WORKFLOW_NAME: "Code Radiator" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/code-radiator.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_VERSION: "1.0.52" + GH_AW_INFO_AWF_VERSION: "v0.25.55" GH_AW_INFO_ENGINE_ID: "copilot" - name: Generate agentic run info id: generate_aw_info @@ -106,16 +107,16 @@ jobs: GH_AW_INFO_ENGINE_ID: "copilot" GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" GH_AW_INFO_MODEL: "claude-sonnet-4.5" - GH_AW_INFO_VERSION: "1.0.48" - GH_AW_INFO_AGENT_VERSION: "1.0.48" - GH_AW_INFO_CLI_VERSION: "v0.74.8" + GH_AW_INFO_VERSION: "1.0.52" + GH_AW_INFO_AGENT_VERSION: "1.0.52" + GH_AW_INFO_CLI_VERSION: "v0.76.1" GH_AW_INFO_WORKFLOW_NAME: "Code Radiator" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" GH_AW_INFO_STAGED: "false" GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]' GH_AW_INFO_FIREWALL_ENABLED: "true" - GH_AW_INFO_AWF_VERSION: "v0.25.49" + GH_AW_INFO_AWF_VERSION: "v0.25.55" GH_AW_INFO_AWMG_VERSION: "" GH_AW_INFO_FIREWALL_TYPE: "squid" GH_AW_COMPILED_STRICT: "true" @@ -138,6 +139,7 @@ jobs: sparse-checkout: | .github .agents + .antigravity .claude .codex .crush @@ -148,8 +150,8 @@ jobs: fetch-depth: 1 - name: Save agent config folders for base branch restoration env: - GH_AW_AGENT_FOLDERS: ".agents .claude .codex .crush .gemini .github .opencode .pi" - GH_AW_AGENT_FILES: ".crush.json AGENTS.md CLAUDE.md GEMINI.md PI.md opencode.jsonc" + GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi" + GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc" # poutine:ignore untrusted_checkout_exec run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh" - name: Check workflow lock file @@ -167,7 +169,7 @@ jobs: - name: Check compile-agentic version uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: - GH_AW_COMPILED_VERSION: "v0.74.8" + GH_AW_COMPILED_VERSION: "v0.76.1" with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); @@ -190,24 +192,24 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_f2376eda3a4bbca0_EOF' + cat << 'GH_AW_PROMPT_87b0926ff2940306_EOF' - GH_AW_PROMPT_f2376eda3a4bbca0_EOF + GH_AW_PROMPT_87b0926ff2940306_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_f2376eda3a4bbca0_EOF' + cat << 'GH_AW_PROMPT_87b0926ff2940306_EOF' Tools: add_comment(max:10), create_pull_request(max:10), update_pull_request(max:10), add_labels(max:10), push_to_pull_request_branch(max:10), missing_tool, missing_data, noop - GH_AW_PROMPT_f2376eda3a4bbca0_EOF + GH_AW_PROMPT_87b0926ff2940306_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_push_to_pr_branch.md" - cat << 'GH_AW_PROMPT_f2376eda3a4bbca0_EOF' + cat << 'GH_AW_PROMPT_87b0926ff2940306_EOF' - GH_AW_PROMPT_f2376eda3a4bbca0_EOF + GH_AW_PROMPT_87b0926ff2940306_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_f2376eda3a4bbca0_EOF' + cat << 'GH_AW_PROMPT_87b0926ff2940306_EOF' The following GitHub context information is available for this workflow: {{#if github.actor}} @@ -239,12 +241,12 @@ jobs: - **Note**: If a branch you need is not in the list above and is not listed as an additional fetched ref, it has NOT been checked out. For private repositories you cannot fetch it without proper authentication. If the branch is required and not available, exit with an error and ask the user to add it to the `fetch:` option of the `checkout:` configuration (e.g., `fetch: ["refs/pulls/open/*"]` for all open PR refs, or `fetch: ["main", "feature/my-branch"]` for specific branches). - GH_AW_PROMPT_f2376eda3a4bbca0_EOF + GH_AW_PROMPT_87b0926ff2940306_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_f2376eda3a4bbca0_EOF' + cat << 'GH_AW_PROMPT_87b0926ff2940306_EOF' {{#runtime-import .github/workflows/code-radiator.md}} - GH_AW_PROMPT_f2376eda3a4bbca0_EOF + GH_AW_PROMPT_87b0926ff2940306_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -316,6 +318,7 @@ jobs: /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/base /tmp/gh-aw/.github/agents + /tmp/gh-aw/.github/skills if-no-files-found: ignore retention-days: 1 @@ -335,15 +338,15 @@ jobs: GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs GH_AW_WORKFLOW_ID_SANITIZED: coderadiator outputs: - agentic_engine_timeout: ${{ steps.detect-copilot-errors.outputs.agentic_engine_timeout || 'false' }} + agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }} effective_tokens_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.effective_tokens_rate_limit_error || 'false' }} has_patch: ${{ steps.collect_output.outputs.has_patch }} - inference_access_error: ${{ steps.detect-copilot-errors.outputs.inference_access_error || 'false' }} - mcp_policy_error: ${{ steps.detect-copilot-errors.outputs.mcp_policy_error || 'false' }} + inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }} + mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }} model: ${{ needs.activation.outputs.model }} - model_not_supported_error: ${{ steps.detect-copilot-errors.outputs.model_not_supported_error || 'false' }} + model_not_supported_error: ${{ steps.detect-agent-errors.outputs.model_not_supported_error || 'false' }} output: ${{ steps.collect_output.outputs.output }} output_types: ${{ steps.collect_output.outputs.output_types }} setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} @@ -352,7 +355,7 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 + uses: github/gh-aw-actions/setup@46d564922b082d0db93244972e8005ea6904ee5f # v0.76.1 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -361,7 +364,8 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Code Radiator" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/code-radiator.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_VERSION: "1.0.52" + GH_AW_INFO_AWF_VERSION: "v0.25.55" GH_AW_INFO_ENGINE_ID: "copilot" - name: Set runtime paths id: set-runtime-paths @@ -416,11 +420,11 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); await main(); - name: Install GitHub Copilot CLI - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.48 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.52 env: GH_HOST: github.com - name: Install AWF binary - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.49 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.55 - name: Parse integrity filter lists id: parse-guard-vars env: @@ -436,24 +440,28 @@ jobs: - name: Restore agent config folders from base branch if: steps.checkout-pr.outcome == 'success' env: - GH_AW_AGENT_FOLDERS: ".agents .claude .codex .crush .gemini .github .opencode .pi" - GH_AW_AGENT_FILES: ".crush.json AGENTS.md CLAUDE.md GEMINI.md PI.md opencode.jsonc" + GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi" + GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc" run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh" - name: Restore inline sub-agents from activation artifact env: GH_AW_SUB_AGENT_DIR: ".github/agents" GH_AW_SUB_AGENT_EXT: ".agent.md" run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh" + - name: Restore inline skills from activation artifact + env: + GH_AW_SKILL_DIR: ".github/skills" + run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh" - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.49 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49 ghcr.io/github/gh-aw-firewall/squid:0.25.49 ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388 ghcr.io/github/github-mcp-server:v1.0.4 node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.55 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55 ghcr.io/github/gh-aw-firewall/squid:0.25.55 ghcr.io/github/gh-aw-mcpg:v0.3.19 ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4 node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14 - name: Generate Safe Outputs Config run: | mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_c85768df61d14c26_EOF' - {"add_comment":{"max":10,"target":"*"},"add_labels":{"max":10,"target":"*"},"create_pull_request":{"allowed_base_branches":["net*.0","xcode*","xcode*.*"],"max":10,"max_patch_files":100,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"]},"create_report_incomplete_issue":{},"merge_pull_request":{"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_to_pull_request_branch":{"if_no_changes":"warn","max":10,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"target":"*","title_prefix":"🤖 Merge 'main' =\u003e '"},"report_incomplete":{},"update_pull_request":{"allow_body":true,"allow_title":true,"max":10,"update_branch":false}} - GH_AW_SAFE_OUTPUTS_CONFIG_c85768df61d14c26_EOF + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_b50ec395f48eecb1_EOF' + {"add_comment":{"max":10,"target":"*"},"add_labels":{"max":10,"target":"*"},"create_pull_request":{"allowed_base_branches":["net*.0","xcode*","xcode*.*"],"max":10,"max_patch_files":100,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"protected_files_policy":"request_review"},"create_report_incomplete_issue":{},"merge_pull_request":{"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_to_pull_request_branch":{"if_no_changes":"warn","max":10,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"target":"*","title_prefix":"🤖 Merge 'main' =\u003e '"},"report_incomplete":{},"update_pull_request":{"allow_body":true,"allow_title":true,"max":10,"update_branch":false}} + GH_AW_SAFE_OUTPUTS_CONFIG_b50ec395f48eecb1_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -792,11 +800,11 @@ jobs: * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;; esac DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0') - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.9' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.19' mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_be302d958a95e8cb_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_76afe8ad36364972_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -840,7 +848,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_be302d958a95e8cb_EOF + GH_AW_MCP_CONFIG_76afe8ad36364972_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -874,7 +882,7 @@ jobs: export GH_AW_NODE_BIN export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" (umask 177 && touch /tmp/gh-aw/agent-stdio.log) - printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.49/awf-config.schema.json","network":{"allowDomains":["*.githubusercontent.com","api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","codeload.github.com","crl.geotrust.com","crl.globalsign.com","crl.identrust.com","crl.sectigo.com","crl.thawte.com","crl.usertrust.com","crl.verisign.com","crl3.digicert.com","crl4.digicert.com","crls.ssl.com","docs.github.com","github-cloud.githubusercontent.com","github-cloud.s3.amazonaws.com","github.blog","github.com","github.githubassets.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.com","lfs.github.com","objects.githubusercontent.com","ocsp.digicert.com","ocsp.geotrust.com","ocsp.globalsign.com","ocsp.identrust.com","ocsp.sectigo.com","ocsp.ssl.com","ocsp.thawte.com","ocsp.usertrust.com","ocsp.verisign.com","packagecloud.io","packages.cloud.google.com","packages.microsoft.com","patch-diff.githubusercontent.com","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000,"models":{"agent":["sonnet-6x","gpt-5.4","gpt-5","gemini-pro","haiku","any"],"any":["copilot/*","anthropic/*","openai/*","google/*","gemini/*"],"auto":["large"],"claude":["agent","sonnet-6x","haiku","any"],"codex":["agent","gpt-5-codex","gpt-5","any"],"coding":["copilot/gpt-5*codex*","openai/gpt-5*codex*","gpt-5-codex"],"copilot":["agent","gpt-5.4","sonnet","gpt-5","any"],"deep-research":["copilot/deep-research*","copilot/o3-deep-research*","copilot/o4-mini-deep-research*","google/deep-research*","gemini/deep-research*","openai/o3-deep-research*","openai/o4-mini-deep-research*"],"gemini":["agent","gemini-pro","gemini-flash","any"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*","gemini/gemini-*flash*"],"gemini-flash-lite":["copilot/gemini-*flash*lite*","google/gemini-*flash*lite*","gemini/gemini-*flash*lite*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*","gemini/gemini-*pro*"],"gemma":["copilot/gemma*","google/gemma*","gemini/gemma*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash-lite","copilot/raptor*mini*"],"opus":["copilot/*opus*","anthropic/*opus*"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"],"sonnet-6x":["copilot/*sonnet-4.5*","copilot/*sonnet-4-5*","anthropic/*sonnet-4.5*","anthropic/*sonnet-4-5*","copilot/*sonnet-3.7*","copilot/*sonnet-3-7*","anthropic/*sonnet-3.7*","anthropic/*sonnet-3-7*","copilot/*sonnet-3.5*","copilot/*sonnet-3-5*","anthropic/*sonnet-3.5*","anthropic/*sonnet-3-5*"],"vision":["copilot/gemini-*image*","gemini/gemini-*image*","copilot/gemini-*flash*","gemini/gemini-*flash*"]}},"container":{"imageTag":"0.25.49"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" + printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.55/awf-config.schema.json","network":{"allowDomains":["*.githubusercontent.com","api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","codeload.github.com","crl.geotrust.com","crl.globalsign.com","crl.identrust.com","crl.sectigo.com","crl.thawte.com","crl.usertrust.com","crl.verisign.com","crl3.digicert.com","crl4.digicert.com","crls.ssl.com","docs.github.com","github-cloud.githubusercontent.com","github-cloud.s3.amazonaws.com","github.blog","github.com","github.githubassets.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.com","lfs.github.com","objects.githubusercontent.com","ocsp.digicert.com","ocsp.geotrust.com","ocsp.globalsign.com","ocsp.identrust.com","ocsp.sectigo.com","ocsp.ssl.com","ocsp.thawte.com","ocsp.usertrust.com","ocsp.verisign.com","packagecloud.io","packages.cloud.google.com","packages.microsoft.com","patch-diff.githubusercontent.com","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000,"models":{"agent":["sonnet-6x","gpt-5.4","gpt-5.3","gemini-pro","any"],"antigravity":["copilot/antigravity*","google/antigravity*","gemini/antigravity*"],"any":["copilot/*","anthropic/*","openai/*","google/*","gemini/*"],"claude":["agent"],"codex":["agent"],"coding":["copilot/gpt-5*codex*","openai/gpt-5*codex*","gpt-5-codex"],"computer-use":["copilot/*computer-use*","google/*computer-use*","gemini/*computer-use*","openai/*computer-use*"],"copilot":["agent"],"deep-research":["copilot/deep-research*","copilot/o3-deep-research*","copilot/o4-mini-deep-research*","google/deep-research*","gemini/deep-research*","openai/o3-deep-research*","openai/o4-mini-deep-research*"],"gemini":["agent"],"gemini-3-flash":["copilot/gemini-3*flash*","google/gemini-3*flash*","gemini/gemini-3*flash*"],"gemini-3-pro":["copilot/gemini-3*pro*","google/gemini-3*pro*","gemini/gemini-3*pro*"],"gemini-3.1-flash":["copilot/gemini-3.1*flash*","google/gemini-3.1*flash*","gemini/gemini-3.1*flash*"],"gemini-3.1-pro":["copilot/gemini-3.1*pro*","google/gemini-3.1*pro*","gemini/gemini-3.1*pro*"],"gemini-3.5-flash":["copilot/gemini-3.5*flash*","google/gemini-3.5*flash*","gemini/gemini-3.5*flash*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*","gemini/gemini-*flash*"],"gemini-flash-lite":["copilot/gemini-*flash*lite*","google/gemini-*flash*lite*","gemini/gemini-*flash*lite*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*","gemini/gemini-*pro*"],"gemma":["copilot/gemma*","google/gemma*","gemini/gemma*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"gpt-5.2":["copilot/gpt-5.2*","openai/gpt-5.2*"],"gpt-5.3":["copilot/gpt-5.3*","openai/gpt-5.3*"],"gpt-5.4":["copilot/gpt-5.4*","openai/gpt-5.4*"],"gpt-5.5":["copilot/gpt-5.5*","openai/gpt-5.5*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash-lite"],"opus":["copilot/*opus*","anthropic/*opus*"],"opusplan":["opus?effort=high"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"robotics":["copilot/*robotics*","google/*robotics*","gemini/*robotics*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"],"sonnet-6x":["copilot/*sonnet-4-5-*","anthropic/*sonnet-4-5-*","copilot/*sonnet-4-6*","anthropic/*sonnet-4-6*"],"summarization":["haiku","gpt-5-mini","gemini-flash-lite","mini"],"vision":["copilot/gemini-*image*","gemini/gemini-*image*","copilot/gemini-*flash*","gemini/gemini-*flash*"]}},"container":{"imageTag":"0.25.55"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="" if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then @@ -893,7 +901,7 @@ jobs: GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.74.8 + GH_AW_VERSION: v0.76.1 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows @@ -908,11 +916,11 @@ jobs: GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com GIT_COMMITTER_NAME: github-actions[bot] XDG_CONFIG_HOME: /home/runner - - name: Detect Copilot errors - id: detect-copilot-errors + - name: Detect agent errors if: always() + id: detect-agent-errors continue-on-error: true - run: node "${RUNNER_TEMP}/gh-aw/actions/detect_copilot_errors.cjs" + run: node "${RUNNER_TEMP}/gh-aw/actions/detect_agent_errors.cjs" - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} @@ -1096,7 +1104,7 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 + uses: github/gh-aw-actions/setup@46d564922b082d0db93244972e8005ea6904ee5f # v0.76.1 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -1105,7 +1113,8 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Code Radiator" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/code-radiator.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_VERSION: "1.0.52" + GH_AW_INFO_AWF_VERSION: "v0.25.55" GH_AW_INFO_ENGINE_ID: "copilot" - name: Download agent output artifact id: download-agent-output @@ -1128,6 +1137,7 @@ jobs: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} GH_AW_NOOP_MAX: "1" GH_AW_WORKFLOW_NAME: "Code Radiator" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/code-radiator.md" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} GH_AW_NOOP_REPORT_AS_ISSUE: "true" @@ -1144,6 +1154,7 @@ jobs: env: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} GH_AW_WORKFLOW_NAME: "Code Radiator" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/code-radiator.md" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} @@ -1161,6 +1172,7 @@ jobs: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} GH_AW_MISSING_TOOL_CREATE_ISSUE: "true" GH_AW_WORKFLOW_NAME: "Code Radiator" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/code-radiator.md" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | @@ -1175,6 +1187,7 @@ jobs: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true" GH_AW_WORKFLOW_NAME: "Code Radiator" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/code-radiator.md" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | @@ -1189,6 +1202,7 @@ jobs: env: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} GH_AW_WORKFLOW_NAME: "Code Radiator" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/code-radiator.md" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} GH_AW_WORKFLOW_ID: "code-radiator" @@ -1237,7 +1251,7 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 + uses: github/gh-aw-actions/setup@46d564922b082d0db93244972e8005ea6904ee5f # v0.76.1 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -1246,7 +1260,8 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Code Radiator" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/code-radiator.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_VERSION: "1.0.52" + GH_AW_INFO_AWF_VERSION: "v0.25.55" GH_AW_INFO_ENGINE_ID: "copilot" - name: Download agent output artifact id: download-agent-output @@ -1273,7 +1288,7 @@ jobs: rm -rf /tmp/gh-aw/sandbox/firewall/logs rm -rf /tmp/gh-aw/sandbox/firewall/audit - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.49 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49 ghcr.io/github/gh-aw-firewall/squid:0.25.49 + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.55 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55 ghcr.io/github/gh-aw-firewall/squid:0.25.55 - name: Check if detection needed id: detection_guard if: always() @@ -1332,11 +1347,11 @@ jobs: node-version: '24' package-manager-cache: false - name: Install GitHub Copilot CLI - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.48 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.52 env: GH_HOST: github.com - name: Install AWF binary - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.49 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.55 - name: Execute GitHub Copilot CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' continue-on-error: true @@ -1351,7 +1366,7 @@ jobs: export GH_AW_NODE_BIN export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log) - printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.49/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","host.docker.internal","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000},"container":{"imageTag":"0.25.49"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" + printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.55/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","host.docker.internal","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000},"container":{"imageTag":"0.25.55"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="" if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then @@ -1368,7 +1383,7 @@ jobs: COPILOT_MODEL: claude-sonnet-4.5 GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.74.8 + GH_AW_VERSION: v0.76.1 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows @@ -1442,9 +1457,10 @@ jobs: GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }} GH_AW_ENGINE_ID: "copilot" GH_AW_ENGINE_MODEL: "claude-sonnet-4.5" - GH_AW_ENGINE_VERSION: "1.0.48" + GH_AW_ENGINE_VERSION: "1.0.52" GH_AW_WORKFLOW_ID: "code-radiator" GH_AW_WORKFLOW_NAME: "Code Radiator" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/code-radiator.md" outputs: code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} @@ -1461,7 +1477,7 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@efa55847f72aadb03490d955263ff911bf758700 # v0.74.8 + uses: github/gh-aw-actions/setup@46d564922b082d0db93244972e8005ea6904ee5f # v0.76.1 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -1470,7 +1486,8 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Code Radiator" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/code-radiator.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.48" + GH_AW_INFO_VERSION: "1.0.52" + GH_AW_INFO_AWF_VERSION: "v0.25.55" GH_AW_INFO_ENGINE_ID: "copilot" - name: Download agent output artifact id: download-agent-output @@ -1522,7 +1539,7 @@ jobs: ref: ${{ github.event.repository.default_branch }} token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} persist-credentials: false - fetch-depth: 1 + fetch-depth: 0 - name: Checkout repository if: ((!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') || (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) && github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -1530,7 +1547,7 @@ jobs: ref: ${{ steps.extract-base-branch.outputs.base-branch || github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }} token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} persist-credentials: false - fetch-depth: 1 + fetch-depth: 0 - name: Configure Git credentials if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') || (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch') env: @@ -1563,7 +1580,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10,\"target\":\"*\"},\"add_labels\":{\"max\":10,\"target\":\"*\"},\"create_pull_request\":{\"allowed_base_branches\":[\"net*.0\",\"xcode*\",\"xcode*.*\"],\"max\":10,\"max_patch_files\":100,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"]},\"create_report_incomplete_issue\":{},\"merge_pull_request\":{\"max\":10},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max\":10,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"target\":\"*\",\"title_prefix\":\"🤖 Merge 'main' =\\u003e '\"},\"report_incomplete\":{},\"update_pull_request\":{\"allow_body\":true,\"allow_title\":true,\"max\":10,\"update_branch\":false}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10,\"target\":\"*\"},\"add_labels\":{\"max\":10,\"target\":\"*\"},\"create_pull_request\":{\"allowed_base_branches\":[\"net*.0\",\"xcode*\",\"xcode*.*\"],\"max\":10,\"max_patch_files\":100,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"protected_files_policy\":\"request_review\"},\"create_report_incomplete_issue\":{},\"merge_pull_request\":{\"max\":10},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max\":10,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"target\":\"*\",\"title_prefix\":\"🤖 Merge 'main' =\\u003e '\"},\"report_incomplete\":{},\"update_pull_request\":{\"allow_body\":true,\"allow_title\":true,\"max\":10,\"update_branch\":false}}" GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }} with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/code-radiator.md b/.github/workflows/code-radiator.md index 496e72c3da55..8618affe282c 100644 --- a/.github/workflows/code-radiator.md +++ b/.github/workflows/code-radiator.md @@ -42,7 +42,7 @@ safe-outputs: push-to-pull-request-branch: max: 10 target: "*" - title-prefix: "🤖 Merge 'main' => '" + required-title-prefix: "🤖 Merge 'main' => '" update-pull-request: max: 10 --- @@ -137,19 +137,19 @@ If there are merge conflicts: - Add the `do-not-merge` label. - Add a comment requesting human review of the conflict resolution, listing which files were manually resolved. -#### e. Push and create/update the PR +#### e. Create or update the PR -```bash -git push origin "" -``` +Do **NOT** run `git push` manually. The safeoutputs tool handles pushing. + +Use the `create_pull_request` safeoutput tool to push the branch and create/update the PR: +- `branch`: `` (e.g., `merge/main-to-net11.0-20260527`) +- `base`: `` (e.g., `net11.0`) — **always provide this field** +- `title`: `🤖 Merge 'main' => ''` +- `body`: `Automated merge of \`main\` into \`\`.\n\nCreated by the code-radiator workflow.` + +> **Important**: Do NOT unset the upstream tracking branch. After `git checkout -B "" "origin/"`, the upstream is set to `origin/`. Keep it set — the safeoutputs tool relies on this to detect the commits to push. -- If updating an existing PR: the push is sufficient (the PR updates automatically). -- If creating a new PR: - - Title: `🤖 Merge 'main' => ''` - - Body: `Automated merge of \`main\` into \`\`.\n\nCreated by the code-radiator workflow.` - - Base: the target branch - - Head: the local branch - - Enable automerge (merge strategy) on the new PR. +After creating the PR, enable automerge (merge strategy) using the GitHub MCP `enable_auto_merge` tool or `gh pr merge --auto --merge`. ### 3. Summary @@ -178,7 +178,10 @@ Split version strings on `.`, `-`, and `+`. Compare each segment numerically. Ex ## Important Notes -- Never force push. Always use regular `git push`. +- Never force push. +- Do NOT run `git push` manually — the safeoutputs `create_pull_request` tool handles pushing. +- Do NOT unset the upstream tracking branch after creating the local branch. The safeoutputs tool uses `@{upstream}` to detect commits that need to be pushed. Unsetting the upstream causes the tool to report "No changes to commit - no commits found" even when commits exist. +- Always provide the `base` branch when calling `safeoutputs create_pull_request` (e.g., `base: "net11.0"`). - The workflow operates on the current repository checkout. - Run `git fetch origin` before starting to ensure up-to-date remote refs. -- Use `gh` CLI for PR operations (create, comment, list, merge --auto). +- Use the GitHub MCP tools or `gh` CLI for non-push PR operations (comment, list, merge --auto, enable automerge). From 05a09417b3640eab1aa6012293e7aa95f3feafa3 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 29 May 2026 12:48:52 +0000 Subject: [PATCH 37/79] [main] Update dependencies from dotnet/dotnet (#25557) This pull request updates the following dependencies ## From https://github.com/dotnet/dotnet - **Subscription**: [da09b56a-0fb1-439a-b894-def14d2ec0a4](https://maestro.dot.net/subscriptions?search=da09b56a-0fb1-439a-b894-def14d2ec0a4) - **Build**: [20260527.9](https://dev.azure.com/dnceng/internal/_build/results?buildId=2985462) ([316084](https://maestro.dot.net/channel/10307/github:dotnet:dotnet/build/316084)) - **Date Produced**: May 27, 2026 7:32:35 PM UTC - **Commit**: [2f68b99e483cd8f4acbc1aa365fc806f1bd2374b](https://github.com/dotnet/dotnet/commit/2f68b99e483cd8f4acbc1aa365fc806f1bd2374b) - **Branch**: [release/10.0.4xx](https://github.com/dotnet/dotnet/tree/release/10.0.4xx) - **Dependency Updates**: - From [10.0.0-beta.26276.120 to 10.0.0-beta.26277.109][1] - Microsoft.DotNet.Arcade.Sdk - Microsoft.DotNet.Build.Tasks.Feed - Microsoft.DotNet.SharedFramework.Sdk - From [10.0.400-preview.0.26276.120 to 10.0.400-preview.0.26277.109][1] - Microsoft.NET.Sdk - From [10.0.400-preview.26276.120 to 10.0.400-preview.26277.109][1] - Microsoft.TemplateEngine.Authoring.Tasks [1]: https://github.com/dotnet/dotnet/compare/c9b6c9515f...2f68b99e48 --- eng/Version.Details.props | 10 +++++----- eng/Version.Details.xml | 20 ++++++++++---------- global.json | 6 +++--- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 7cda8864708c..5277e7173d29 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -6,16 +6,16 @@ This file should be imported by eng/Versions.props - 10.0.0-beta.26276.120 - 10.0.0-beta.26276.120 + 10.0.0-beta.26277.109 + 10.0.0-beta.26277.109 0.11.5-alpha.26070.104 - 10.0.0-beta.26276.120 + 10.0.0-beta.26277.109 10.0.3-servicing.26070.104 10.0.3 10.0.3 - 10.0.400-preview.0.26276.120 + 10.0.400-preview.0.26277.109 10.0.3 - 10.0.400-preview.26276.120 + 10.0.400-preview.26277.109 26.0.11017 18.5.9227 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index f875d42c06e2..e53abe16f371 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,8 +1,8 @@ - + https://github.com/dotnet/dotnet - c9b6c9515ff95716e797906a3eff7179baa272b2 + 2f68b99e483cd8f4acbc1aa365fc806f1bd2374b https://github.com/dotnet/dotnet @@ -95,25 +95,25 @@ - + https://github.com/dotnet/dotnet - c9b6c9515ff95716e797906a3eff7179baa272b2 + 2f68b99e483cd8f4acbc1aa365fc806f1bd2374b - + https://github.com/dotnet/dotnet - c9b6c9515ff95716e797906a3eff7179baa272b2 + 2f68b99e483cd8f4acbc1aa365fc806f1bd2374b - + https://github.com/dotnet/dotnet - c9b6c9515ff95716e797906a3eff7179baa272b2 + 2f68b99e483cd8f4acbc1aa365fc806f1bd2374b https://github.com/dotnet/xharness 51ca379106cfd749a498cb0822210ef1aa926e41 - + https://github.com/dotnet/dotnet - c9b6c9515ff95716e797906a3eff7179baa272b2 + 2f68b99e483cd8f4acbc1aa365fc806f1bd2374b diff --git a/global.json b/global.json index e96f69ff8e58..294c78cab356 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "10.0.400-preview.0.26276.120", + "version": "10.0.400-preview.0.26277.109", "paths": [ "builds/downloads/dotnet", "$host$" @@ -8,9 +8,9 @@ "errorMessage": "The .NET SDK could not be found, please run 'make dotnet -C builds'." }, "tools": { - "dotnet": "10.0.400-preview.0.26276.120" + "dotnet": "10.0.400-preview.0.26277.109" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26276.120" + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26277.109" } } From 32352a1a9f0026a9ee69130534d18964350bc723 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 29 May 2026 14:54:11 +0200 Subject: [PATCH 38/79] [tools] Make csproj.inc generation parallel-safe. (#25547) --- tools/common/create-makefile-fragment.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tools/common/create-makefile-fragment.sh b/tools/common/create-makefile-fragment.sh index 076245d65ca2..d436c538b039 100755 --- a/tools/common/create-makefile-fragment.sh +++ b/tools/common/create-makefile-fragment.sh @@ -33,13 +33,15 @@ fi PROJECT_FILE="$1" PROJECT=$(basename -s .csproj "$PROJECT_FILE") PROJECT_DIR=$(dirname "$PROJECT_FILE") -FRAGMENT_PATH="$2" +FINAL_FRAGMENT_PATH="$2" REFERENCES_PATH=$(pwd)/$PROJECT-references.txt -if test -z "$FRAGMENT_PATH"; then - FRAGMENT_PATH=$PROJECT_FILE.inc +if test -z "$FINAL_FRAGMENT_PATH"; then + FINAL_FRAGMENT_PATH=$PROJECT_FILE.inc fi +FRAGMENT_PATH="$FINAL_FRAGMENT_PATH.$$.tmp" + BUILD_EXECUTABLE="dotnet build" if ! dotnet --version >& /dev/null; then @@ -59,6 +61,7 @@ fi function upon_exit () { rm -f "$PROJECT_DIR/ProjectInspector.csproj" + rm -f "$FRAGMENT_PATH" } trap upon_exit EXIT cp ProjectInspector.csproj "$PROJECT_DIR" @@ -89,6 +92,7 @@ function delete_tmpproj if test -n "$TMPPROJ"; then rm -f "$TMPPROJ" fi + rm -f "$FRAGMENT_PATH" } trap delete_tmpproj EXIT trap delete_tmpproj ERR @@ -138,5 +142,7 @@ if test -z "$ABSOLUTE_PATHS"; then sed "${SED_INPLACE_FLAGS[@]}" "s@$PROJECT_DIR/@@" "$FRAGMENT_PATH" fi +mv "$FRAGMENT_PATH" "$FINAL_FRAGMENT_PATH" + # Cleanup rm -f "${INPUT_PATHS[@]}" From ed01fc2e50560e706a5a941958db30d73e6fa46d Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 1 Jun 2026 08:44:16 +0200 Subject: [PATCH 39/79] [msbuild] Fix Content items with PublishFolderType losing files that match SDK names. Fixes #25497. (#25528) Content items with PublishFolderType (e.g. RootDirectory) would lose files whose names match SDK assemblies or runtime files (e.g. libclrgc.dylib, Microsoft.macOS.dll). This happened because the .NET SDK's ResolveOverlappingItemGroupConflicts task (during ComputeResolvedFilesToPublishList) removes user items when their filename conflicts with SDK package files. Fix this by setting TargetPath metadata (from Link) when Content/BundleResource items with PublishFolderType are first added to ResolvedFileToPublish. This way the conflict resolver sees the full relative path (e.g. Contents/SharedSupport/SubApp.app/.../libclrgc.dylib) instead of just the filename, and doesn't flag them as conflicts with SDK files. Fixes #25497. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- msbuild/Xamarin.Shared/Xamarin.Shared.targets | 16 +++++-- .../AppDelegate.cs | 12 +++++ .../helper/.gitignore | 2 + .../osx-arm64/HeartBeatHandlerMac.dll | 0 .../.xamarin/osx-arm64/Microsoft.CSharp.dll | 0 .../osx-arm64/Microsoft.VisualBasic.dll | 0 .../.xamarin/osx-arm64/Microsoft.macOS.dll | 0 .../Contents/MonoBundle/AsyncIO.dll | 0 .../MonoBundle/HeartbeatHandlerLib.dll | 0 .../System.ServiceModel.Security.dll | 0 .../Contents/MonoBundle/libclrgc.dylib | 0 .../macOS/ContentWithPublishFolderType.csproj | 22 +++++++++ .../ContentWithPublishFolderTypeTest.cs | 48 +++++++++++++++++++ 13 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 tests/dotnet/ContentWithPublishFolderType/AppDelegate.cs create mode 100644 tests/dotnet/ContentWithPublishFolderType/helper/.gitignore create mode 100644 tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/.xamarin/osx-arm64/HeartBeatHandlerMac.dll create mode 100644 tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.CSharp.dll create mode 100644 tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.VisualBasic.dll create mode 100644 tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.macOS.dll create mode 100644 tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/AsyncIO.dll create mode 100644 tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/HeartbeatHandlerLib.dll create mode 100644 tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/System.ServiceModel.Security.dll create mode 100644 tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/libclrgc.dylib create mode 100644 tests/dotnet/ContentWithPublishFolderType/macOS/ContentWithPublishFolderType.csproj create mode 100644 tests/dotnet/UnitTests/ContentWithPublishFolderTypeTest.cs diff --git a/msbuild/Xamarin.Shared/Xamarin.Shared.targets b/msbuild/Xamarin.Shared/Xamarin.Shared.targets index d9965c1b6db8..2c5f43be44b8 100644 --- a/msbuild/Xamarin.Shared/Xamarin.Shared.targets +++ b/msbuild/Xamarin.Shared/Xamarin.Shared.targets @@ -481,10 +481,18 @@ Copyright (C) 2018 Microsoft. All rights reserved. - - - - + + + + %(BundleResource.Link) + + + %(Content.Link) + diff --git a/tests/dotnet/ContentWithPublishFolderType/AppDelegate.cs b/tests/dotnet/ContentWithPublishFolderType/AppDelegate.cs new file mode 100644 index 000000000000..b99d6adb454a --- /dev/null +++ b/tests/dotnet/ContentWithPublishFolderType/AppDelegate.cs @@ -0,0 +1,12 @@ +using System; +using Foundation; + +namespace ContentWithPublishFolderType { + public class Program { + static int Main (string [] args) + { + GC.KeepAlive (typeof (NSObject)); // prevent linking away the platform assembly + return 0; + } + } +} diff --git a/tests/dotnet/ContentWithPublishFolderType/helper/.gitignore b/tests/dotnet/ContentWithPublishFolderType/helper/.gitignore new file mode 100644 index 000000000000..9a16fc334d87 --- /dev/null +++ b/tests/dotnet/ContentWithPublishFolderType/helper/.gitignore @@ -0,0 +1,2 @@ +# Override root .gitignore to allow test DLL files +!*.dll diff --git a/tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/.xamarin/osx-arm64/HeartBeatHandlerMac.dll b/tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/.xamarin/osx-arm64/HeartBeatHandlerMac.dll new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.CSharp.dll b/tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.CSharp.dll new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.VisualBasic.dll b/tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.VisualBasic.dll new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.macOS.dll b/tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.macOS.dll new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/AsyncIO.dll b/tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/AsyncIO.dll new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/HeartbeatHandlerLib.dll b/tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/HeartbeatHandlerLib.dll new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/System.ServiceModel.Security.dll b/tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/System.ServiceModel.Security.dll new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/libclrgc.dylib b/tests/dotnet/ContentWithPublishFolderType/helper/SubApp.app/Contents/MonoBundle/libclrgc.dylib new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/dotnet/ContentWithPublishFolderType/macOS/ContentWithPublishFolderType.csproj b/tests/dotnet/ContentWithPublishFolderType/macOS/ContentWithPublishFolderType.csproj new file mode 100644 index 000000000000..f72ebb8816e7 --- /dev/null +++ b/tests/dotnet/ContentWithPublishFolderType/macOS/ContentWithPublishFolderType.csproj @@ -0,0 +1,22 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion)-macos + Exe + ContentWithPublishFolderType + com.xamarin.contentwithpublishfoldertype + + + + + + + + + + + RootDirectory + Contents/SharedSupport/%(RecursiveDir)%(Filename)%(Extension) + + + diff --git a/tests/dotnet/UnitTests/ContentWithPublishFolderTypeTest.cs b/tests/dotnet/UnitTests/ContentWithPublishFolderTypeTest.cs new file mode 100644 index 000000000000..edd961ca624c --- /dev/null +++ b/tests/dotnet/UnitTests/ContentWithPublishFolderTypeTest.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Xamarin.Tests { + [TestFixture] + public class ContentWithPublishFolderTypeTest : TestBaseClass { + + [Test] + [TestCase (ApplePlatform.MacOSX, "osx-arm64")] + public void ContentFilesWithSdkAssemblyNamesAreIncluded (ApplePlatform platform, string runtimeIdentifiers) + { + // Regression test for https://github.com/dotnet/macios/issues/25497 + // When Content items have PublishFolderType=RootDirectory and filenames matching + // SDK assemblies (e.g., Microsoft.macOS.dll) or runtime files (e.g., libclrgc.dylib), + // they should still be copied to the app bundle. + var project = "ContentWithPublishFolderType"; + Configuration.IgnoreIfIgnoredPlatform (platform); + Configuration.AssertRuntimeIdentifiersAvailable (platform, runtimeIdentifiers); + + var project_path = GetProjectPath (project, runtimeIdentifiers: runtimeIdentifiers, platform: platform, out var appPath); + Clean (project_path); + + var properties = GetDefaultProperties (runtimeIdentifiers); + DotNet.AssertBuild (project_path, properties); + + // These are files in our helper directory that have the same names as SDK assemblies / runtime files. + // They should all be present in the app bundle's SharedSupport directory. + var expectedFiles = new string [] { + // Files that match SDK assembly names (these are the ones that go missing per the bug report) + "Contents/SharedSupport/SubApp.app/Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.macOS.dll", + "Contents/SharedSupport/SubApp.app/Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.CSharp.dll", + "Contents/SharedSupport/SubApp.app/Contents/MonoBundle/.xamarin/osx-arm64/Microsoft.VisualBasic.dll", + // A runtime dylib that matches a known runtime file name + "Contents/SharedSupport/SubApp.app/Contents/MonoBundle/libclrgc.dylib", + // Files that don't match SDK assembly names (these work fine) + "Contents/SharedSupport/SubApp.app/Contents/MonoBundle/.xamarin/osx-arm64/HeartBeatHandlerMac.dll", + "Contents/SharedSupport/SubApp.app/Contents/MonoBundle/AsyncIO.dll", + "Contents/SharedSupport/SubApp.app/Contents/MonoBundle/HeartbeatHandlerLib.dll", + "Contents/SharedSupport/SubApp.app/Contents/MonoBundle/System.ServiceModel.Security.dll", + }; + + foreach (var expectedFile in expectedFiles) { + var fullPath = Path.Combine (appPath, expectedFile); + Assert.That (fullPath, Does.Exist, $"Expected file '{expectedFile}' to be in the app bundle"); + } + } + } +} From 438194830ca60ce4c4f58d29d76171274969e2dd Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 1 Jun 2026 08:48:43 +0200 Subject: [PATCH 40/79] [msbuild] Improve diagnostics when simulator runtimes aren't available, Fixes #25298 (#25483) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apple's Xcode tools (actool, ibtool, etc.) require the simulator runtime even when building for physical devices. When the runtime is missing or the wrong version, these tools fail with unhelpful errors or hang indefinitely. ## Changes After a tool failure, run `xcrun simctl --json-output= list runtimes` to check whether the required simulator runtime is installed. If missing, emit a clear error (E7170) with instructions on how to install it. Additionally, detect simulator runtime version mismatch errors in tool output (e.g. `No simulator runtime version from [...] available to use with iphonesimulator SDK version ...`) and emit E7172 suggesting the user update their simulator runtime. The simctl check uses a 1-minute timeout to avoid hanging, and results are cached per platform/SdkDevPath. ## New diagnostics - **E7170**: Simulator runtime not installed (post-failure simctl check) - **W7171**: Unable to determine simulator runtime availability - **E7172**: Tool error indicates incompatible simulator runtime version Fixes https://github.com/dotnet/macios/issues/25298 🤖 Pull request created by Copilot --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Rolf Bjarne Kvinge --- .../MSBStrings.resx | 18 +++ .../Tasks/XcodeCompilerToolTask.cs | 112 +++++++++++++++++- 2 files changed, 126 insertions(+), 4 deletions(-) diff --git a/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx b/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx index 5f68eea415e1..5de6bb55b800 100644 --- a/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx +++ b/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx @@ -1643,4 +1643,22 @@ The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + + The {0} simulator runtime is not installed. This is required by Apple's development tools (even when building for physical devices). Install it by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the required simulator runtime is not installed. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + + Unable to determine if the {0} simulator runtime is installed. If the build fails or hangs, install the {0} simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line. + Shown when we're unable to check for simulator runtime availability. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + + The installed {0} simulator runtime is not compatible with the current Xcode version. Update the simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the tool reports a simulator runtime version mismatch. +{0} - The platform name (e.g. "iOS" or "tvOS"). + diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/XcodeCompilerToolTask.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/XcodeCompilerToolTask.cs index 419963539f9b..c189dde986e5 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/XcodeCompilerToolTask.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/XcodeCompilerToolTask.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.IO; using System.Linq; using System.Text; @@ -13,6 +14,7 @@ using Xamarin.Localization.MSBuild; using Xamarin.MacDev; +using Xamarin.MacDev.Models; using Xamarin.Messaging.Build.Client; using Xamarin.Utils; @@ -154,6 +156,92 @@ static bool IsTranslated () return translated.Value; } + // Cache simulator runtime check results to avoid running simctl multiple times. + // Key includes SdkDevPath because different Xcode installations may have different runtimes. + static ConcurrentDictionary simulatorRuntimeCache = new (); + + /// + /// Returns the platform name used by simctl for the current build platform, or null if no simulator is needed. + /// + string? GetSimulatorPlatformName () + { + switch (Platform) { + case ApplePlatform.iOS: + case ApplePlatform.MacCatalyst: + // Mac Catalyst uses the iOS-based toolchain, so it also needs the iOS simulator runtime. + return "iOS"; + case ApplePlatform.TVOS: + return "tvOS"; + case ApplePlatform.MacOSX: + default: + return null; + } + } + + /// + /// Checks if the required simulator runtime is installed and emits a diagnostic if not. + /// Apple's Xcode tools (actool, ibtool, etc.) require the simulator runtime to function, + /// even when building for physical devices. Call this after a tool failure to provide + /// actionable guidance to the user. + /// + void CheckSimulatorRuntimeAvailable () + { + var simPlatform = GetSimulatorPlatformName (); + if (simPlatform is null) + return; + + var cacheKey = $"{simPlatform}:{SdkDevPath}"; + if (simulatorRuntimeCache.TryGetValue (cacheKey, out var cachedResult)) { + if (!cachedResult) + Log.LogError (MSBStrings.E7175, simPlatform); + return; + } + + var jsonOutputFile = Path.GetTempFileName (); + try { + using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource (cancellationTokenSource.Token); + timeoutCts.CancelAfter (TimeSpan.FromMinutes (1)); + var args = new List { + "simctl", + "list", + "runtimes", + "-j", + "--json-output=" + jsonOutputFile + }; + var rv = ExecuteAsync ("xcrun", args, showErrorIfFailure: false, cancellationToken: timeoutCts.Token).Result; + + if (rv.ExitCode != 0) { + Log.LogWarning (MSBStrings.W7176, simPlatform); + return; + } + + var json = File.ReadAllText (jsonOutputFile); + var runtimes = SimctlOutputParser.ParseRuntimes (json); + + var hasRuntime = runtimes.Any (r => + string.Equals (r.Platform, simPlatform, StringComparison.OrdinalIgnoreCase) && r.IsAvailable); + + simulatorRuntimeCache [cacheKey] = hasRuntime; + if (!hasRuntime) + Log.LogError (MSBStrings.E7175, simPlatform); + } catch (OperationCanceledException) when (cancellationTokenSource.IsCancellationRequested) { + // User cancelled - don't emit diagnostics + } catch (AggregateException ae) when (ae.InnerException is OperationCanceledException && cancellationTokenSource.IsCancellationRequested) { + // User cancelled - don't emit diagnostics + } catch (OperationCanceledException) { + // Timeout + Log.LogWarning (MSBStrings.W7176, simPlatform); + } catch (AggregateException ae) when (ae.InnerException is OperationCanceledException) { + // Timeout + Log.LogWarning (MSBStrings.W7176, simPlatform); + } catch (Exception ex) { + Log.LogWarning (MSBStrings.W7176, simPlatform); + Log.LogMessage (MessageImportance.Low, "Exception while checking simulator runtime: {0}", ex.Message); + } finally { + File.Delete (jsonOutputFile); + } + } + protected int Compile (ITaskItem [] items, string output, ITaskItem manifest) { var environment = new Dictionary (); @@ -193,7 +281,7 @@ protected int Compile (ITaskItem [] items, string output, ITaskItem manifest) if (Log.HasLoggedErrors) return 1; - var rv = ExecuteAsync (executable, args, environment: environment, cancellationToken: cancellationTokenSource.Token).Result; + var rv = ExecuteAsync (executable, args, showErrorIfFailure: true, environment: environment, cancellationToken: cancellationTokenSource.Token).Result; var exitCode = rv.ExitCode; var messages = rv.Output.StandardOutput; File.WriteAllText (manifest.ItemSpec, messages); @@ -206,8 +294,6 @@ protected int Compile (ITaskItem [] items, string output, ITaskItem manifest) if (errors.Length > 0) Log.LogError (null, null, null, items [0].ItemSpec, 0, 0, 0, 0, "{0}", errors); - Log.LogError (MSBStrings.E0117, ToolName, exitCode); - // Note: If the log file exists and is parseable, log those warnings/errors as well... if (File.Exists (manifest.ItemSpec)) { try { @@ -220,6 +306,9 @@ protected int Compile (ITaskItem [] items, string output, ITaskItem manifest) File.Delete (manifest.ItemSpec); } + + // Check if the failure might be caused by a missing simulator runtime. + CheckSimulatorRuntimeAvailable (); } return exitCode; @@ -285,8 +374,14 @@ protected void LogWarningsAndErrors (PDictionary plist, ITaskItem file) if (plist.TryGetValue (string.Format ("com.apple.{0}.errors", ToolName), out array)) { foreach (var item in array.OfType ()) { - if (item.TryGetValue ("description", out message)) + if (item.TryGetValue ("description", out message)) { Log.LogError (ToolName, null, null, file.ItemSpec, 0, 0, 0, 0, "{0}", message.Value); + if (IsSimulatorRuntimeVersionError (message.Value)) { + var simPlatform = GetSimulatorPlatformName (); + if (simPlatform is not null) + Log.LogError (MSBStrings.E7177, simPlatform); + } + } } } @@ -298,6 +393,15 @@ protected void LogWarningsAndErrors (PDictionary plist, ITaskItem file) } } + /// + /// Detects error messages like "No simulator runtime version from [...] available to use with ... SDK version ..." + /// which indicate an incompatible or missing simulator runtime version. + /// + static bool IsSimulatorRuntimeVersionError (string message) + { + return message.IndexOf ("simulator runtime", StringComparison.OrdinalIgnoreCase) >= 0; + } + public void Cancel () { if (ShouldExecuteRemotely ()) { From 67b8de6a9371181faba167eabc141e4c3a60061e Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 1 Jun 2026 08:49:30 +0200 Subject: [PATCH 41/79] Add support for Composer Icons (#24722) ## Add support for Icon Composer (.icon) app icons This adds support for Xcode Icon Composer based `.icon` folders introduced in macOS 26 Tahoe's Liquid Glass design system (see #24132). ### Changes - **Recognize `.icon` folders**: Treats `.icon` directories as asset catalogs alongside `.xcassets` - **"Process"/Detect `icon.json` files**: Handles `icon.json` metadata files in addition to `Contents.json` - **Enable `--app-icon` flag**: `.icon`-based icons now pass validation and get the `--app-icon` flag passed to `actool` - **Support alternate icons**: Works with `IncludeAllAppIcons` for runtime icon switching ### Background The new `.icon` format is a folder structure containing: - `icon.json` - Icon metadata (layers, materials, effects) - `Assets/` subfolder - Vector graphics and image assets This replaces static `.icns` files to support Liquid Glass features like translucency, specular lighting, and cross-platform rendering. ### Usage Add `.icon` folders to your project: ```xml AppIcon true ``` The build system will: 1. Recognize the `.icon` folder as a valid app icon 2. Pass it to `actool` with the `--app-icon AppIcon` flag 3. Compile it into `Assets.car` --- This is a recreation of #24476 (from @paulober), because our CI can't work with pull requests from forks. Fixes https://github.com/dotnet/macios/issues/24132. --------- Co-authored-by: paulober <44974737+paulober@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Rolf Bjarne Kvinge --- docs/building-apps/build-items.md | 3 +- dotnet/DefaultCompilationIncludes.md | 9 +- .../Microsoft.Sdk.DefaultItems.template.props | 3 +- msbuild/Xamarin.MacDev.Tasks/Tasks/ACTool.cs | 52 +++-- .../dotnet/AppWithComposerIcon/AppDelegate.cs | 35 +++ .../MacCatalyst/AppWithComposerIcon.csproj | 7 + .../AppWithComposerIcon/MacCatalyst/Makefile | 1 + .../Resources/AppIcon.icon/Assets/back.png | Bin 0 -> 5705 bytes .../Resources/AppIcon.icon/Assets/front.png | Bin 0 -> 5705 bytes .../Resources/AppIcon.icon/icon.json | 16 ++ .../iOS/AppWithComposerIcon.csproj | 7 + tests/dotnet/AppWithComposerIcon/iOS/Makefile | 1 + .../Resources/AppIcon.icon/Assets/back.png | Bin 0 -> 5705 bytes .../Resources/AppIcon.icon/Assets/front.png | Bin 0 -> 5705 bytes .../iOS/Resources/AppIcon.icon/icon.json | 16 ++ .../macOS/AppWithComposerIcon.csproj | 7 + .../dotnet/AppWithComposerIcon/macOS/Makefile | 1 + .../Resources/AppIcon.icon/Assets/back.png | Bin 0 -> 5705 bytes .../Resources/AppIcon.icon/Assets/front.png | Bin 0 -> 5705 bytes .../macOS/Resources/AppIcon.icon/icon.json | 16 ++ .../dotnet/AppWithComposerIcon/shared.csproj | 23 ++ tests/dotnet/AppWithComposerIcon/shared.mk | 3 + .../tvOS/AppWithComposerIcon.csproj | 7 + .../dotnet/AppWithComposerIcon/tvOS/Makefile | 1 + .../Resources/AppIcon.icon/Assets/back.png | Bin 0 -> 5705 bytes .../Resources/AppIcon.icon/Assets/front.png | Bin 0 -> 5705 bytes .../tvOS/Resources/AppIcon.icon/icon.json | 16 ++ tests/dotnet/UnitTests/AppIconTest.cs | 34 +++ .../TaskTests/ACToolTaskTest.cs | 210 ++++++++++++++++++ 29 files changed, 442 insertions(+), 26 deletions(-) create mode 100644 tests/dotnet/AppWithComposerIcon/AppDelegate.cs create mode 100644 tests/dotnet/AppWithComposerIcon/MacCatalyst/AppWithComposerIcon.csproj create mode 100644 tests/dotnet/AppWithComposerIcon/MacCatalyst/Makefile create mode 100644 tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/Assets/back.png create mode 100644 tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/Assets/front.png create mode 100644 tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/icon.json create mode 100644 tests/dotnet/AppWithComposerIcon/iOS/AppWithComposerIcon.csproj create mode 100644 tests/dotnet/AppWithComposerIcon/iOS/Makefile create mode 100644 tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/Assets/back.png create mode 100644 tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/Assets/front.png create mode 100644 tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/icon.json create mode 100644 tests/dotnet/AppWithComposerIcon/macOS/AppWithComposerIcon.csproj create mode 100644 tests/dotnet/AppWithComposerIcon/macOS/Makefile create mode 100644 tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/Assets/back.png create mode 100644 tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/Assets/front.png create mode 100644 tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/icon.json create mode 100644 tests/dotnet/AppWithComposerIcon/shared.csproj create mode 100644 tests/dotnet/AppWithComposerIcon/shared.mk create mode 100644 tests/dotnet/AppWithComposerIcon/tvOS/AppWithComposerIcon.csproj create mode 100644 tests/dotnet/AppWithComposerIcon/tvOS/Makefile create mode 100644 tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/Assets/back.png create mode 100644 tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/Assets/front.png create mode 100644 tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/icon.json diff --git a/docs/building-apps/build-items.md b/docs/building-apps/build-items.md index ff18c071b9e6..2fd81202cf18 100644 --- a/docs/building-apps/build-items.md +++ b/docs/building-apps/build-items.md @@ -186,7 +186,8 @@ Only applicable to iOS and tvOS projects. ## ImageAsset -An item group that contains image assets. +An item group that contains image assets, including files inside asset catalogs +(\*.xcassets) and Icon Composer directories (\*.icon). ## InterfaceDefinition diff --git a/dotnet/DefaultCompilationIncludes.md b/dotnet/DefaultCompilationIncludes.md index 3dec5cdb0a5f..9f14799e6634 100644 --- a/dotnet/DefaultCompilationIncludes.md +++ b/dotnet/DefaultCompilationIncludes.md @@ -33,6 +33,13 @@ All \*.pdf, \*.jpg, \*.png and \*.json files inside asset catalogs (\*.xcassets) in the project directory or any subdirectory are included by default (as `ImageAsset` items). +## Icon Composer files + +All files inside Icon Composer directories (\*.icon) in the project directory +or any subdirectory are included by default (as `ImageAsset` items). Icon +Composer files are created by Xcode's Icon Composer tool (Xcode 26+) and +contain layered app icons with `icon.json` metadata. + ## Atlas Textures All \*.png files inside \*.atlas directories in the project directory or any @@ -52,7 +59,7 @@ included by default (as `Metal` items). All files in the Resources/ subdirectory, except any items in the `Compile` or `EmbeddedResource` item groups, and except the ones mentioned above -(\*.scnassets, \*.storyboard, \*.xib, \*.xcassets, \*.atlas, \*.mlmodel, +(\*.scnassets, \*.storyboard, \*.xib, \*.xcassets, \*.icon, \*.atlas, \*.mlmodel, \*.metal) are included by default (as `BundleResource` items). [1]: https://docs.microsoft.com/en-us/dotnet/core/tools/csproj#default-compilation-includes-in-net-core-projects diff --git a/dotnet/targets/Microsoft.Sdk.DefaultItems.template.props b/dotnet/targets/Microsoft.Sdk.DefaultItems.template.props index d705ae3f56c2..afc08fe18531 100644 --- a/dotnet/targets/Microsoft.Sdk.DefaultItems.template.props +++ b/dotnet/targets/Microsoft.Sdk.DefaultItems.template.props @@ -24,7 +24,7 @@ - + $([MSBuild]::MakeRelative ('$(MSBuildProjectDirectory)', '%(FullPath)')) false true @@ -65,6 +65,7 @@ $(DefaultItemExcludes); $(DefaultExcludesInProjectFolder); $(_ResourcePrefix)\**\*.xcassets\**\*.*; + $(_ResourcePrefix)\**\*.icon\**\*.*; $(_ResourcePrefix)\**\*.storyboard;**\*.xib; $(_ResourcePrefix)\**\*.atlas\*.png; $(_ResourcePrefix)\**\*.mlmodel; diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/ACTool.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/ACTool.cs index fbdd82f46eb6..829b904260e9 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/ACTool.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/ACTool.cs @@ -248,12 +248,12 @@ IEnumerable GetCompiledBundleResources (PDictionary output, string in yield break; } - void FindXCAssetsDirectory (string main, string secondary, out string mainResult, out string secondaryResult) + void FindAssetCatalogDirectory (string main, string secondary, out string mainResult, out string secondaryResult) { mainResult = main; secondaryResult = secondary; - while (!string.IsNullOrEmpty (mainResult) && !mainResult.EndsWith (".xcassets", StringComparison.OrdinalIgnoreCase)) { + while (!string.IsNullOrEmpty (mainResult) && !mainResult.EndsWith (".xcassets", StringComparison.OrdinalIgnoreCase) && !mainResult.EndsWith (".icon", StringComparison.OrdinalIgnoreCase)) { mainResult = Path.GetDirectoryName (mainResult)!; if (!string.IsNullOrEmpty (secondaryResult)) secondaryResult = Path.GetDirectoryName (secondaryResult)!; @@ -292,14 +292,14 @@ public override bool Execute () var vpath = BundleResource.GetVirtualProjectPath (this, imageAsset); var catalogFullPath = imageAsset.GetMetadata ("FullPath"); - // get the parent (which will typically be .appiconset, .launchimage, .imageset, .iconset, etc) + // get the parent (which will typically be .appiconset, .launchimage, .imageset, .iconset, .icon, etc) var catalog = Path.GetDirectoryName (vpath)!; catalogFullPath = Path.GetDirectoryName (catalogFullPath)!; var assetType = Path.GetExtension (catalog).TrimStart ('.'); - // keep walking up the directory structure until we get to the .xcassets directory - FindXCAssetsDirectory (catalog, catalogFullPath, out var catalog2, out var catalogFullPath2); + // keep walking up the directory structure until we get to the .xcassets or .icon directory + FindAssetCatalogDirectory (catalog, catalogFullPath, out var catalog2, out var catalogFullPath2); catalog = catalog2; catalogFullPath = catalogFullPath2; @@ -325,11 +325,11 @@ public override bool Execute () continue; } - // filter out everything except paths containing a Contents.json file since our main processing loop only cares about these - if (Path.GetFileName (vpath) != "Contents.json") - continue; - - items.Add (asset); + // Handle both Contents.json (for .xcassets) and icon.json (for .icon folders) + var fileName = Path.GetFileName (vpath); + if (fileName == "Contents.json" || fileName == "icon.json") { + items.Add (asset); + } } // clone any *.xcassets dirs that need cloning @@ -370,18 +370,20 @@ public override bool Execute () File.Copy (src, dest, true); - // filter out everything except paths containing a Contents.json file since our main processing loop only cares about these - if (Path.GetFileName (vpath) != "Contents.json") + // Handle both Contents.json (for .xcassets) and icon.json (for .icon folders) + var fileName = Path.GetFileName (vpath); + if (fileName != "Contents.json" && fileName != "icon.json") continue; item = new TaskItem (dest); assetItem.CopyMetadataTo (item); item.SetMetadata ("Link", vpath); - FindXCAssetsDirectory (Path.GetFullPath (dest), "", out var catalogFullPath, out var _); + FindAssetCatalogDirectory (Path.GetFullPath (dest), "", out var catalogFullPath, out var _); items.Add (new AssetInfo (item, vpath, asset.Catalog, catalogFullPath, asset.AssetType)); } else { - // filter out everything except paths containing a Contents.json file since our main processing loop only cares about these - if (Path.GetFileName (vpath) != "Contents.json") + // Handle both Contents.json (for .xcassets) and icon.json (for .icon folders) + var fileName = Path.GetFileName (vpath); + if (fileName != "Contents.json" && fileName != "icon.json") continue; items.Add (asset); @@ -389,7 +391,7 @@ public override bool Execute () } } - // Note: `items` contains only the Contents.json files at this point + // Note: `items` contains only the Contents.json and icon.json files at this point for (int i = 0; i < items.Count; i++) { var asset = items [i]; var assetItem = asset.Item; @@ -397,16 +399,19 @@ public override bool Execute () var catalog = asset.Catalog; var path = assetItem.GetMetadata ("FullPath"); var assetType = asset.AssetType; + var vpathDirNameWithoutExtension = Path.GetFileNameWithoutExtension (Path.GetDirectoryName (vpath)!); if (Platform == ApplePlatform.TVOS) { - if (assetType.Equals ("imagestack", StringComparison.OrdinalIgnoreCase)) { - imageStacksInAssets.Add (Path.GetFileNameWithoutExtension (Path.GetDirectoryName (vpath)!)); - } else if (assetType.Equals ("brandassets", StringComparison.OrdinalIgnoreCase)) { - brandAssetsInAssets.Add (Path.GetFileNameWithoutExtension (Path.GetDirectoryName (vpath)!)); + if (assetType.Equals ("imagestack", StringComparison.OrdinalIgnoreCase) || assetType.Equals ("icon", StringComparison.OrdinalIgnoreCase)) { + imageStacksInAssets.Add (vpathDirNameWithoutExtension); + } + if (assetType.Equals ("brandassets", StringComparison.OrdinalIgnoreCase) || assetType.Equals ("icon", StringComparison.OrdinalIgnoreCase)) { + brandAssetsInAssets.Add (vpathDirNameWithoutExtension); } } else { - if (assetType.Equals ("appiconset", StringComparison.OrdinalIgnoreCase)) - appIconsInAssets.Add (Path.GetFileNameWithoutExtension (Path.GetDirectoryName (vpath)!)); + if (assetType.Equals ("appiconset", StringComparison.OrdinalIgnoreCase) || assetType.Equals ("icon", StringComparison.OrdinalIgnoreCase)) { + appIconsInAssets.Add (vpathDirNameWithoutExtension); + } } if (unique.Add (catalog)) { @@ -416,7 +421,8 @@ public override bool Execute () catalogs.Add (item); } - if (SdkPlatform != "WatchSimulator") { + // Only process Contents.json files for on-demand resources (not icon.json files) + if (SdkPlatform != "WatchSimulator" && Path.GetFileName (vpath) == "Contents.json") { var text = File.ReadAllText (assetItem.ItemSpec); if (string.IsNullOrEmpty (text)) diff --git a/tests/dotnet/AppWithComposerIcon/AppDelegate.cs b/tests/dotnet/AppWithComposerIcon/AppDelegate.cs new file mode 100644 index 000000000000..13cf28d17265 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/AppDelegate.cs @@ -0,0 +1,35 @@ +using System; +using Foundation; + +#if !__MACOS__ +using UIKit; +#endif + +#nullable enable + +namespace AppWithComposerIcon { +#if !(__MACCATALYST__ || __MACOS__) + public class AppDelegate : UIApplicationDelegate { + public override bool FinishedLaunching (UIApplication app, NSDictionary? options) + { + return true; + } + } +#endif + + public class Program { + static int Main (string [] args) + { +#if __MACCATALYST__ || __MACOS__ +GC.KeepAlive (typeof (NSObject)); // prevent linking away the platform assembly + +Console.WriteLine (Environment.GetEnvironmentVariable ("MAGIC_WORD")); + +return args.Length; +#else + UIApplication.Main (args, null, typeof (AppDelegate)); + return 0; +#endif + } + } +} diff --git a/tests/dotnet/AppWithComposerIcon/MacCatalyst/AppWithComposerIcon.csproj b/tests/dotnet/AppWithComposerIcon/MacCatalyst/AppWithComposerIcon.csproj new file mode 100644 index 000000000000..6b0e2c773180 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/MacCatalyst/AppWithComposerIcon.csproj @@ -0,0 +1,7 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion)-maccatalyst + + + diff --git a/tests/dotnet/AppWithComposerIcon/MacCatalyst/Makefile b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Makefile new file mode 100644 index 000000000000..110d078f4577 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Makefile @@ -0,0 +1 @@ +include ../shared.mk diff --git a/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/Assets/back.png b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/Assets/back.png new file mode 100644 index 0000000000000000000000000000000000000000..00c6bc6118f0ee916acf51845aec89a4b11970e4 GIT binary patch literal 5705 zcmeAS@N?(olHy`uVBq!ia0y~yU;#2&7&zE~RK2WrGXsN|fTxRNNX4zUHyi~)QjRlI zgu=ABJQBWbWPMk8aVj%V1VLs3NEHLf7KQ{6fsaX?i4ZH<8aRN4 zC4fxCXC#sWl1v8~i=-D1vw=~838A`K@c zFu-bQWjG080r46sE+@foq;MpXNh8Ba323ldBArXqSfr`pBn0%JNg@dh9SlG~g41ai zjiVI;C@PTRqJdEXRJRU-k|Y7>3}lxfITAaQcBusDj=^9!2{{H5LW3X{A>l@X%V`zR z382(OE5k_%=)qDSGz_Aq-)Mz^-YH>g5CEpv1Ry513?wBD(kMTUW_I*w94wiAv_8O^ zIz2jNnIT<)V$2mi~mG|00P0prw_C zGfA@~OKzuFm|_Dr87{LgbMMXW?B3JK@@SOrrfu*ptFv*mAtAdpTDGfU_D@nH- zu1Y8m4d8+q(=en!#fCRQ=Anp2uBK4n5~8*A1EXl`7aKzRP?DN4)hp7+cMX)(V4(RJ zCwp5)Et5U=182!==m+t?fX;4H(+Oct3$ThZ;ZA^T+`He-x3M3z$kL!<><27LM+xWR e+^J}Ej&;}h!)5t8$NPUks2cA8 literal 0 HcmV?d00001 diff --git a/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/icon.json b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/icon.json new file mode 100644 index 000000000000..9cb9eee03bd1 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/icon.json @@ -0,0 +1,16 @@ +{ + "groups" : [ + { + "layers" : [ + { + "image-name" : "back.png", + "name" : "back" + }, + { + "image-name" : "front.png", + "name" : "front" + } + ] + } + ] +} diff --git a/tests/dotnet/AppWithComposerIcon/iOS/AppWithComposerIcon.csproj b/tests/dotnet/AppWithComposerIcon/iOS/AppWithComposerIcon.csproj new file mode 100644 index 000000000000..86d408734aa8 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/iOS/AppWithComposerIcon.csproj @@ -0,0 +1,7 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion)-ios + + + diff --git a/tests/dotnet/AppWithComposerIcon/iOS/Makefile b/tests/dotnet/AppWithComposerIcon/iOS/Makefile new file mode 100644 index 000000000000..110d078f4577 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/iOS/Makefile @@ -0,0 +1 @@ +include ../shared.mk diff --git a/tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/Assets/back.png b/tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/Assets/back.png new file mode 100644 index 0000000000000000000000000000000000000000..00c6bc6118f0ee916acf51845aec89a4b11970e4 GIT binary patch literal 5705 zcmeAS@N?(olHy`uVBq!ia0y~yU;#2&7&zE~RK2WrGXsN|fTxRNNX4zUHyi~)QjRlI zgu=ABJQBWbWPMk8aVj%V1VLs3NEHLf7KQ{6fsaX?i4ZH<8aRN4 zC4fxCXC#sWl1v8~i=-D1vw=~838A`K@c zFu-bQWjG080r46sE+@foq;MpXNh8Ba323ldBArXqSfr`pBn0%JNg@dh9SlG~g41ai zjiVI;C@PTRqJdEXRJRU-k|Y7>3}lxfITAaQcBusDj=^9!2{{H5LW3X{A>l@X%V`zR z382(OE5k_%=)qDSGz_Aq-)Mz^-YH>g5CEpv1Ry513?wBD(kMTUW_I*w94wiAv_8O^ zIz2jNnIT<)V$2mi~mG|00P0prw_C zGfA@~OKzuFm|_Dr87{LgbMMXW?B3JK@@SOrrfu*ptFv*mAtAdpTDGfU_D@nH- zu1Y8m4d8+q(=en!#fCRQ=Anp2uBK4n5~8*A1EXl`7aKzRP?DN4)hp7+cMX)(V4(RJ zCwp5)Et5U=182!==m+t?fX;4H(+Oct3$ThZ;ZA^T+`He-x3M3z$kL!<><27LM+xWR e+^J}Ej&;}h!)5t8$NPUks2cA8 literal 0 HcmV?d00001 diff --git a/tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/icon.json b/tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/icon.json new file mode 100644 index 000000000000..9cb9eee03bd1 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/icon.json @@ -0,0 +1,16 @@ +{ + "groups" : [ + { + "layers" : [ + { + "image-name" : "back.png", + "name" : "back" + }, + { + "image-name" : "front.png", + "name" : "front" + } + ] + } + ] +} diff --git a/tests/dotnet/AppWithComposerIcon/macOS/AppWithComposerIcon.csproj b/tests/dotnet/AppWithComposerIcon/macOS/AppWithComposerIcon.csproj new file mode 100644 index 000000000000..a77287b9ba00 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/macOS/AppWithComposerIcon.csproj @@ -0,0 +1,7 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion)-macos + + + diff --git a/tests/dotnet/AppWithComposerIcon/macOS/Makefile b/tests/dotnet/AppWithComposerIcon/macOS/Makefile new file mode 100644 index 000000000000..110d078f4577 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/macOS/Makefile @@ -0,0 +1 @@ +include ../shared.mk diff --git a/tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/Assets/back.png b/tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/Assets/back.png new file mode 100644 index 0000000000000000000000000000000000000000..00c6bc6118f0ee916acf51845aec89a4b11970e4 GIT binary patch literal 5705 zcmeAS@N?(olHy`uVBq!ia0y~yU;#2&7&zE~RK2WrGXsN|fTxRNNX4zUHyi~)QjRlI zgu=ABJQBWbWPMk8aVj%V1VLs3NEHLf7KQ{6fsaX?i4ZH<8aRN4 zC4fxCXC#sWl1v8~i=-D1vw=~838A`K@c zFu-bQWjG080r46sE+@foq;MpXNh8Ba323ldBArXqSfr`pBn0%JNg@dh9SlG~g41ai zjiVI;C@PTRqJdEXRJRU-k|Y7>3}lxfITAaQcBusDj=^9!2{{H5LW3X{A>l@X%V`zR z382(OE5k_%=)qDSGz_Aq-)Mz^-YH>g5CEpv1Ry513?wBD(kMTUW_I*w94wiAv_8O^ zIz2jNnIT<)V$2mi~mG|00P0prw_C zGfA@~OKzuFm|_Dr87{LgbMMXW?B3JK@@SOrrfu*ptFv*mAtAdpTDGfU_D@nH- zu1Y8m4d8+q(=en!#fCRQ=Anp2uBK4n5~8*A1EXl`7aKzRP?DN4)hp7+cMX)(V4(RJ zCwp5)Et5U=182!==m+t?fX;4H(+Oct3$ThZ;ZA^T+`He-x3M3z$kL!<><27LM+xWR e+^J}Ej&;}h!)5t8$NPUks2cA8 literal 0 HcmV?d00001 diff --git a/tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/icon.json b/tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/icon.json new file mode 100644 index 000000000000..9cb9eee03bd1 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/icon.json @@ -0,0 +1,16 @@ +{ + "groups" : [ + { + "layers" : [ + { + "image-name" : "back.png", + "name" : "back" + }, + { + "image-name" : "front.png", + "name" : "front" + } + ] + } + ] +} diff --git a/tests/dotnet/AppWithComposerIcon/shared.csproj b/tests/dotnet/AppWithComposerIcon/shared.csproj new file mode 100644 index 000000000000..702960b9dd8e --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/shared.csproj @@ -0,0 +1,23 @@ + + + + Exe + + AppWithComposerIcon + com.xamarin.appwithcomposericon + + true + + + + + + AppIcon + + + + + + + + diff --git a/tests/dotnet/AppWithComposerIcon/shared.mk b/tests/dotnet/AppWithComposerIcon/shared.mk new file mode 100644 index 000000000000..de0244100278 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/shared.mk @@ -0,0 +1,3 @@ +TOP=../../../.. +TESTNAME=AppWithComposerIcon +include $(TOP)/tests/common/shared-dotnet.mk diff --git a/tests/dotnet/AppWithComposerIcon/tvOS/AppWithComposerIcon.csproj b/tests/dotnet/AppWithComposerIcon/tvOS/AppWithComposerIcon.csproj new file mode 100644 index 000000000000..bd487ddcd88d --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/tvOS/AppWithComposerIcon.csproj @@ -0,0 +1,7 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion)-tvos + + + diff --git a/tests/dotnet/AppWithComposerIcon/tvOS/Makefile b/tests/dotnet/AppWithComposerIcon/tvOS/Makefile new file mode 100644 index 000000000000..110d078f4577 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/tvOS/Makefile @@ -0,0 +1 @@ +include ../shared.mk diff --git a/tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/Assets/back.png b/tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/Assets/back.png new file mode 100644 index 0000000000000000000000000000000000000000..00c6bc6118f0ee916acf51845aec89a4b11970e4 GIT binary patch literal 5705 zcmeAS@N?(olHy`uVBq!ia0y~yU;#2&7&zE~RK2WrGXsN|fTxRNNX4zUHyi~)QjRlI zgu=ABJQBWbWPMk8aVj%V1VLs3NEHLf7KQ{6fsaX?i4ZH<8aRN4 zC4fxCXC#sWl1v8~i=-D1vw=~838A`K@c zFu-bQWjG080r46sE+@foq;MpXNh8Ba323ldBArXqSfr`pBn0%JNg@dh9SlG~g41ai zjiVI;C@PTRqJdEXRJRU-k|Y7>3}lxfITAaQcBusDj=^9!2{{H5LW3X{A>l@X%V`zR z382(OE5k_%=)qDSGz_Aq-)Mz^-YH>g5CEpv1Ry513?wBD(kMTUW_I*w94wiAv_8O^ zIz2jNnIT<)V$2mi~mG|00P0prw_C zGfA@~OKzuFm|_Dr87{LgbMMXW?B3JK@@SOrrfu*ptFv*mAtAdpTDGfU_D@nH- zu1Y8m4d8+q(=en!#fCRQ=Anp2uBK4n5~8*A1EXl`7aKzRP?DN4)hp7+cMX)(V4(RJ zCwp5)Et5U=182!==m+t?fX;4H(+Oct3$ThZ;ZA^T+`He-x3M3z$kL!<><27LM+xWR e+^J}Ej&;}h!)5t8$NPUks2cA8 literal 0 HcmV?d00001 diff --git a/tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/icon.json b/tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/icon.json new file mode 100644 index 000000000000..9cb9eee03bd1 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/icon.json @@ -0,0 +1,16 @@ +{ + "groups" : [ + { + "layers" : [ + { + "image-name" : "back.png", + "name" : "back" + }, + { + "image-name" : "front.png", + "name" : "front" + } + ] + } + ] +} diff --git a/tests/dotnet/UnitTests/AppIconTest.cs b/tests/dotnet/UnitTests/AppIconTest.cs index 970676172b53..a5eb8dc0d9c3 100644 --- a/tests/dotnet/UnitTests/AppIconTest.cs +++ b/tests/dotnet/UnitTests/AppIconTest.cs @@ -672,5 +672,39 @@ void TestXCAssetsImpl (ApplePlatform platform, string runtimeIdentifiers, Dictio throw; } } + + [TestCase (ApplePlatform.iOS, "iossimulator-arm64")] + [TestCase (ApplePlatform.TVOS, "tvossimulator-arm64")] + [TestCase (ApplePlatform.MacCatalyst, "maccatalyst-arm64")] + [TestCase (ApplePlatform.MacOSX, "osx-arm64")] + public void ComposerIcon (ApplePlatform platform, string runtimeIdentifiers) + { + Configuration.AssertRuntimeIdentifiersAvailable (platform, runtimeIdentifiers); + Configuration.IgnoreIfIgnoredPlatform (platform); + + var project = "AppWithComposerIcon"; + var projectPath = GetProjectPath (project, runtimeIdentifiers: runtimeIdentifiers, platform: platform, out var appPath); + Clean (projectPath); + + var properties = GetDefaultProperties (runtimeIdentifiers); + DotNet.Execute ("build", projectPath, properties); + + var resourcesDirectory = GetResourcesDirectory (platform, appPath); + + // Verify that the raw .icon files are not in the app bundle as BundleResources + var iconJsonInBundle = Path.Combine (resourcesDirectory, "AppIcon.icon", "icon.json"); + Assert.That (iconJsonInBundle, Does.Not.Exist, "icon.json should not be in the app bundle as a raw BundleResource"); + + var frontPngInBundle = Path.Combine (resourcesDirectory, "AppIcon.icon", "Assets", "front.png"); + Assert.That (frontPngInBundle, Does.Not.Exist, "front.png should not be in the app bundle as a raw BundleResource"); + + var backPngInBundle = Path.Combine (resourcesDirectory, "AppIcon.icon", "Assets", "back.png"); + Assert.That (backPngInBundle, Does.Not.Exist, "back.png should not be in the app bundle as a raw BundleResource"); + + // Verify that the compiled asset catalog exists in the app bundle + var assetsCar = Path.Combine (resourcesDirectory, "Assets.car"); + Assert.That (assetsCar, Does.Exist, "Assets.car"); + } } } + diff --git a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs index e41ff90d614e..218b3cf4fa9e 100644 --- a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs +++ b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs @@ -633,5 +633,215 @@ public void XSAppIconAssetsAndAppIcon (ApplePlatform platform) ExecuteTask (actool, 1); Assert.That (Engine.Logger.ErrorEvents [0].Message, Is.EqualTo ("Can't specify both 'XSAppIconAssets' in the Info.plist and 'AppIcon' in the project file. Please select one or the other."), "Error message"); } + + [Test] + [TestCase (ApplePlatform.iOS)] + [TestCase (ApplePlatform.TVOS)] + [TestCase (ApplePlatform.MacCatalyst)] + [TestCase (ApplePlatform.MacOSX)] + public void IconFileSupport (ApplePlatform platform) + { + // Test that .icon folders (Icon Composer format) are recognized as app icons + var projectDir = Cache.CreateTemporaryDirectory (); + var iconFolderPath = Path.Combine (projectDir, "Resources", "AppIcon.icon"); + var assetsPath = Path.Combine (iconFolderPath, "Assets"); + Directory.CreateDirectory (assetsPath); + + // Create a placeholder icon.json file (simplified structure for testing) + var iconJsonPath = Path.Combine (iconFolderPath, "icon.json"); + File.WriteAllText (iconJsonPath, @"{""groups"":[{""layers"":[{""image-name"":""icon_512x512.png"",""name"":""icon""}]}]}"); + + // Create a placeholder image file in the Assets folder + var imagePath = Path.Combine (assetsPath, "icon_512x512.png"); + File.WriteAllText (imagePath, "placeholder image"); + + var actool = CreateACToolTask ( + platform, + projectDir, + out var _, + iconJsonPath + "|Resources/AppIcon.icon/icon.json", + imagePath + "|Resources/AppIcon.icon/Assets/icon_512x512.png" + ); + actool.AppIcon = "AppIcon"; + + // actool may fail on the placeholder .icon content, but the validation phase should pass + actool.Execute (); + + // Verify that no icon validation errors were logged + AssertNoIconValidationErrors (); + } + + [Test] + [TestCase (ApplePlatform.iOS)] + [TestCase (ApplePlatform.TVOS)] + [TestCase (ApplePlatform.MacCatalyst)] + [TestCase (ApplePlatform.MacOSX)] + public void IconFileSupportWithIncludeAllAppIcons (ApplePlatform platform) + { + // Test that .icon folders work with IncludeAllAppIcons + var projectDir = Cache.CreateTemporaryDirectory (); + var iconFolderPath = Path.Combine (projectDir, "Resources", "AppIcon.icon"); + var assetsPath = Path.Combine (iconFolderPath, "Assets"); + Directory.CreateDirectory (assetsPath); + + var iconJsonPath = Path.Combine (iconFolderPath, "icon.json"); + File.WriteAllText (iconJsonPath, @"{""groups"":[{""layers"":[{""image-name"":""icon_512x512.png"",""name"":""icon""}]}]}"); + + var imagePath = Path.Combine (assetsPath, "icon_512x512.png"); + File.WriteAllText (imagePath, "placeholder image"); + + var actool = CreateACToolTask ( + platform, + projectDir, + out var _, + iconJsonPath + "|Resources/AppIcon.icon/icon.json", + imagePath + "|Resources/AppIcon.icon/Assets/icon_512x512.png" + ); + actool.AppIcon = "AppIcon"; + actool.IncludeAllAppIcons = true; + + // actool may fail on the placeholder .icon content, but the validation phase should pass + actool.Execute (); + + // Verify that no icon validation errors were logged + AssertNoIconValidationErrors (); + } + + [Test] + [TestCase (ApplePlatform.iOS)] + [TestCase (ApplePlatform.TVOS)] + [TestCase (ApplePlatform.MacCatalyst)] + [TestCase (ApplePlatform.MacOSX)] + public void IconFileSupportAsAlternateIcon (ApplePlatform platform) + { + // Test that .icon folders work as alternate app icons + var projectDir = Cache.CreateTemporaryDirectory (); + var iconFolderPath = Path.Combine (projectDir, "Resources", "AlternateIcon.icon"); + var assetsPath = Path.Combine (iconFolderPath, "Assets"); + Directory.CreateDirectory (assetsPath); + + var iconJsonPath = Path.Combine (iconFolderPath, "icon.json"); + File.WriteAllText (iconJsonPath, @"{""groups"":[{""layers"":[{""image-name"":""icon_512x512.png"",""name"":""icon""}]}]}"); + + var imagePath = Path.Combine (assetsPath, "icon_512x512.png"); + File.WriteAllText (imagePath, "placeholder image"); + + // Also need a primary icon for the alternate icon test to make sense + var primaryIconPath = Path.Combine (projectDir, "Resources", "AppIcon.icon"); + var primaryAssetsPath = Path.Combine (primaryIconPath, "Assets"); + Directory.CreateDirectory (primaryAssetsPath); + + var primaryIconJsonPath = Path.Combine (primaryIconPath, "icon.json"); + File.WriteAllText (primaryIconJsonPath, @"{""groups"":[{""layers"":[{""image-name"":""icon_512x512.png"",""name"":""icon""}]}]}"); + + var primaryImagePath = Path.Combine (primaryAssetsPath, "icon_512x512.png"); + File.WriteAllText (primaryImagePath, "placeholder image"); + + var actool = CreateACToolTask ( + platform, + projectDir, + out var _, + iconJsonPath + "|Resources/AlternateIcon.icon/icon.json", + imagePath + "|Resources/AlternateIcon.icon/Assets/icon_512x512.png", + primaryIconJsonPath + "|Resources/AppIcon.icon/icon.json", + primaryImagePath + "|Resources/AppIcon.icon/Assets/icon_512x512.png" + ); + actool.AppIcon = "AppIcon"; + actool.AlternateAppIcons = new ITaskItem [] { new TaskItem ("AlternateIcon") }; + + // actool may fail on the placeholder .icon content, but the validation phase should pass + actool.Execute (); + + // Verify that no icon validation errors were logged + AssertNoIconValidationErrors (); + } + + [Test] + [TestCase (ApplePlatform.iOS)] + [TestCase (ApplePlatform.TVOS)] + [TestCase (ApplePlatform.MacCatalyst)] + [TestCase (ApplePlatform.MacOSX)] + public void InexistentIconFile (ApplePlatform platform) + { + // Test that an inexistent .icon-based app icon is correctly reported + var projectDir = Cache.CreateTemporaryDirectory (); + var iconFolderPath = Path.Combine (projectDir, "Resources", "AppIcon.icon"); + var assetsPath = Path.Combine (iconFolderPath, "Assets"); + Directory.CreateDirectory (assetsPath); + + var iconJsonPath = Path.Combine (iconFolderPath, "icon.json"); + File.WriteAllText (iconJsonPath, @"{""groups"":[{""layers"":[{""image-name"":""icon_512x512.png"",""name"":""icon""}]}]}"); + + var imagePath = Path.Combine (assetsPath, "icon_512x512.png"); + File.WriteAllText (imagePath, "placeholder image"); + + var actool = CreateACToolTask ( + platform, + projectDir, + out var _, + iconJsonPath + "|Resources/AppIcon.icon/icon.json", + imagePath + "|Resources/AppIcon.icon/Assets/icon_512x512.png" + ); + actool.AppIcon = "InexistentIcon"; + + ExecuteTask (actool, 1); + + var errorMessages = Engine.Logger.ErrorEvents.Select (e => e.Message).ToList (); + Assert.That (errorMessages.Any (m => m?.Contains ("Can't find the AppIcon 'InexistentIcon'") == true), Is.True, "Should report that InexistentIcon is not found among image resources"); + } + + [Test] + [TestCase (ApplePlatform.iOS)] + [TestCase (ApplePlatform.MacCatalyst)] + [TestCase (ApplePlatform.MacOSX)] + public void MixedXCAssetsAndIconFile (ApplePlatform platform) + { + // Test that .icon folders and .xcassets can coexist in the validation phase + var projectDir = Path.Combine (Configuration.SourceRoot, "tests", "dotnet", "AppWithXCAssets", platform.AsString ()); + var files = Directory.GetFiles (Path.Combine (projectDir, "Resources", "Images.xcassets"), "*", SearchOption.AllDirectories); + var imageAssets = files.Select (v => v + "|" + v.Substring (projectDir.Length + 1)).ToList (); + + // Add a .icon folder alongside the existing .xcassets + var tmpDir = Cache.CreateTemporaryDirectory (); + var iconFolderPath = Path.Combine (tmpDir, "ComposerIcon.icon"); + var assetsPath = Path.Combine (iconFolderPath, "Assets"); + Directory.CreateDirectory (assetsPath); + + var iconJsonPath = Path.Combine (iconFolderPath, "icon.json"); + File.WriteAllText (iconJsonPath, @"{""groups"":[{""layers"":[{""image-name"":""icon_512x512.png"",""name"":""icon""}]}]}"); + + var imagePath = Path.Combine (assetsPath, "icon_512x512.png"); + File.WriteAllText (imagePath, "placeholder image"); + + imageAssets.Add (iconJsonPath + "|Resources/ComposerIcon.icon/icon.json"); + imageAssets.Add (imagePath + "|Resources/ComposerIcon.icon/Assets/icon_512x512.png"); + + var actool = CreateACToolTask ( + platform, + projectDir, + out var _, + imageAssets.ToArray () + ); + actool.AppIcon = "AppIcons"; + + // actool may fail on the placeholder .icon content, but the validation phase should pass + actool.Execute (); + + // Verify that no icon validation errors were logged + AssertNoIconValidationErrors (); + } + + void AssertNoIconValidationErrors () + { + var errorMessages = Engine.Logger.ErrorEvents.Select (e => e.Message).ToList (); + Assert.That (errorMessages, Has.None.Contain ("Can't find the AppIcon"), + "Should not report that AppIcon is not found among image resources"); + Assert.That (errorMessages, Has.None.Contain ("Can't find the AlternateAppIcon"), + "Should not report that AlternateAppIcon is not found among image resources"); + Assert.That (errorMessages, Has.None.Contain ("is specified as both 'AppIcon' and 'AlternateAppIcon'"), + "Should not report icon conflict between AppIcon and AlternateAppIcon"); + Assert.That (errorMessages, Has.None.Contain ("Can't specify both 'XSAppIconAssets'"), + "Should not report XSAppIconAssets conflict"); + } } } From 75e59fc26c46e560111894cc523662ae2c33cbb6 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 1 Jun 2026 08:50:12 +0200 Subject: [PATCH 42/79] [docs] Fix reference to the ReferenceNativeSymbol item group. (#25579) --- docs/building-apps/build-properties.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/building-apps/build-properties.md b/docs/building-apps/build-properties.md index 684cd816cb7d..634396282b90 100644 --- a/docs/building-apps/build-properties.md +++ b/docs/building-apps/build-properties.md @@ -587,7 +587,7 @@ Example: ``` -Custom behavior for specific Objective-C classes can be set using the [ReferenceNativeSymbol](build-items.md#referencenativesymbols) item group: +Custom behavior for specific Objective-C classes can be set using the [ReferenceNativeSymbol](build-items.md#referencenativesymbol) item group: ```xml @@ -617,7 +617,7 @@ Example: ``` -Custom behavior for specific symbols can be set using the [ReferenceNativeSymbol](build-items.md#referencenativesymbols) item group: +Custom behavior for specific symbols can be set using the [ReferenceNativeSymbol](build-items.md#referencenativesymbol) item group: ```xml From 53ffc7202a22a069931b5ef744224c2c772a096b Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Jun 2026 07:24:32 -0400 Subject: [PATCH 43/79] [github] Raise Code Radiator PR patch-file limit to 1000 for inter-branch merges (#25576) Inter-branch merge PR creation was blocked because the safe-output patch file cap was too low for `main => net11.0` (195 files). This change raises the cap to accommodate large automated merge PRs. - **Workflow source update** - Added a repository-level safe-output limit in `/.github/workflows/code-radiator.md`: - `safe-outputs.max-patch-files: 1000` - **Compiled workflow parity** - Updated the generated lockfile `/.github/workflows/code-radiator.lock.yml` so runtime safe-output config uses: - `create_pull_request.max_patch_files: 1000` - **Effective config (example)** ```yaml safe-outputs: max-patch-files: 1000 create-pull-request: max: 10 allowed-base-branches: - "net*.0" - "xcode*" - "xcode*.*" ``` --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: rolfbjarne <249268+rolfbjarne@users.noreply.github.com> --- .github/workflows/code-radiator.lock.yml | 4 ++-- .github/workflows/code-radiator.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/code-radiator.lock.yml b/.github/workflows/code-radiator.lock.yml index abcb3b886b9c..d36e5f5b4d16 100644 --- a/.github/workflows/code-radiator.lock.yml +++ b/.github/workflows/code-radiator.lock.yml @@ -460,7 +460,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_b50ec395f48eecb1_EOF' - {"add_comment":{"max":10,"target":"*"},"add_labels":{"max":10,"target":"*"},"create_pull_request":{"allowed_base_branches":["net*.0","xcode*","xcode*.*"],"max":10,"max_patch_files":100,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"protected_files_policy":"request_review"},"create_report_incomplete_issue":{},"merge_pull_request":{"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_to_pull_request_branch":{"if_no_changes":"warn","max":10,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"target":"*","title_prefix":"🤖 Merge 'main' =\u003e '"},"report_incomplete":{},"update_pull_request":{"allow_body":true,"allow_title":true,"max":10,"update_branch":false}} + {"add_comment":{"max":10,"target":"*"},"add_labels":{"max":10,"target":"*"},"create_pull_request":{"allowed_base_branches":["net*.0","xcode*","xcode*.*"],"max":10,"max_patch_files":1000,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"protected_files_policy":"request_review"},"create_report_incomplete_issue":{},"merge_pull_request":{"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_to_pull_request_branch":{"if_no_changes":"warn","max":10,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"target":"*","title_prefix":"🤖 Merge 'main' =\u003e '"},"report_incomplete":{},"update_pull_request":{"allow_body":true,"allow_title":true,"max":10,"update_branch":false}} GH_AW_SAFE_OUTPUTS_CONFIG_b50ec395f48eecb1_EOF - name: Generate Safe Outputs Tools env: @@ -1580,7 +1580,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10,\"target\":\"*\"},\"add_labels\":{\"max\":10,\"target\":\"*\"},\"create_pull_request\":{\"allowed_base_branches\":[\"net*.0\",\"xcode*\",\"xcode*.*\"],\"max\":10,\"max_patch_files\":100,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"protected_files_policy\":\"request_review\"},\"create_report_incomplete_issue\":{},\"merge_pull_request\":{\"max\":10},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max\":10,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"target\":\"*\",\"title_prefix\":\"🤖 Merge 'main' =\\u003e '\"},\"report_incomplete\":{},\"update_pull_request\":{\"allow_body\":true,\"allow_title\":true,\"max\":10,\"update_branch\":false}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10,\"target\":\"*\"},\"add_labels\":{\"max\":10,\"target\":\"*\"},\"create_pull_request\":{\"allowed_base_branches\":[\"net*.0\",\"xcode*\",\"xcode*.*\"],\"max\":10,\"max_patch_files\":1000,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"protected_files_policy\":\"request_review\"},\"create_report_incomplete_issue\":{},\"merge_pull_request\":{\"max\":10},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max\":10,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"target\":\"*\",\"title_prefix\":\"🤖 Merge 'main' =\\u003e '\"},\"report_incomplete\":{},\"update_pull_request\":{\"allow_body\":true,\"allow_title\":true,\"max\":10,\"update_branch\":false}}" GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }} with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/code-radiator.md b/.github/workflows/code-radiator.md index 8618affe282c..5d4d18d5a0e7 100644 --- a/.github/workflows/code-radiator.md +++ b/.github/workflows/code-radiator.md @@ -25,6 +25,7 @@ checkout: fetch: ["*"] fetch-depth: 0 safe-outputs: + max-patch-files: 1000 create-pull-request: max: 10 allowed-base-branches: From 1fb4e7e3f55d8dfc0cebb4ad820530f82c7b3da9 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 2 Jun 2026 08:00:06 +0200 Subject: [PATCH 44/79] [src] Add opt-in to build the platform assemblies serially. (#25546) The platform assemblies are rather big, and take a while to compile. This also means the C# compiler (csc) uses a lot of computational resources during the process. Each csc process is multi-threaded, and then if make also runs multiple csc processes in parallel, the build may completely thrash the CPU, and csc (aka Roslyn) isn't particularly easy on the memory either, causing any other use of the machine to suffer signicantly. So with this change we optionally build the platform assemblies serially. It looks a bit weird, but the idea is that in the make template, every platform assembly depends on the assembly from the previous template expansion. It slows down the build somewhat (src/ builds in ~5:30 instead of ~3:45 on my 9-year old iMac Pro), so it's opt-in instead of on by default. --- src/Makefile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Makefile b/src/Makefile index 703dc110204c..5752ed549ca3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -435,6 +435,15 @@ $($(2)_DOTNET_BUILD_DIR)/Microsoft.$(1).rsp: Makefile frameworks.sources $(RSP_D $($(2)_DOTNET_BUILD_DIR)/$(4)/Microsoft.$(1)%dll $($(2)_DOTNET_BUILD_DIR)/$(4)/Microsoft.$(1)%pdb $($(2)_DOTNET_BUILD_DIR)/ref/Microsoft.$(1)%dll $($(2)_DOTNET_BUILD_DIR)/ref/Microsoft.$(1)%xml: $$($(2)_DOTNET_PLATFORM_ASSEMBLY_DEPENDENCIES) $$(ROSLYN_GENERATOR) $$(ROSLYN_ANALYZER) | $$($(2)_DOTNET_PLATFORM_ASSEMBLY_DIR_DEPENDENCIES) $$(call Q_PROF_CSC,dotnet) $(DOTNET_CSC) @$($(2)_DOTNET_BUILD_DIR)/Microsoft.$(1).rsp +ifdef SERIALIZE_CSC +# This is to serialize the csc commands for the platform assembly. The platform assembly takes a while to compile (maxing out the CPU), and csc is already multi-threaded, which +# means that if we're running multiple long csc tasks simultaneously, the CPU of the machine will get absolutely thrashed. csc (aka Roslyn) isn't exactly a cheapskate with memory either... +# It looks a bit weird, but the idea is that in this template, every platform assembly depends on the assembly from the previous template expansion. +# It's opt-in, because it slows down the build a bit. +$($(2)_DOTNET_BUILD_DIR)/$(4)/Microsoft.$(1).dll: $(LAST_DLL) +LAST_DLL=$($(2)_DOTNET_BUILD_DIR)/$(4)/Microsoft.$(1).dll +endif + dotnet-$(3):: $($(2)_DOTNET_BUILD_DIR)/$(4)/Microsoft.$(1).dll DOTNET_TARGETS_$(3) += \ From 957df39ce2025e2287b305e9f96b44e57a76c95b Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 2 Jun 2026 09:00:04 +0200 Subject: [PATCH 45/79] [src] Remove NSPrintPreviewGraphicsContext. (#25527) NSPrintPreviewGraphicsContext existed in our bindings because we sometimes get instances of this type, and since its parent type is NSProxy, it causes problems at runtime when we try to figure out which managed type to instantiate. In particular, when: * The app is trimmed * The NSProxy type is trimmed away This happens: MarshalManagedException: ObjCRuntime.RuntimeException: The ObjectiveC class 'NSPrintPreviewGraphicsContext' could not be registered, it does not seem to derive from any known ObjectiveC class (including NSObject). at Registrar.DynamicRegistrar.Lookup(IntPtr class, Boolean throw_on_error) in /Users/builder/azdo/_work/1/s/macios/src/ObjCRuntime/DynamicRegistrar.cs:line 1069 at ObjCRuntime.Class.Lookup(IntPtr klass, Boolean throw_on_error) in /Users/builder/azdo/_work/1/s/macios/src/ObjCRuntime/Class.cs:line 291 at ObjCRuntime.Class.Lookup(IntPtr klass) in /Users/builder/azdo/_work/1/s/macios/src/ObjCRuntime/Class.cs:line 272 at ObjCRuntime.Runtime.GetNSObject[T](IntPtr ptr, IntPtr sel, RuntimeMethodHandle method_handle, Boolean evenInFinalizerQueue, Boolean typeSafe) in /Users/builder/azdo/_work/1/s/macios/src/ObjCRuntime/Runtime.cs:line 1893 at ObjCRuntime.Runtime.GetNSObject[T](IntPtr ptr) in /Users/builder/azdo/_work/1/s/macios/src/ObjCRuntime/Runtime.cs:line 1844 at ObjCRuntime.Runtime.GetNSObject[T](IntPtr ptr, Boolean owns) in /Users/builder/azdo/_work/1/s/macios/src/ObjCRuntime/Runtime.cs:line 1930 at AppKit.NSGraphicsContext.get_CurrentContext() in /Users/builder/azdo/_work/1/s/macios/src/build/dotnet/macos/generated-sources/AppKit/NSGraphicsContext.g.cs:line 411 at PrintableView.DrawPageBorder(CGSize borderSize) in /Users/rolf/test/dotnet/macos-printpreview/Program.cs:line 97 because `NSProxy` doesn't subclass `NSObject`, we can't find a managed type to instantiate. In the Xamarin days we had custom linker support to keep `NSPrintPreviewGraphicsContext` (https://github.com/xamarin/maccore/commit/d047ee123f3bfc28126bb9258878fccfed8d1c7a), but this was never ported to .NET, so this has been broken since forever in .NET. So fix this by: * Removing the `NSPrintPreviewGraphicsContext` binding, it causes problems when inlining calls to Class.GetHandle, because it's not part of the public API. * Ensure `NSProxy` is not trimmed away when `NSGraphicsContext.CurrentContext` is used, this way we'll return an `NSProxy` instance when the native instance is an actual `NSPrintPreviewGraphicsContext`. This required adding support for copying `[DynamicDependency]` attributes from binding code to the generated code. * Also add unit tests. This required removing the WebKit_NSProxy unit test, because it doesn't work anymore (it asserts that the `NSProxy` type is trimmed away, but one of the new tests depends on `NSProxy` not being trimmed away). References: * https://github.com/xamarin/bugzilla-archives/blob/main/16/16505/bug.html * https://github.com/xamarin/maccore/commit/d047ee123f3bfc28126bb9258878fccfed8d1c7a --- src/AppKit/NSPrintPreviewGraphicsContext.cs | 31 ++++++++ src/appkit.cs | 13 ++-- src/bgen/AttributeManager.cs | 1 + src/bgen/Generator.cs | 78 +++++++++++++++++-- src/frameworks.sources | 1 + tests/bgen/BGenTests.cs | 58 ++++++++++++++ .../tests/dynamic-dependency-attribute.cs | 42 ++++++++++ .../Documentation.KnownFailures.txt | 1 - tests/linker/link all/LinkAllMacTest.cs | 70 +++++++++++++++++ tests/linker/link all/LinkAllTest.cs | 9 --- .../macOS-AppKit.ignore | 3 - 11 files changed, 280 insertions(+), 27 deletions(-) create mode 100644 src/AppKit/NSPrintPreviewGraphicsContext.cs create mode 100644 tests/bgen/tests/dynamic-dependency-attribute.cs diff --git a/src/AppKit/NSPrintPreviewGraphicsContext.cs b/src/AppKit/NSPrintPreviewGraphicsContext.cs new file mode 100644 index 000000000000..ccc191f682c9 --- /dev/null +++ b/src/AppKit/NSPrintPreviewGraphicsContext.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; + +#if __MACOS__ && !XAMCORE_5_0 + +namespace AppKit { + [UnsupportedOSPlatform ("maccatalyst")] + [UnsupportedOSPlatform ("macos")] + [Obsolete ("This class does not form part of the public API in macOS, and will be removed in the future.")] + [EditorBrowsable (EditorBrowsableState.Never)] + public partial class NSPrintPreviewGraphicsContext : NSGraphicsContext { + + [EditorBrowsable (EditorBrowsableState.Never)] + public override NativeHandle ClassHandle { get { return default; } } + + [EditorBrowsable (EditorBrowsableState.Never)] + protected NSPrintPreviewGraphicsContext (NSObjectFlag t) : base (t) + { + } + + [EditorBrowsable (EditorBrowsableState.Never)] + protected internal NSPrintPreviewGraphicsContext (NativeHandle handle) : base (handle) + { + } + } /* class NSPrintPreviewGraphicsContext */ +} + +#endif // __MACOS__ && !XAMCORE_5_0 diff --git a/src/appkit.cs b/src/appkit.cs index 349228a5507c..f8eb8010f3c9 100644 --- a/src/appkit.cs +++ b/src/appkit.cs @@ -32,6 +32,7 @@ #nullable enable using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Diagnostics; using System.ComponentModel; using CoreGraphics; @@ -8701,7 +8702,11 @@ interface NSGraphicsContext { NSGraphicsContext FromGraphicsPort (IntPtr graphicsPort, bool initialFlippedState); [Static, Export ("currentContext"), NullAllowed] - NSGraphicsContext CurrentContext { get; set; } + NSGraphicsContext CurrentContext { + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors, "Foundation.NSProxy", "Microsoft.macOS")] // https://github.com/xamarin/bugzilla-archives/blob/main/16/16505/bug.html + get; + set; + } [Static, Export ("currentContextDrawingToScreen")] bool IsCurrentContextDrawingToScreen { get; } @@ -8964,12 +8969,6 @@ interface NSGridCell : NSCoding { NSLayoutConstraint [] CustomPlacementConstraints { get; set; } } - [NoMacCatalyst] - [BaseType (typeof (NSGraphicsContext))] - [DisableDefaultCtor] - interface NSPrintPreviewGraphicsContext { - } - [NoMacCatalyst] [BaseType (typeof (NSImageRep))] [DisableDefaultCtor] // An uncaught exception was raised: -[NSEPSImageRep init]: unrecognized selector sent to instance 0x1db2d90 diff --git a/src/bgen/AttributeManager.cs b/src/bgen/AttributeManager.cs index 3ceccdd0c76e..9ae2768b7670 100644 --- a/src/bgen/AttributeManager.cs +++ b/src/bgen/AttributeManager.cs @@ -15,6 +15,7 @@ public class AttributeManager { "System.Runtime.CompilerServices.NullableAttribute", "System.Runtime.CompilerServices.NullableContextAttribute", "System.Runtime.CompilerServices.NativeIntegerAttribute", + "System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute", }; TypeCache TypeCache { get; } diff --git a/src/bgen/Generator.cs b/src/bgen/Generator.cs index f5559c942d26..f1221a33dafb 100644 --- a/src/bgen/Generator.cs +++ b/src/bgen/Generator.cs @@ -2788,7 +2788,7 @@ public string MakeSignature (MemberInformation minfo, bool is_async, ParameterIn string name = GetMethodName (minfo, is_async); // Some codepaths already write preservation info - PrintAttributes (minfo.mi, preserve: !alreadyPreserved, advice: true, bindAs: true, requiresSuper: true); + PrintAttributes (minfo.mi, preserve: !alreadyPreserved, advice: true, bindAs: true, requiresSuper: true, dynamicDependency: true); if (minfo.is_ctor && minfo.is_protocol_member) { sb.Append ("T? "); @@ -4037,7 +4037,7 @@ void GenerateProperty (Type type, PropertyInfo pi, List? instance_fields pi.Name.GetSafeParamName ()); indent++; if (generate_getter) { - PrintAttributes (pi.GetGetMethod ()!, platform: true, preserve: true, advice: true); + PrintAttributes (pi.GetGetMethod ()!, platform: true, preserve: true, advice: true, dynamicDependency: true); print ("get {"); indent++; @@ -4056,7 +4056,7 @@ void GenerateProperty (Type type, PropertyInfo pi, List? instance_fields print ("}"); } if (generate_setter) { - PrintAttributes (pi.GetSetMethod ()!, platform: true, preserve: true, advice: true); + PrintAttributes (pi.GetSetMethod ()!, platform: true, preserve: true, advice: true, dynamicDependency: true); print ("set {"); indent++; @@ -4130,7 +4130,7 @@ void GenerateProperty (Type type, PropertyInfo pi, List? instance_fields // If property getter or setter has its own WrapAttribute we let the user do whatever their heart desires if (generate_getter) { PrintAttributes (pi, platform: true); - PrintAttributes (pi.GetGetMethod (), platform: true, preserve: true, advice: true); + PrintAttributes (pi.GetGetMethod (), platform: true, preserve: true, advice: true, dynamicDependency: true); print ("get {"); indent++; @@ -4170,7 +4170,7 @@ void GenerateProperty (Type type, PropertyInfo pi, List? instance_fields PrintExport (minfo, sel, export!.ArgumentSemantic); } - PrintAttributes (pi.GetGetMethod (), platform: true, preserve: true, advice: true, notImplemented: true, inlinedType: inlinedType); + PrintAttributes (pi.GetGetMethod (), platform: true, preserve: true, advice: true, notImplemented: true, inlinedType: inlinedType, dynamicDependency: true); if (minfo.is_protocol_member && !minfo.is_static) { print ("get {"); print ($"\treturn _Get{pi.Name.GetSafeParamName ()} (this);"); @@ -4226,7 +4226,7 @@ void GenerateProperty (Type type, PropertyInfo pi, List? instance_fields if (not_implemented_attr is null && (!minfo.is_sealed || !minfo.is_wrapper)) PrintExport (minfo, sel, export!.ArgumentSemantic); - PrintAttributes (pi.GetSetMethod (), platform: true, preserve: true, advice: true, notImplemented: true, inlinedType: inlinedType); + PrintAttributes (pi.GetSetMethod (), platform: true, preserve: true, advice: true, notImplemented: true, inlinedType: inlinedType, dynamicDependency: true); if (minfo.is_protocol_member && !minfo.is_static) { print ("set {"); print ($"\t_Set{pi.Name.GetSafeParamName ()} (this, value);"); @@ -5509,7 +5509,7 @@ public void PrintBindAsAttribute (ICustomAttributeProvider? mi, StringBuilder? s // Not adding the experimental attribute is bad (it would mean that an API // we meant to be experimental ended up being released as stable), so it's // opt-out instead of opt-in. - public void PrintAttributes (ICustomAttributeProvider? mi, bool platform = false, bool preserve = false, bool advice = false, bool notImplemented = false, bool bindAs = false, bool requiresSuper = false, Type? inlinedType = null, bool experimental = true, bool obsolete = false, bool objectiveCFramework = false, bool simulatorAvailability = true) + public void PrintAttributes (ICustomAttributeProvider? mi, bool platform = false, bool preserve = false, bool advice = false, bool notImplemented = false, bool bindAs = false, bool requiresSuper = false, Type? inlinedType = null, bool experimental = true, bool obsolete = false, bool objectiveCFramework = false, bool simulatorAvailability = true, bool dynamicDependency = false) { if (platform) PrintPlatformAttributes (mi as MemberInfo, inlinedType); @@ -5531,6 +5531,70 @@ public void PrintAttributes (ICustomAttributeProvider? mi, bool platform = false PrintObjectiveCFrameworkAttribute (mi); if (simulatorAvailability) PrintSimulatorAvailabilityAttributes (mi); + if (dynamicDependency) + PrintDynamicDependencyAttributes (mi); + } + + public void PrintDynamicDependencyAttributes (ICustomAttributeProvider? mi) + { + if (mi is not MemberInfo memberInfo) + return; + + var allAttribs = memberInfo.GetCustomAttributesData (); + foreach (var attrib in allAttribs) { + if (attrib.GetAttributeType ().FullName != "System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute") + continue; + var args = attrib.ConstructorArguments; + var parts = new List (); + foreach (var arg in args) { + if (arg.Value is string stringValue) { + parts.Add ($"\"{stringValue}\""); + } else if (arg.Value is int intValue) { + parts.Add (FormatDynamicallyAccessedMemberTypes (intValue)); + } else if (arg.Value is Type typeValue) { + parts.Add ($"typeof ({typeValue})"); + } else { + exceptions.Add (ErrorHelper.CreateError (99, $"Unexpected attribute argument value for DynamicDependency attribute: {arg.ArgumentType.FullName} => {arg.Value}")); + } + } + print ($"[DynamicDependency ({string.Join (", ", parts)})]"); + } + } + + static string FormatDynamicallyAccessedMemberTypes (int memberTypes) + { + if (memberTypes == -1) // All + return "DynamicallyAccessedMemberTypes.All"; + if (memberTypes == 0) // None + return "DynamicallyAccessedMemberTypes.None"; + + // Use the composite values first to match the original source + var flagValues = new (int value, string name) [] { + (3, "PublicConstructors"), // 3 includes PublicParameterlessConstructor + (1, "PublicParameterlessConstructor"), + (4, "NonPublicConstructors"), + (8, "PublicMethods"), + (16, "NonPublicMethods"), + (32, "PublicFields"), + (64, "NonPublicFields"), + (128, "PublicNestedTypes"), + (256, "NonPublicNestedTypes"), + (512, "PublicProperties"), + (1024, "NonPublicProperties"), + (2048, "PublicEvents"), + (4096, "NonPublicEvents"), + (8192, "Interfaces"), + }; + + var parts = new List (); + var remaining = memberTypes; + foreach (var (value, name) in flagValues) { + if (value != 0 && (remaining & value) == value) { + parts.Add ($"DynamicallyAccessedMemberTypes.{name}"); + remaining &= ~value; + } + } + return string.Join (" | ", parts); } public void PrintExperimentalAttribute (ICustomAttributeProvider? mi) diff --git a/src/frameworks.sources b/src/frameworks.sources index accf45f75e36..c715fddb973f 100644 --- a/src/frameworks.sources +++ b/src/frameworks.sources @@ -130,6 +130,7 @@ APPKIT_SOURCES = \ AppKit/NSPopUpButtonCell.cs \ AppKit/NSPredicateEditorRowTemplate.cs \ AppKit/NSPrintInfo.cs \ + AppKit/NSPrintPreviewGraphicsContext.cs \ AppKit/NSScreen.cs \ AppKit/NSSegmentedControl.cs \ AppKit/NSSharingService.cs \ diff --git a/tests/bgen/BGenTests.cs b/tests/bgen/BGenTests.cs index 37de37291334..656c8ac2c867 100644 --- a/tests/bgen/BGenTests.cs +++ b/tests/bgen/BGenTests.cs @@ -1155,6 +1155,64 @@ public void GeneratedAttributeOnPropertyAccessors2 () Assert.That (RenderSupportedOSPlatformAttributes (setter), Is.EqualTo (expectedSetterAttributes), "Setter Attributes"); } + [Test] + public void DynamicDependencyAttribute () + { + var bgen = BuildFile (Profile.macOSMobile, "dynamic-dependency-attribute.cs"); + + var type = bgen.ApiAssembly.MainModule.Types.First (v => v.Name == "MyClass"); + var getter = type.Methods.First (v => v.Name == "get_CurrentContext"); + var setter = type.Methods.First (v => v.Name == "set_CurrentContext"); + var doSomething = type.Methods.First (v => v.Name == "DoSomething"); + var doSomethingElse = type.Methods.First (v => v.Name == "DoSomethingElse"); + var doAnother = type.Methods.First (v => v.Name == "DoAnother"); + var doYetAnother = type.Methods.First (v => v.Name == "DoYetAnother"); + var doAll = type.Methods.First (v => v.Name == "DoAll"); + + // (DynamicallyAccessedMemberTypes, string, string) on property getter + var getterDDA = getter.CustomAttributes.Where (ca => ca.AttributeType.Name == "DynamicDependencyAttribute").ToArray (); + Assert.That (getterDDA.Length, Is.EqualTo (1), "Getter DynamicDependency count"); + Assert.That ((int) getterDDA [0].ConstructorArguments [0].Value, Is.EqualTo (7), "Getter DynamicDependency MemberTypes (PublicConstructors | NonPublicConstructors)"); + Assert.That (getterDDA [0].ConstructorArguments [1].Value, Is.EqualTo ("Foundation.NSProxy"), "Getter DynamicDependency TypeName"); + Assert.That (getterDDA [0].ConstructorArguments [2].Value, Is.EqualTo ("Microsoft.macOS"), "Getter DynamicDependency AssemblyName"); + + // Setter should not have it + var setterDDA = setter.CustomAttributes.Where (ca => ca.AttributeType.Name == "DynamicDependencyAttribute").ToArray (); + Assert.That (setterDDA.Length, Is.EqualTo (0), "Setter DynamicDependency count"); + + // (string, string, string) on method + var methodDDA = doSomething.CustomAttributes.Where (ca => ca.AttributeType.Name == "DynamicDependencyAttribute").ToArray (); + Assert.That (methodDDA.Length, Is.EqualTo (1), "DoSomething DynamicDependency count"); + Assert.That (methodDDA [0].ConstructorArguments [0].Value, Is.EqualTo ("Create"), "DoSomething DynamicDependency MemberSignature"); + Assert.That (methodDDA [0].ConstructorArguments [1].Value, Is.EqualTo ("NS.MyClass"), "DoSomething DynamicDependency TypeName"); + Assert.That (methodDDA [0].ConstructorArguments [2].Value, Is.EqualTo ("api0"), "DoSomething DynamicDependency AssemblyName"); + + // (string) - single member signature + var elseDDA = doSomethingElse.CustomAttributes.Where (ca => ca.AttributeType.Name == "DynamicDependencyAttribute").ToArray (); + Assert.That (elseDDA.Length, Is.EqualTo (1), "DoSomethingElse DynamicDependency count"); + Assert.That (elseDDA [0].ConstructorArguments.Count, Is.EqualTo (1), "DoSomethingElse DynamicDependency arg count"); + Assert.That (elseDDA [0].ConstructorArguments [0].Value, Is.EqualTo ("Activate"), "DoSomethingElse DynamicDependency MemberSignature"); + + // (DynamicallyAccessedMemberTypes, Type) + var anotherDDA = doAnother.CustomAttributes.Where (ca => ca.AttributeType.Name == "DynamicDependencyAttribute").ToArray (); + Assert.That (anotherDDA.Length, Is.EqualTo (1), "DoAnother DynamicDependency count"); + Assert.That ((int) anotherDDA [0].ConstructorArguments [0].Value, Is.EqualTo (8 | 512), "DoAnother DynamicDependency MemberTypes (PublicMethods | PublicProperties)"); + Assert.That (((Mono.Cecil.TypeReference) anotherDDA [0].ConstructorArguments [1].Value).Name, Is.EqualTo ("NSObject"), "DoAnother DynamicDependency Type"); + + // (string, Type) + var yetAnotherDDA = doYetAnother.CustomAttributes.Where (ca => ca.AttributeType.Name == "DynamicDependencyAttribute").ToArray (); + Assert.That (yetAnotherDDA.Length, Is.EqualTo (1), "DoYetAnother DynamicDependency count"); + Assert.That (yetAnotherDDA [0].ConstructorArguments [0].Value, Is.EqualTo ("Create"), "DoYetAnother DynamicDependency MemberSignature"); + Assert.That (((Mono.Cecil.TypeReference) yetAnotherDDA [0].ConstructorArguments [1].Value).Name, Is.EqualTo ("NSObject"), "DoYetAnother DynamicDependency Type"); + + // (DynamicallyAccessedMemberTypes.All, string, string) + var allDDA = doAll.CustomAttributes.Where (ca => ca.AttributeType.Name == "DynamicDependencyAttribute").ToArray (); + Assert.That (allDDA.Length, Is.EqualTo (1), "DoAll DynamicDependency count"); + Assert.That ((int) allDDA [0].ConstructorArguments [0].Value, Is.EqualTo (-1), "DoAll DynamicDependency MemberTypes (All)"); + Assert.That (allDDA [0].ConstructorArguments [1].Value, Is.EqualTo ("NS.MyClass"), "DoAll DynamicDependency TypeName"); + Assert.That (allDDA [0].ConstructorArguments [2].Value, Is.EqualTo ("api0"), "DoAll DynamicDependency AssemblyName"); + } + [Test] [TestCase (Profile.iOS)] public void NewerAvailabilityInInlinedProtocol (Profile profile) diff --git a/tests/bgen/tests/dynamic-dependency-attribute.cs b/tests/bgen/tests/dynamic-dependency-attribute.cs new file mode 100644 index 000000000000..5ffbc3ac5d1e --- /dev/null +++ b/tests/bgen/tests/dynamic-dependency-attribute.cs @@ -0,0 +1,42 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Foundation; +using ObjCRuntime; + +namespace NS { + [BaseType (typeof (NSObject))] + interface MyClass { + [Static, Export ("currentContext"), NullAllowed] + NSObject CurrentContext { + // (DynamicallyAccessedMemberTypes, string, string) + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors, "Foundation.NSProxy", "Microsoft.macOS")] + get; + set; + } + + // (string, string, string) + [Export ("doSomething")] + [DynamicDependency ("Create", "NS.MyClass", "api0")] + void DoSomething (); + + // (string) - single member signature + [Export ("doSomethingElse")] + [DynamicDependency ("Activate")] + void DoSomethingElse (); + + // (DynamicallyAccessedMemberTypes, Type) + [Export ("doAnother")] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicProperties, typeof (NSObject))] + void DoAnother (); + + // (string, Type) + [Export ("doYetAnother")] + [DynamicDependency ("Create", typeof (NSObject))] + void DoYetAnother (); + + // (DynamicallyAccessedMemberTypes.All, string, string) + [Export ("doAll")] + [DynamicDependency (DynamicallyAccessedMemberTypes.All, "NS.MyClass", "api0")] + void DoAll (); + } +} diff --git a/tests/cecil-tests/Documentation.KnownFailures.txt b/tests/cecil-tests/Documentation.KnownFailures.txt index 259d2d5ebdf1..9e1603a34248 100644 --- a/tests/cecil-tests/Documentation.KnownFailures.txt +++ b/tests/cecil-tests/Documentation.KnownFailures.txt @@ -25152,7 +25152,6 @@ T:AppKit.NSPrintingPageOrder T:AppKit.NSPrintingPaginationMode T:AppKit.NSPrintPanelOptions T:AppKit.NSPrintPanelResult -T:AppKit.NSPrintPreviewGraphicsContext T:AppKit.NSPrintRenderingQuality T:AppKit.NSProgressIndicatorStyle T:AppKit.NSProgressIndicatorThickness diff --git a/tests/linker/link all/LinkAllMacTest.cs b/tests/linker/link all/LinkAllMacTest.cs index 363e29d8d920..fad1a76cf15f 100644 --- a/tests/linker/link all/LinkAllMacTest.cs +++ b/tests/linker/link all/LinkAllMacTest.cs @@ -2,6 +2,7 @@ using System.IO; using System.Runtime.CompilerServices; using System.Threading; +using System.Threading.Tasks; using System.Xml; using System.Xml.Serialization; @@ -73,6 +74,75 @@ public SerializeMe () SetMe = 1; } } + + // https://github.com/xamarin/bugzilla-archives/blob/main/16/16505/bug.html + [Test] + public void PrintPreview_NSGraphicsContextCurrentContext () + { + TestRuntime.AssertXcodeVersion (26, 0); + + // Verify that accessing NSGraphicsContext.CurrentContext during print preview + // doesn't crash due to the linker trimming NSPrintPreviewGraphicsContext. + var printableView = new PrintableView (new CoreGraphics.CGRect (0, 0, 100, 100)); + + var printInfo = (NSPrintInfo) NSPrintInfo.SharedPrintInfo.Copy (); + printInfo.JobDisposition = "NSPrintPreviewJob"; + + var printOp = NSPrintOperation.FromView (printableView, printInfo); + printOp.ShowsPrintPanel = true; // this is required to trigger the bug + printOp.ShowsProgressPanel = true; + + var closedPreview = false; + var closeAction = new Action (() => { + if (closedPreview) + return; + NSApplication.SharedApplication.AbortModal (); + closedPreview = true; + }); + + printableView.TaskCompletionSource.Task.ContinueWith (task => { + closeAction (); + }, TaskScheduler.FromCurrentSynchronizationContext ()); + + // Auto-close after 3 seconds in case something goes wrong + var timer = NSTimer.CreateScheduledTimer (3.0, (t) => closeAction ()); + NSRunLoop.Current.AddTimer (timer, NSRunLoopMode.ModalPanel); + + printOp.RunOperation (); + + Assert.That (printableView.TaskCompletionSource.Task.IsCompletedSuccessfully, Is.True, "DrawPageBorder was called successfully"); + var context = printableView.TaskCompletionSource.Task.Result; + Assert.That (context, Is.Not.Null, "NSGraphicsContext.CurrentContext was not null during print preview"); + } + } + + class PrintableView : NSView { + public PrintableView (CoreGraphics.CGRect frame) : base (frame) { } + + public TaskCompletionSource TaskCompletionSource = new (); + + public override void DrawPageBorder (CoreGraphics.CGSize borderSize) + { + try { + var context = NSGraphicsContext.CurrentContext; + base.DrawPageBorder (borderSize); + TaskCompletionSource.TrySetResult (context); + } catch (Exception e) { + Console.WriteLine ($"Unexpected exception occurred: {e}"); + TaskCompletionSource.TrySetException (e); + } + } + + public override bool KnowsPageRange (ref Foundation.NSRange range) + { + range = new Foundation.NSRange (1, 1); + return true; + } + + public override CoreGraphics.CGRect RectForPage (nint pageNumber) + { + return Bounds; + } } } #endif // __MACOS__ diff --git a/tests/linker/link all/LinkAllTest.cs b/tests/linker/link all/LinkAllTest.cs index 2ff37fa27fd0..8a21b29c7d13 100644 --- a/tests/linker/link all/LinkAllTest.cs +++ b/tests/linker/link all/LinkAllTest.cs @@ -466,15 +466,6 @@ public void AppleTls () Assert.That (Helper.GetType (fqn), Is.Null, "Should NOT be included (no SslStream or Socket support)"); } - [Test] - // https://bugzilla.xamarin.com/show_bug.cgi?id=59247 - public void WebKit_NSProxy () - { - // this test works only because "Link all" does not use WebKit - var fqn = typeof (NSObject).AssemblyQualifiedName!.Replace ("Foundation.NSObject", "Foundation.NSProxy"); - Assert.That (Helper.GetType (fqn), Is.Null, fqn); - } - static Type type_Task = typeof (Task); [Test] diff --git a/tests/xtro-sharpie/api-annotations-dotnet/macOS-AppKit.ignore b/tests/xtro-sharpie/api-annotations-dotnet/macOS-AppKit.ignore index 268a5aeb27fa..a9a43afadb4c 100644 --- a/tests/xtro-sharpie/api-annotations-dotnet/macOS-AppKit.ignore +++ b/tests/xtro-sharpie/api-annotations-dotnet/macOS-AppKit.ignore @@ -1,9 +1,6 @@ ## https://bugzilla.xamarin.com/show_bug.cgi?id=30717 !duplicate-register! _NSDatePickerCellDelegate exists as both AppKit.NSDatePickerCell/_NSDatePickerCellDelegate and AppKit.NSDatePicker/_NSDatePickerCellDelegate -## Proxy class for NSGraphicsContext internal to AppKit -!unknown-type! NSPrintPreviewGraphicsContext bound - ## In header as old style unnamaed enum !unknown-native-enum! NSModalResponse bound !unknown-native-enum! NSWindowLevel bound From 56ffb2f440c86d0ef4858c8281a13b2788200d50 Mon Sep 17 00:00:00 2001 From: "CSIGS@microsoft.com" Date: Tue, 2 Jun 2026 00:39:41 -0700 Subject: [PATCH 46/79] LEGO: Pull request from lego/hb_5df43909-4a19-4f55-bc3f-9ea8fccf3c82_20260528175129236 to main (#25569) LEGO: Pull request from lego/hb_5df43909-4a19-4f55-bc3f-9ea8fccf3c82_20260528175129236 to main with localized lcls --- .../loc/cs/macios/src/Resources.resx.lcl | 7 ++-- .../MSBStrings.resx.lcl | 36 +++++++++++++++++++ .../loc/de/macios/src/Resources.resx.lcl | 7 ++-- .../de/macios/tools/mtouch/Errors.resx.lcl | 9 +++++ .../loc/es/macios/src/Resources.resx.lcl | 7 ++-- .../MSBStrings.resx.lcl | 36 +++++++++++++++++++ .../fr/macios/tools/mtouch/Errors.resx.lcl | 9 +++++ .../MSBStrings.resx.lcl | 36 +++++++++++++++++++ .../loc/ja/macios/src/Resources.resx.lcl | 7 ++-- .../ja/macios/tools/mtouch/Errors.resx.lcl | 9 +++++ .../MSBStrings.resx.lcl | 36 +++++++++++++++++++ .../loc/pl/macios/src/Resources.resx.lcl | 7 ++-- .../pl/macios/tools/mtouch/Errors.resx.lcl | 9 +++++ 13 files changed, 205 insertions(+), 10 deletions(-) diff --git a/macios/Localize/loc/cs/macios/src/Resources.resx.lcl b/macios/Localize/loc/cs/macios/src/Resources.resx.lcl index 9549f1d38696..8e1699b9acaf 100644 --- a/macios/Localize/loc/cs/macios/src/Resources.resx.lcl +++ b/macios/Localize/loc/cs/macios/src/Resources.resx.lcl @@ -381,10 +381,13 @@ - + - + + + + diff --git a/macios/Localize/loc/de/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/de/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index b2fc70394ad2..564e87794819 100644 --- a/macios/Localize/loc/de/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/de/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -3625,6 +3625,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macios/Localize/loc/de/macios/src/Resources.resx.lcl b/macios/Localize/loc/de/macios/src/Resources.resx.lcl index 364c5c515e6e..1c29fc1ad7ee 100644 --- a/macios/Localize/loc/de/macios/src/Resources.resx.lcl +++ b/macios/Localize/loc/de/macios/src/Resources.resx.lcl @@ -381,10 +381,13 @@ - + - + + + + diff --git a/macios/Localize/loc/de/macios/tools/mtouch/Errors.resx.lcl b/macios/Localize/loc/de/macios/tools/mtouch/Errors.resx.lcl index 272d0fd57cab..a92aca87125c 100644 --- a/macios/Localize/loc/de/macios/tools/mtouch/Errors.resx.lcl +++ b/macios/Localize/loc/de/macios/tools/mtouch/Errors.resx.lcl @@ -4573,6 +4573,15 @@ + + + + + + + + + diff --git a/macios/Localize/loc/es/macios/src/Resources.resx.lcl b/macios/Localize/loc/es/macios/src/Resources.resx.lcl index 85246e0ce950..b097266a8d41 100644 --- a/macios/Localize/loc/es/macios/src/Resources.resx.lcl +++ b/macios/Localize/loc/es/macios/src/Resources.resx.lcl @@ -381,10 +381,13 @@ - + - + + + + diff --git a/macios/Localize/loc/fr/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/fr/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index fada47500cc4..e47ef87b934e 100644 --- a/macios/Localize/loc/fr/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/fr/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -3625,6 +3625,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macios/Localize/loc/fr/macios/tools/mtouch/Errors.resx.lcl b/macios/Localize/loc/fr/macios/tools/mtouch/Errors.resx.lcl index c46996116ac1..57d5b027ef04 100644 --- a/macios/Localize/loc/fr/macios/tools/mtouch/Errors.resx.lcl +++ b/macios/Localize/loc/fr/macios/tools/mtouch/Errors.resx.lcl @@ -4573,6 +4573,15 @@ + + + + + + + + + diff --git a/macios/Localize/loc/ja/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/ja/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index f71a2b3a978f..429efcd0d6a5 100644 --- a/macios/Localize/loc/ja/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/ja/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -3625,6 +3625,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macios/Localize/loc/ja/macios/src/Resources.resx.lcl b/macios/Localize/loc/ja/macios/src/Resources.resx.lcl index b042a0a70bac..4ec32400c436 100644 --- a/macios/Localize/loc/ja/macios/src/Resources.resx.lcl +++ b/macios/Localize/loc/ja/macios/src/Resources.resx.lcl @@ -381,10 +381,13 @@ - + - + + + + diff --git a/macios/Localize/loc/ja/macios/tools/mtouch/Errors.resx.lcl b/macios/Localize/loc/ja/macios/tools/mtouch/Errors.resx.lcl index 0326c3f3dc31..0a5c0969fde0 100644 --- a/macios/Localize/loc/ja/macios/tools/mtouch/Errors.resx.lcl +++ b/macios/Localize/loc/ja/macios/tools/mtouch/Errors.resx.lcl @@ -4573,6 +4573,15 @@ + + + + + + + + + diff --git a/macios/Localize/loc/pl/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/pl/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index aff2c57e5e45..10afa0d41504 100644 --- a/macios/Localize/loc/pl/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/pl/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -3625,6 +3625,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macios/Localize/loc/pl/macios/src/Resources.resx.lcl b/macios/Localize/loc/pl/macios/src/Resources.resx.lcl index 535e5be2e3df..40459f23f097 100644 --- a/macios/Localize/loc/pl/macios/src/Resources.resx.lcl +++ b/macios/Localize/loc/pl/macios/src/Resources.resx.lcl @@ -381,10 +381,13 @@ - + - + + + + diff --git a/macios/Localize/loc/pl/macios/tools/mtouch/Errors.resx.lcl b/macios/Localize/loc/pl/macios/tools/mtouch/Errors.resx.lcl index 8e3eafdf8f62..d81fe2182e28 100644 --- a/macios/Localize/loc/pl/macios/tools/mtouch/Errors.resx.lcl +++ b/macios/Localize/loc/pl/macios/tools/mtouch/Errors.resx.lcl @@ -4573,6 +4573,15 @@ + + + + + + + + + From d3a1bda206759b5d040721960b3cd1971fe0320e Mon Sep 17 00:00:00 2001 From: "CSIGS@microsoft.com" Date: Tue, 2 Jun 2026 00:54:25 -0700 Subject: [PATCH 47/79] LEGO: Pull request from lego/hb_5df43909-4a19-4f55-bc3f-9ea8fccf3c82_20260528055153236 to main (#25558) LEGO: Pull request from lego/hb_5df43909-4a19-4f55-bc3f-9ea8fccf3c82_20260528055153236 to main with localized lcls --- .../MSBStrings.resx.lcl | 36 +++++++++++++++++++ .../es/macios/tools/mtouch/Errors.resx.lcl | 9 +++++ .../loc/fr/macios/src/Resources.resx.lcl | 7 ++-- .../MSBStrings.resx.lcl | 36 +++++++++++++++++++ .../loc/zh-Hans/macios/src/Resources.resx.lcl | 7 ++-- .../macios/tools/mtouch/Errors.resx.lcl | 9 +++++ .../MSBStrings.resx.lcl | 36 +++++++++++++++++++ .../loc/zh-Hant/macios/src/Resources.resx.lcl | 7 ++-- .../macios/tools/mtouch/Errors.resx.lcl | 9 +++++ 9 files changed, 150 insertions(+), 6 deletions(-) diff --git a/macios/Localize/loc/es/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/es/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index f20f4cb2c0c9..b692e401e9c4 100644 --- a/macios/Localize/loc/es/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/es/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -3625,6 +3625,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macios/Localize/loc/es/macios/tools/mtouch/Errors.resx.lcl b/macios/Localize/loc/es/macios/tools/mtouch/Errors.resx.lcl index 6ae060269c6e..3040949dddb1 100644 --- a/macios/Localize/loc/es/macios/tools/mtouch/Errors.resx.lcl +++ b/macios/Localize/loc/es/macios/tools/mtouch/Errors.resx.lcl @@ -4573,6 +4573,15 @@ + + + + + + + + + diff --git a/macios/Localize/loc/fr/macios/src/Resources.resx.lcl b/macios/Localize/loc/fr/macios/src/Resources.resx.lcl index 6175b9d20601..d8878181007a 100644 --- a/macios/Localize/loc/fr/macios/src/Resources.resx.lcl +++ b/macios/Localize/loc/fr/macios/src/Resources.resx.lcl @@ -381,10 +381,13 @@ - + - + + + + diff --git a/macios/Localize/loc/zh-Hans/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/zh-Hans/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index 00517d08e59d..2e41c4b28761 100644 --- a/macios/Localize/loc/zh-Hans/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/zh-Hans/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -3625,6 +3625,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macios/Localize/loc/zh-Hans/macios/src/Resources.resx.lcl b/macios/Localize/loc/zh-Hans/macios/src/Resources.resx.lcl index 2d4649df1b79..f5da49efa398 100644 --- a/macios/Localize/loc/zh-Hans/macios/src/Resources.resx.lcl +++ b/macios/Localize/loc/zh-Hans/macios/src/Resources.resx.lcl @@ -381,10 +381,13 @@ - + - + + + + diff --git a/macios/Localize/loc/zh-Hans/macios/tools/mtouch/Errors.resx.lcl b/macios/Localize/loc/zh-Hans/macios/tools/mtouch/Errors.resx.lcl index c37d02e4d78a..af179127e37e 100644 --- a/macios/Localize/loc/zh-Hans/macios/tools/mtouch/Errors.resx.lcl +++ b/macios/Localize/loc/zh-Hans/macios/tools/mtouch/Errors.resx.lcl @@ -4573,6 +4573,15 @@ + + + + + + + + + diff --git a/macios/Localize/loc/zh-Hant/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/zh-Hant/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index b8f65ed4218a..0c5e6cd287eb 100644 --- a/macios/Localize/loc/zh-Hant/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/zh-Hant/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -3625,6 +3625,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macios/Localize/loc/zh-Hant/macios/src/Resources.resx.lcl b/macios/Localize/loc/zh-Hant/macios/src/Resources.resx.lcl index 21c2a5d3082f..0074c1598564 100644 --- a/macios/Localize/loc/zh-Hant/macios/src/Resources.resx.lcl +++ b/macios/Localize/loc/zh-Hant/macios/src/Resources.resx.lcl @@ -381,10 +381,13 @@ - + - + + + + diff --git a/macios/Localize/loc/zh-Hant/macios/tools/mtouch/Errors.resx.lcl b/macios/Localize/loc/zh-Hant/macios/tools/mtouch/Errors.resx.lcl index f987a2aa5168..69beebeb0b2d 100644 --- a/macios/Localize/loc/zh-Hant/macios/tools/mtouch/Errors.resx.lcl +++ b/macios/Localize/loc/zh-Hant/macios/tools/mtouch/Errors.resx.lcl @@ -4573,6 +4573,15 @@ + + + + + + + + + From 151a329cb023cd0b488d20be70b23e7f7d7090c0 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Tue, 2 Jun 2026 00:58:42 -0700 Subject: [PATCH 48/79] Localized file check-in by OneLocBuild Task: Build definition ID 14411: Build ID 14245989 (#25593) This is the pull request automatically created by the OneLocBuild task in the build process to check-in localized files generated based upon translation source files (.lcl files) handed-back from the downstream localization pipeline. If there are issues in translations, visit https://aka.ms/icxLocBug and log bugs for fixes. The OneLocBuild wiki is https://aka.ms/onelocbuild and the localization process in general is documented at https://aka.ms/AllAboutLoc. --- .../TranslatedAssemblies/MSBStrings.cs.resx | 15 +++++++++++++++ .../TranslatedAssemblies/MSBStrings.de.resx | 15 +++++++++++++++ .../TranslatedAssemblies/MSBStrings.es.resx | 15 +++++++++++++++ .../TranslatedAssemblies/MSBStrings.fr.resx | 15 +++++++++++++++ .../TranslatedAssemblies/MSBStrings.it.resx | 15 +++++++++++++++ .../TranslatedAssemblies/MSBStrings.ja.resx | 15 +++++++++++++++ .../TranslatedAssemblies/MSBStrings.ko.resx | 15 +++++++++++++++ .../TranslatedAssemblies/MSBStrings.pl.resx | 15 +++++++++++++++ .../TranslatedAssemblies/MSBStrings.pt-BR.resx | 15 +++++++++++++++ .../TranslatedAssemblies/MSBStrings.ru.resx | 15 +++++++++++++++ .../TranslatedAssemblies/MSBStrings.tr.resx | 15 +++++++++++++++ .../TranslatedAssemblies/MSBStrings.zh-Hans.resx | 15 +++++++++++++++ .../TranslatedAssemblies/MSBStrings.zh-Hant.resx | 15 +++++++++++++++ 13 files changed, 195 insertions(+) diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.cs.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.cs.resx index f3125770202e..9f4afd35224d 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.cs.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.cs.resx @@ -1296,4 +1296,19 @@ The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + The {0} simulator runtime is not installed. This is required by Apple's development tools (even when building for physical devices). Install it by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the required simulator runtime is not installed. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + Unable to determine if the {0} simulator runtime is installed. If the build fails or hangs, install the {0} simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line. + Shown when we're unable to check for simulator runtime availability. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + The installed {0} simulator runtime is not compatible with the current Xcode version. Update the simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the tool reports a simulator runtime version mismatch. +{0} - The platform name (e.g. "iOS" or "tvOS"). + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.de.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.de.resx index f3125770202e..9f4afd35224d 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.de.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.de.resx @@ -1296,4 +1296,19 @@ The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + The {0} simulator runtime is not installed. This is required by Apple's development tools (even when building for physical devices). Install it by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the required simulator runtime is not installed. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + Unable to determine if the {0} simulator runtime is installed. If the build fails or hangs, install the {0} simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line. + Shown when we're unable to check for simulator runtime availability. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + The installed {0} simulator runtime is not compatible with the current Xcode version. Update the simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the tool reports a simulator runtime version mismatch. +{0} - The platform name (e.g. "iOS" or "tvOS"). + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.es.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.es.resx index f3125770202e..9f4afd35224d 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.es.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.es.resx @@ -1296,4 +1296,19 @@ The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + The {0} simulator runtime is not installed. This is required by Apple's development tools (even when building for physical devices). Install it by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the required simulator runtime is not installed. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + Unable to determine if the {0} simulator runtime is installed. If the build fails or hangs, install the {0} simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line. + Shown when we're unable to check for simulator runtime availability. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + The installed {0} simulator runtime is not compatible with the current Xcode version. Update the simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the tool reports a simulator runtime version mismatch. +{0} - The platform name (e.g. "iOS" or "tvOS"). + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.fr.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.fr.resx index f3125770202e..9f4afd35224d 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.fr.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.fr.resx @@ -1296,4 +1296,19 @@ The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + The {0} simulator runtime is not installed. This is required by Apple's development tools (even when building for physical devices). Install it by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the required simulator runtime is not installed. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + Unable to determine if the {0} simulator runtime is installed. If the build fails or hangs, install the {0} simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line. + Shown when we're unable to check for simulator runtime availability. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + The installed {0} simulator runtime is not compatible with the current Xcode version. Update the simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the tool reports a simulator runtime version mismatch. +{0} - The platform name (e.g. "iOS" or "tvOS"). + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.it.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.it.resx index f3125770202e..9f4afd35224d 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.it.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.it.resx @@ -1296,4 +1296,19 @@ The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + The {0} simulator runtime is not installed. This is required by Apple's development tools (even when building for physical devices). Install it by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the required simulator runtime is not installed. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + Unable to determine if the {0} simulator runtime is installed. If the build fails or hangs, install the {0} simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line. + Shown when we're unable to check for simulator runtime availability. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + The installed {0} simulator runtime is not compatible with the current Xcode version. Update the simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the tool reports a simulator runtime version mismatch. +{0} - The platform name (e.g. "iOS" or "tvOS"). + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ja.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ja.resx index f3125770202e..9f4afd35224d 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ja.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ja.resx @@ -1296,4 +1296,19 @@ The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + The {0} simulator runtime is not installed. This is required by Apple's development tools (even when building for physical devices). Install it by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the required simulator runtime is not installed. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + Unable to determine if the {0} simulator runtime is installed. If the build fails or hangs, install the {0} simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line. + Shown when we're unable to check for simulator runtime availability. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + The installed {0} simulator runtime is not compatible with the current Xcode version. Update the simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the tool reports a simulator runtime version mismatch. +{0} - The platform name (e.g. "iOS" or "tvOS"). + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ko.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ko.resx index f3125770202e..9f4afd35224d 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ko.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ko.resx @@ -1296,4 +1296,19 @@ The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + The {0} simulator runtime is not installed. This is required by Apple's development tools (even when building for physical devices). Install it by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the required simulator runtime is not installed. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + Unable to determine if the {0} simulator runtime is installed. If the build fails or hangs, install the {0} simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line. + Shown when we're unable to check for simulator runtime availability. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + The installed {0} simulator runtime is not compatible with the current Xcode version. Update the simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the tool reports a simulator runtime version mismatch. +{0} - The platform name (e.g. "iOS" or "tvOS"). + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.pl.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.pl.resx index f3125770202e..9f4afd35224d 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.pl.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.pl.resx @@ -1296,4 +1296,19 @@ The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + The {0} simulator runtime is not installed. This is required by Apple's development tools (even when building for physical devices). Install it by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the required simulator runtime is not installed. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + Unable to determine if the {0} simulator runtime is installed. If the build fails or hangs, install the {0} simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line. + Shown when we're unable to check for simulator runtime availability. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + The installed {0} simulator runtime is not compatible with the current Xcode version. Update the simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the tool reports a simulator runtime version mismatch. +{0} - The platform name (e.g. "iOS" or "tvOS"). + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.pt-BR.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.pt-BR.resx index f3125770202e..9f4afd35224d 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.pt-BR.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.pt-BR.resx @@ -1296,4 +1296,19 @@ The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + The {0} simulator runtime is not installed. This is required by Apple's development tools (even when building for physical devices). Install it by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the required simulator runtime is not installed. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + Unable to determine if the {0} simulator runtime is installed. If the build fails or hangs, install the {0} simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line. + Shown when we're unable to check for simulator runtime availability. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + The installed {0} simulator runtime is not compatible with the current Xcode version. Update the simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the tool reports a simulator runtime version mismatch. +{0} - The platform name (e.g. "iOS" or "tvOS"). + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ru.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ru.resx index f3125770202e..9f4afd35224d 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ru.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.ru.resx @@ -1296,4 +1296,19 @@ The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + The {0} simulator runtime is not installed. This is required by Apple's development tools (even when building for physical devices). Install it by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the required simulator runtime is not installed. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + Unable to determine if the {0} simulator runtime is installed. If the build fails or hangs, install the {0} simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line. + Shown when we're unable to check for simulator runtime availability. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + The installed {0} simulator runtime is not compatible with the current Xcode version. Update the simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the tool reports a simulator runtime version mismatch. +{0} - The platform name (e.g. "iOS" or "tvOS"). + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.tr.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.tr.resx index f3125770202e..9f4afd35224d 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.tr.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.tr.resx @@ -1296,4 +1296,19 @@ The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + The {0} simulator runtime is not installed. This is required by Apple's development tools (even when building for physical devices). Install it by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the required simulator runtime is not installed. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + Unable to determine if the {0} simulator runtime is installed. If the build fails or hangs, install the {0} simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line. + Shown when we're unable to check for simulator runtime availability. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + The installed {0} simulator runtime is not compatible with the current Xcode version. Update the simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the tool reports a simulator runtime version mismatch. +{0} - The platform name (e.g. "iOS" or "tvOS"). + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.zh-Hans.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.zh-Hans.resx index f3125770202e..9f4afd35224d 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.zh-Hans.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.zh-Hans.resx @@ -1296,4 +1296,19 @@ The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + The {0} simulator runtime is not installed. This is required by Apple's development tools (even when building for physical devices). Install it by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the required simulator runtime is not installed. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + Unable to determine if the {0} simulator runtime is installed. If the build fails or hangs, install the {0} simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line. + Shown when we're unable to check for simulator runtime availability. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + The installed {0} simulator runtime is not compatible with the current Xcode version. Update the simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the tool reports a simulator runtime version mismatch. +{0} - The platform name (e.g. "iOS" or "tvOS"). + \ No newline at end of file diff --git a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.zh-Hant.resx b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.zh-Hant.resx index f3125770202e..9f4afd35224d 100644 --- a/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.zh-Hant.resx +++ b/macios/msbuild/Xamarin.Localization.MSBuild/TranslatedAssemblies/MSBStrings.zh-Hant.resx @@ -1296,4 +1296,19 @@ The settings file '{0}' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use. + + The {0} simulator runtime is not installed. This is required by Apple's development tools (even when building for physical devices). Install it by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the required simulator runtime is not installed. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + Unable to determine if the {0} simulator runtime is installed. If the build fails or hangs, install the {0} simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line. + Shown when we're unable to check for simulator runtime availability. +{0} - The platform name (e.g. "iOS" or "tvOS"). + + + The installed {0} simulator runtime is not compatible with the current Xcode version. Update the simulator runtime by running 'xcodebuild -downloadPlatform {0}' from the command line, or from Xcode (Settings > Components). + Shown when the tool reports a simulator runtime version mismatch. +{0} - The platform name (e.g. "iOS" or "tvOS"). + \ No newline at end of file From 9d4392d0845ea5c8ac3ea587204f99a5f454930b Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 2 Jun 2026 12:19:42 +0200 Subject: [PATCH 49/79] [docs] Minor update to docs about how to update api docs. (#25595) --- docs/update-api-docs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/update-api-docs.md b/docs/update-api-docs.md index 58903732193a..ddb1e000babc 100644 --- a/docs/update-api-docs.md +++ b/docs/update-api-docs.md @@ -24,7 +24,7 @@ The steps are: * Copy our platform assemblies and their xml files into their corresponding directory. * Create a new commit and push it to origin. -4. Go here: [Continuous Integration](https://ops.microsoft.com/#/repos/85f784f4-01e7-ffb8-ed06-a012f7d649c0?tabName=ci) (might need VPN enabled, otherwise sometimes you'll get a 403 error page) and then: +4. Go here: [OPS dotnet/macios-api-docs / Continuous Integration](https://ops.microsoft.com/#/repos/85f784f4-01e7-ffb8-ed06-a012f7d649c0?tabName=ci) (if you have to go through the authentication workflow you'll end up on the OPS homepage, in which case just click the link again) and then: * Expand the '.NET macios API docs' job, and then: * Change `Target Repo` -> `Target Branch` to `netX.Y-xcodeZ.W` (same branch as created above). From 18077ca662f1ea0292ded2e5c180ef66982487ea Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 2 Jun 2026 13:31:44 +0200 Subject: [PATCH 50/79] [tests] Fix flaky BasicAuthWorksWhenBearerIsAdvertisedFirst test. Fixes #25597 (#25604) The test was getting HTTP 404 on a CI bot, but the local HttpListener server never returns 404. The likely cause is localhost resolving to ::1 (IPv6) on certain macOS bots while HttpListener with http://*:port/ only binds to IPv4. The request reaches something else and gets 404. Fix: - Use 127.0.0.1 explicitly in both the HttpListener prefix and the request URL to avoid IPv6/hostname resolution mismatches. - Add CI tolerance: if the server received zero requests and the status is 404, mark the test as inconclusive (infrastructure issue, not a code bug). - Add IgnoreInCIIfBadNetwork and timeout handling consistent with other tests in this file. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../NSUrlSessionHandlerTest.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/monotouch-test/System.Net.Http/NSUrlSessionHandlerTest.cs b/tests/monotouch-test/System.Net.Http/NSUrlSessionHandlerTest.cs index 8b1fe66f384a..79c787d387c1 100644 --- a/tests/monotouch-test/System.Net.Http/NSUrlSessionHandlerTest.cs +++ b/tests/monotouch-test/System.Net.Http/NSUrlSessionHandlerTest.cs @@ -286,14 +286,27 @@ public void BasicAuthWorksWhenBearerIsAdvertisedFirst () using var handler = new NSUrlSessionHandler (); handler.Credentials = new NetworkCredential (username, password); using var client = new HttpClient (handler); - using var request = new HttpRequestMessage (HttpMethod.Get, $"http://localhost:{listeningPort}/test"); + // Use 127.0.0.1 instead of localhost to avoid IPv6 resolution + // issues where NSUrlSession may connect to ::1 while + // HttpListener only binds to IPv4. + using var request = new HttpRequestMessage (HttpMethod.Get, $"http://127.0.0.1:{listeningPort}/test"); var response = await client.SendAsync (request).ConfigureAwait (false); statusCode = response.StatusCode; responseBody = await response.Content.ReadAsStringAsync ().ConfigureAwait (false); }, out var ex); - Assert.That (done, Is.True, "Request timed out"); + if (!done) { + TestRuntime.IgnoreInCI ("Transient localhost server failure - ignore in CI"); + Assert.Inconclusive ("Request timed out."); + } + TestRuntime.IgnoreInCIIfBadNetwork (ex); Assert.That (ex, Is.Null, $"Exception: {ex}"); + // If no request reached the server, the failure is an infrastructure + // issue (e.g. port conflict), not a code bug. + if (Volatile.Read (ref requestIndex) == 0 && statusCode == HttpStatusCode.NotFound) { + TestRuntime.IgnoreInCI ($"Server received no requests and got status {statusCode} - infrastructure issue, ignore in CI"); + Assert.Inconclusive ($"Server received no requests; status was {statusCode}. Likely a port/binding issue."); + } Assert.That (statusCode, Is.EqualTo (HttpStatusCode.OK), "Expected 200 OK after Basic auth negotiation"); Assert.That (responseBody, Is.EqualTo ("authenticated"), "Response body"); Assert.That (firstUnauthenticatedIndex, Is.GreaterThan (0), "Server should have received an unauthenticated request"); @@ -316,7 +329,7 @@ public void BasicAuthWorksWhenBearerIsAdvertisedFirst () for (var port = MinPort; port < MaxPort; port++) { var listener = new HttpListener (); - listener.Prefixes.Add ($"http://*:{port}/"); + listener.Prefixes.Add ($"http://127.0.0.1:{port}/"); try { listener.Start (); listeningPort = port; From 47eb243a3eed2249e64aa9e166915212f75353d4 Mon Sep 17 00:00:00 2001 From: "CSIGS@microsoft.com" Date: Tue, 2 Jun 2026 04:44:20 -0700 Subject: [PATCH 51/79] LEGO: Pull request from lego/hb_5df43909-4a19-4f55-bc3f-9ea8fccf3c82_20260529053637714 to main (#25575) LEGO: Pull request from lego/hb_5df43909-4a19-4f55-bc3f-9ea8fccf3c82_20260529053637714 to main with localized lcls --- .../MSBStrings.resx.lcl | 36 +++++++++++++++++++ .../cs/macios/tools/mtouch/Errors.resx.lcl | 9 +++++ .../MSBStrings.resx.lcl | 36 +++++++++++++++++++ .../loc/it/macios/src/Resources.resx.lcl | 7 ++-- .../it/macios/tools/mtouch/Errors.resx.lcl | 9 +++++ .../MSBStrings.resx.lcl | 36 +++++++++++++++++++ .../loc/ko/macios/src/Resources.resx.lcl | 7 ++-- .../ko/macios/tools/mtouch/Errors.resx.lcl | 9 +++++ .../MSBStrings.resx.lcl | 36 +++++++++++++++++++ .../loc/pt-BR/macios/src/Resources.resx.lcl | 7 ++-- .../pt-BR/macios/tools/mtouch/Errors.resx.lcl | 9 +++++ .../MSBStrings.resx.lcl | 36 +++++++++++++++++++ .../loc/ru/macios/src/Resources.resx.lcl | 7 ++-- .../ru/macios/tools/mtouch/Errors.resx.lcl | 9 +++++ .../MSBStrings.resx.lcl | 36 +++++++++++++++++++ .../loc/tr/macios/src/Resources.resx.lcl | 7 ++-- .../tr/macios/tools/mtouch/Errors.resx.lcl | 9 +++++ 17 files changed, 295 insertions(+), 10 deletions(-) diff --git a/macios/Localize/loc/cs/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/cs/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index 620e5dd4fb7f..44c49d101321 100644 --- a/macios/Localize/loc/cs/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/cs/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -3625,6 +3625,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macios/Localize/loc/cs/macios/tools/mtouch/Errors.resx.lcl b/macios/Localize/loc/cs/macios/tools/mtouch/Errors.resx.lcl index 029bc250773a..e737c9d8e469 100644 --- a/macios/Localize/loc/cs/macios/tools/mtouch/Errors.resx.lcl +++ b/macios/Localize/loc/cs/macios/tools/mtouch/Errors.resx.lcl @@ -4573,6 +4573,15 @@ + + + + + + + + + diff --git a/macios/Localize/loc/it/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/it/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index f1a86dc8993b..c67c30add6ac 100644 --- a/macios/Localize/loc/it/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/it/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -3625,6 +3625,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macios/Localize/loc/it/macios/src/Resources.resx.lcl b/macios/Localize/loc/it/macios/src/Resources.resx.lcl index 8b231c676455..c6f87770593a 100644 --- a/macios/Localize/loc/it/macios/src/Resources.resx.lcl +++ b/macios/Localize/loc/it/macios/src/Resources.resx.lcl @@ -381,10 +381,13 @@ - + - + + + + diff --git a/macios/Localize/loc/it/macios/tools/mtouch/Errors.resx.lcl b/macios/Localize/loc/it/macios/tools/mtouch/Errors.resx.lcl index 5a5bbaefc1ae..70d4408c9f26 100644 --- a/macios/Localize/loc/it/macios/tools/mtouch/Errors.resx.lcl +++ b/macios/Localize/loc/it/macios/tools/mtouch/Errors.resx.lcl @@ -4573,6 +4573,15 @@ + + + + + + + + + diff --git a/macios/Localize/loc/ko/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/ko/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index 638a95bf9892..8f6a9309a649 100644 --- a/macios/Localize/loc/ko/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/ko/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -3625,6 +3625,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macios/Localize/loc/ko/macios/src/Resources.resx.lcl b/macios/Localize/loc/ko/macios/src/Resources.resx.lcl index 2a6ec47780f7..b6f9d3f7c969 100644 --- a/macios/Localize/loc/ko/macios/src/Resources.resx.lcl +++ b/macios/Localize/loc/ko/macios/src/Resources.resx.lcl @@ -381,10 +381,13 @@ - + - + + + + diff --git a/macios/Localize/loc/ko/macios/tools/mtouch/Errors.resx.lcl b/macios/Localize/loc/ko/macios/tools/mtouch/Errors.resx.lcl index 36785bba0430..073974f830fe 100644 --- a/macios/Localize/loc/ko/macios/tools/mtouch/Errors.resx.lcl +++ b/macios/Localize/loc/ko/macios/tools/mtouch/Errors.resx.lcl @@ -4573,6 +4573,15 @@ + + + + + + + + + diff --git a/macios/Localize/loc/pt-BR/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/pt-BR/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index e9b8116173e4..b36703f288b6 100644 --- a/macios/Localize/loc/pt-BR/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/pt-BR/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -3625,6 +3625,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macios/Localize/loc/pt-BR/macios/src/Resources.resx.lcl b/macios/Localize/loc/pt-BR/macios/src/Resources.resx.lcl index d12b83b1ff3e..b7ce10acb40c 100644 --- a/macios/Localize/loc/pt-BR/macios/src/Resources.resx.lcl +++ b/macios/Localize/loc/pt-BR/macios/src/Resources.resx.lcl @@ -381,10 +381,13 @@ - + - + + + + diff --git a/macios/Localize/loc/pt-BR/macios/tools/mtouch/Errors.resx.lcl b/macios/Localize/loc/pt-BR/macios/tools/mtouch/Errors.resx.lcl index cb12d7bf13cc..7e153e535bc9 100644 --- a/macios/Localize/loc/pt-BR/macios/tools/mtouch/Errors.resx.lcl +++ b/macios/Localize/loc/pt-BR/macios/tools/mtouch/Errors.resx.lcl @@ -4573,6 +4573,15 @@ + + + + + + + + + diff --git a/macios/Localize/loc/ru/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/ru/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index 80fd78c3f715..ff190b437318 100644 --- a/macios/Localize/loc/ru/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/ru/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -3625,6 +3625,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macios/Localize/loc/ru/macios/src/Resources.resx.lcl b/macios/Localize/loc/ru/macios/src/Resources.resx.lcl index 7bc3cfac722a..095e1f672a82 100644 --- a/macios/Localize/loc/ru/macios/src/Resources.resx.lcl +++ b/macios/Localize/loc/ru/macios/src/Resources.resx.lcl @@ -381,10 +381,13 @@ - + - + + + + diff --git a/macios/Localize/loc/ru/macios/tools/mtouch/Errors.resx.lcl b/macios/Localize/loc/ru/macios/tools/mtouch/Errors.resx.lcl index 720865503b6d..a0a2d50d4c8a 100644 --- a/macios/Localize/loc/ru/macios/tools/mtouch/Errors.resx.lcl +++ b/macios/Localize/loc/ru/macios/tools/mtouch/Errors.resx.lcl @@ -4573,6 +4573,15 @@ + + + + + + + + + diff --git a/macios/Localize/loc/tr/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/tr/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index 94ffb3bad606..3251464cd07f 100644 --- a/macios/Localize/loc/tr/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/tr/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -3625,6 +3625,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macios/Localize/loc/tr/macios/src/Resources.resx.lcl b/macios/Localize/loc/tr/macios/src/Resources.resx.lcl index 87909f7e2e5e..4cf6e27fa0dc 100644 --- a/macios/Localize/loc/tr/macios/src/Resources.resx.lcl +++ b/macios/Localize/loc/tr/macios/src/Resources.resx.lcl @@ -381,10 +381,13 @@ - + - + + + + diff --git a/macios/Localize/loc/tr/macios/tools/mtouch/Errors.resx.lcl b/macios/Localize/loc/tr/macios/tools/mtouch/Errors.resx.lcl index 69c6f943eadf..2aedad71123b 100644 --- a/macios/Localize/loc/tr/macios/tools/mtouch/Errors.resx.lcl +++ b/macios/Localize/loc/tr/macios/tools/mtouch/Errors.resx.lcl @@ -4573,6 +4573,15 @@ + + + + + + + + + From 58bb1f6407cf9f495463b20918f1dbc45a98dc27 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 2 Jun 2026 12:16:39 +0000 Subject: [PATCH 52/79] [main] Update dependencies from dotnet/dotnet (#25587) This pull request updates the following dependencies ## From https://github.com/dotnet/dotnet - **Subscription**: [da09b56a-0fb1-439a-b894-def14d2ec0a4](https://maestro.dot.net/subscriptions?search=da09b56a-0fb1-439a-b894-def14d2ec0a4) - **Build**: [20260531.4](https://dev.azure.com/dnceng/internal/_build/results?buildId=2988460) ([316516](https://maestro.dot.net/channel/10307/github:dotnet:dotnet/build/316516)) - **Date Produced**: May 31, 2026 7:18:38 PM UTC - **Commit**: [aec921632e75e1f29327709dd52e98c41f3b55cf](https://github.com/dotnet/dotnet/commit/aec921632e75e1f29327709dd52e98c41f3b55cf) - **Branch**: [release/10.0.4xx](https://github.com/dotnet/dotnet/tree/release/10.0.4xx) - **Dependency Updates**: - From [10.0.0-beta.26277.109 to 10.0.0-beta.26281.104][2] - Microsoft.DotNet.Arcade.Sdk - Microsoft.DotNet.Build.Tasks.Feed - Microsoft.DotNet.SharedFramework.Sdk - From [10.0.400-preview.0.26277.109 to 10.0.400-preview.0.26281.104][2] - Microsoft.NET.Sdk - From [10.0.400-preview.26277.109 to 10.0.400-preview.26281.104][2] - Microsoft.TemplateEngine.Authoring.Tasks [2]: https://github.com/dotnet/dotnet/compare/2f68b99e48...aec921632e --- eng/Version.Details.props | 10 +++++----- eng/Version.Details.xml | 20 ++++++++++---------- global.json | 6 +++--- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 5277e7173d29..51bd77497e11 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -6,16 +6,16 @@ This file should be imported by eng/Versions.props - 10.0.0-beta.26277.109 - 10.0.0-beta.26277.109 + 10.0.0-beta.26281.104 + 10.0.0-beta.26281.104 0.11.5-alpha.26070.104 - 10.0.0-beta.26277.109 + 10.0.0-beta.26281.104 10.0.3-servicing.26070.104 10.0.3 10.0.3 - 10.0.400-preview.0.26277.109 + 10.0.400-preview.0.26281.104 10.0.3 - 10.0.400-preview.26277.109 + 10.0.400-preview.26281.104 26.0.11017 18.5.9227 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index e53abe16f371..f31e396323df 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,8 +1,8 @@ - + https://github.com/dotnet/dotnet - 2f68b99e483cd8f4acbc1aa365fc806f1bd2374b + aec921632e75e1f29327709dd52e98c41f3b55cf https://github.com/dotnet/dotnet @@ -95,25 +95,25 @@ - + https://github.com/dotnet/dotnet - 2f68b99e483cd8f4acbc1aa365fc806f1bd2374b + aec921632e75e1f29327709dd52e98c41f3b55cf - + https://github.com/dotnet/dotnet - 2f68b99e483cd8f4acbc1aa365fc806f1bd2374b + aec921632e75e1f29327709dd52e98c41f3b55cf - + https://github.com/dotnet/dotnet - 2f68b99e483cd8f4acbc1aa365fc806f1bd2374b + aec921632e75e1f29327709dd52e98c41f3b55cf https://github.com/dotnet/xharness 51ca379106cfd749a498cb0822210ef1aa926e41 - + https://github.com/dotnet/dotnet - 2f68b99e483cd8f4acbc1aa365fc806f1bd2374b + aec921632e75e1f29327709dd52e98c41f3b55cf diff --git a/global.json b/global.json index 294c78cab356..b2855704689d 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "10.0.400-preview.0.26277.109", + "version": "10.0.400-preview.0.26281.104", "paths": [ "builds/downloads/dotnet", "$host$" @@ -8,9 +8,9 @@ "errorMessage": "The .NET SDK could not be found, please run 'make dotnet -C builds'." }, "tools": { - "dotnet": "10.0.400-preview.0.26277.109" + "dotnet": "10.0.400-preview.0.26281.104" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26277.109" + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26281.104" } } From 194f285b35461c96a08d507ab907f31aeab40abb Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 2 Jun 2026 14:47:03 +0200 Subject: [PATCH 53/79] [bgen] Cache raw GetCustomAttributesData() results per provider (#25564) MetadataLoadContext's `GetCustomAttributesData()` returns a fresh `ReadOnlyCollection` on every call. Since bgen queries multiple attribute types per provider (via `GetCustomAttributes`, `HasAttribute`, `HasAttribute(string)`, `IsNullable`), the same provider's raw attribute list was being allocated and discarded many times over. Add a `Dictionary>` that caches the raw result per provider. Changed `GetAttributes()` from static to instance to enable this cache. `HasAttribute(string)` also changed from static to instance (only one caller, already via instance). ``` Baseline average (3 runs): 24.7s, 9164 MB total, gen0=1660, gen1=513 After average (3 runs): 22.1s, 6990 MB total, gen0=1265, gen1=417 Time: -2.6s (-10.5%) Memory: -2174 MB (-23.7%) GC: gen0 -395 (-23.8%), gen1 -96 (-18.7%) ``` --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Rolf Bjarne Kvinge --- src/bgen/AttributeManager.cs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/bgen/AttributeManager.cs b/src/bgen/AttributeManager.cs index 9ae2768b7670..4e3b3acbb927 100644 --- a/src/bgen/AttributeManager.cs +++ b/src/bgen/AttributeManager.cs @@ -18,6 +18,9 @@ public class AttributeManager { "System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute", }; + // Cache raw GetCustomAttributesData() results per provider to avoid repeated reflection allocations. + readonly Dictionary> rawAttributeCache = new (); + TypeCache TypeCache { get; } public AttributeManager (TypeCache typeCache) @@ -501,9 +504,15 @@ public virtual T [] GetCustomAttributes (ICustomAttributeProvider? provider) } [return: NotNullIfNotNull (nameof (provider))] - static IList? GetAttributes (ICustomAttributeProvider? provider) - => provider switch { - null => null, + IList? GetAttributes (ICustomAttributeProvider? provider) + { + if (provider is null) + return null; + + if (rawAttributeCache.TryGetValue (provider, out var cached)) + return cached; + + IList result = provider switch { MemberInfo member => member.GetCustomAttributesData (), Assembly assembly => assembly.GetCustomAttributesData (), ParameterInfo pinfo => pinfo.GetCustomAttributesData (), @@ -511,7 +520,11 @@ public virtual T [] GetCustomAttributes (ICustomAttributeProvider? provider) _ => throw new BindingException (1051, true, provider.GetType ().FullName) }; - public static bool HasAttribute (ICustomAttributeProvider provider, string type_name) + rawAttributeCache [provider] = result; + return result; + } + + public bool HasAttribute (ICustomAttributeProvider provider, string type_name) { var attribs = GetAttributes (provider); for (int i = 0; i < attribs.Count; i++) From a6b6c3cddb41ce3eeafe9211c697de86df43330b Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 2 Jun 2026 16:55:37 +0200 Subject: [PATCH 54/79] [tools] Use instance-based Log methods. (#25566) This will become important soon, because much of the code here will run from inside an MSBuild task, and we mustn't call Console.[Error.]WriteLine from inside an MSBuild task (it can cause tasks to deadlock). With these changes it'll be much easier to remap Log calls to MSBuild's Task.LogMessage method. --- msbuild/Xamarin.MacDev.Tasks/Decompress.cs | 2 +- msbuild/Xamarin.MacDev.Tasks/Tasks/BGen.cs | 4 +- .../Tasks/CreateBindingResourcePackage.cs | 2 +- .../Tasks/MergeAppBundles.cs | 6 +- .../Tasks/ParseBundlerArguments.cs | 4 +- .../Xamarin.MacDev.Tasks/Tasks/XamarinTask.cs | 80 ++++---- .../Xamarin.MacDev.Tasks/VerbosityUtils.cs | 41 ++-- .../Xamarin.MacDev.Tasks.csproj | 3 + msbuild/Xamarin.Shared/Xamarin.Shared.targets | 4 +- src/ObjCRuntime/DynamicRegistrar.cs | 2 +- src/ObjCRuntime/Registrar.cs | 9 +- src/bgen/BindingTouch.cs | 46 ++++- src/bgen/bgen.csproj | 1 + tests/dotnet/UnitTests/BundleStructureTest.cs | 4 +- tests/dotnet/UnitTests/DotNetUnitTests.csproj | 3 + .../TaskTests/ParseBundlerArgumentsTests.cs | 2 +- .../VerbosityTest.cs | 16 +- tools/common/Application.cs | 65 ++++-- tools/common/Assembly.cs | 98 ++++----- tools/common/CoreResolver.cs | 20 +- tools/common/Driver.cs | 127 ++++-------- tools/common/Driver.execution.cs | 77 ++++--- tools/common/ErrorHelper.tools.cs | 59 +++--- tools/common/FileCopier.cs | 191 +++++++----------- tools/common/Frameworks.cs | 2 +- tools/common/IToolLog.cs | 68 +++++++ tools/common/MachO.cs | 42 ++-- tools/common/PInvokeWrapperGenerator.cs | 4 +- tools/common/StaticRegistrar.cs | 32 +-- tools/common/Target.cs | 12 +- tools/common/cache.cs | 139 +++++-------- .../dotnet-linker/BackingFieldDelayHandler.cs | 7 +- tools/dotnet-linker/DotNetResolver.cs | 4 + tools/dotnet-linker/LinkerConfiguration.cs | 100 ++++----- .../PreserveSmartEnumConversionsStep.cs | 11 +- .../Steps/ExceptionalMarkHandler.cs | 2 + .../Steps/InlineClassGetHandleStep.cs | 20 +- .../Steps/InlineDlfcnMethodsStep.cs | 6 +- .../Steps/ManagedRegistrarLookupTablesStep.cs | 4 +- .../Steps/ManagedRegistrarStep.cs | 2 +- tools/dotnet-linker/dotnet-linker.csproj | 3 + tools/linker/CoreOptimizeGeneratedCode.cs | 159 ++++++++------- .../MonoTouch.Tuner/ListExportedSymbols.cs | 6 +- tools/linker/RegistrarRemovalTrackingStep.cs | 4 +- tools/mtouch/AssemblyResolver.cs | 20 +- tools/mtouch/mtouch.csproj | 3 + 46 files changed, 754 insertions(+), 762 deletions(-) create mode 100644 tools/common/IToolLog.cs diff --git a/msbuild/Xamarin.MacDev.Tasks/Decompress.cs b/msbuild/Xamarin.MacDev.Tasks/Decompress.cs index 2d4f755378de..234eb49cd852 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Decompress.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Decompress.cs @@ -108,7 +108,7 @@ public static bool TryDecompress (XamarinTask task, string zip, string resource, var stampFile = decompressedResource.TrimEnd ('\\', '/') + ".stamp"; - if (FileCopier.IsUptodate (zip, stampFile, XamarinTask.GetFileCopierReportErrorCallback (log), XamarinTask.GetFileCopierLogCallback (log), check_stamp: false)) + if (FileCopier.IsUptodate (task, zip, stampFile, check_stamp: false)) return true; // We use 'unzip' to extract on !Windows, and System.IO.Compression to extract on Windows. diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/BGen.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/BGen.cs index 1dce72ab1386..cfa8c01e85cf 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/BGen.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/BGen.cs @@ -55,8 +55,6 @@ public class BGen : XamarinTask, ICancelableTask { public string ExtraArgs { get; set; } = string.Empty; - public int Verbosity { get; set; } - public string GeneratedSourcesDir { get; set; } = string.Empty; public string GeneratedSourcesFileList { get; set; } = string.Empty; @@ -229,7 +227,7 @@ public virtual List GenerateCommandLineArguments () } } - cmd.AddRange (VerbosityUtils.Merge (ExtraArgs, (LoggerVerbosity) Verbosity)); + VerbosityUtils.RenderVerbosity (cmd, Verbosity); return CommandLineArgumentBuilder.CreateResponseFile (this, ResponseFilePath, cmd, null); } diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/CreateBindingResourcePackage.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/CreateBindingResourcePackage.cs index 17d859c5ae4d..722104a9e119 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/CreateBindingResourcePackage.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/CreateBindingResourcePackage.cs @@ -107,7 +107,7 @@ public override bool Execute () Log.LogMessage (MSBStrings.M0121, bindingResourcePath); Directory.CreateDirectory (bindingResourcePath); foreach (var nativeRef in NativeReferences) { - Xamarin.Bundler.FileCopier.UpdateDirectory (nativeRef.ItemSpec, bindingResourcePath, FileCopierReportErrorCallback, FileCopierLogCallback); + Xamarin.Bundler.FileCopier.UpdateDirectory (this, nativeRef.ItemSpec, bindingResourcePath); var bindingOutputPath = Path.Combine (bindingResourcePath, Path.GetFileName (nativeRef.ItemSpec)); if (Directory.Exists (bindingOutputPath)) { diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/MergeAppBundles.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/MergeAppBundles.cs index dd9f8bf3ad13..591180e66f26 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/MergeAppBundles.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/MergeAppBundles.cs @@ -215,7 +215,7 @@ public void CopyTo (string outputDirectory, string? subDirectory = null) } } else { Directory.CreateDirectory (Path.GetDirectoryName (outputFile)!); - if (!FileCopier.IsUptodate (FullPath, outputFile, Task.FileCopierReportErrorCallback, Task.FileCopierLogCallback)) + if (!FileCopier.IsUptodate (Task, FullPath, outputFile)) File.Copy (FullPath, outputFile, true); } @@ -246,7 +246,7 @@ public override bool Execute () sourceDirectory += Path.DirectorySeparatorChar; Log.LogMessage (MessageImportance.Low, $"Copying the single input directory {sourceDirectory} to {targetDirectory}"); - FileCopier.UpdateDirectory (sourceDirectory, targetDirectory, FileCopierReportErrorCallback, FileCopierLogCallback); + FileCopier.UpdateDirectory (this, sourceDirectory, targetDirectory); return !Log.HasLoggedErrors; } @@ -428,7 +428,7 @@ void MergeMachOFiles (string output, IList input) var sourceFiles = input.Select (v => v.FullPath).ToArray (); - if (FileCopier.IsUptodate (sourceFiles, new string [] { output }, FileCopierReportErrorCallback, FileCopierLogCallback)) + if (FileCopier.IsUptodate (this, sourceFiles, new string [] { output })) return; Log.LogMessage (MessageImportance.Low, $"Lipoing '{input [0].RelativePath}' for the merged app bundle from the following sources:\n\t{string.Join ("\n\t", input.Select (v => v.FullPath))}"); diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/ParseBundlerArguments.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/ParseBundlerArguments.cs index 3da5e058b86a..3f2ba75108b7 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/ParseBundlerArguments.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/ParseBundlerArguments.cs @@ -64,7 +64,7 @@ public class ParseBundlerArguments : XamarinTask { public string? SkipMarkingNSObjectsInUserAssemblies { get; set; } [Output] - public string? Verbosity { get; set; } + public string? BundlerVerbosity { get; set; } [Output] public string? Warn { get; set; } @@ -307,7 +307,7 @@ public override bool Execute () } if (verbosity.HasValue) - Verbosity = verbosity.Value.ToString (); + BundlerVerbosity = verbosity.Value.ToString (); } return !Log.HasLoggedErrors; diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/XamarinTask.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/XamarinTask.cs index 2ad8f996c312..3cd5a9386821 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/XamarinTask.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/XamarinTask.cs @@ -9,6 +9,7 @@ using Microsoft.Build.Tasks; using Microsoft.Build.Utilities; +using Xamarin.Bundler; using Xamarin.Localization.MSBuild; using Xamarin.Messaging.Build.Client; using Xamarin.Utils; @@ -17,7 +18,7 @@ #nullable enable namespace Xamarin.MacDev.Tasks { - public abstract class XamarinTask : Task, IHasSessionId, ICustomLogger { + public abstract class XamarinTask : Task, IHasSessionId, ICustomLogger, IToolLog { public string SessionId { get; set; } = string.Empty; @@ -25,6 +26,16 @@ public abstract class XamarinTask : Task, IHasSessionId, ICustomLogger { public string SdkDevPath { get; set; } = string.Empty; + int? verbosity; + public int Verbosity { + get { + if (!verbosity.HasValue) + verbosity = VerbosityUtils.GetVerbosityLevel (Environment.CommandLine); + return verbosity.Value; + } + set => verbosity = value; + } + public string GetSdkDevPath () { if (string.IsNullOrEmpty (SdkDevPath)) { @@ -278,48 +289,6 @@ internal static bool CopyInputsToRemoteServerAsync (T task) where T : Task, I } } - internal protected static ReportErrorCallback GetFileCopierReportErrorCallback (TaskLoggingHelper log) - { - return new ReportErrorCallback ((int code, string format, object? [] arguments) => { - FileCopierReportErrorCallback (log, code, format, arguments); - }); - } - - internal protected static void FileCopierReportErrorCallback (TaskLoggingHelper log, int code, string format, params object? [] arguments) - { - log.LogError (format, arguments); - } - - protected void FileCopierReportErrorCallback (int code, string format, params object? [] arguments) - { - FileCopierReportErrorCallback (Log, code, format, arguments); - } - - internal protected static LogCallback GetFileCopierLogCallback (TaskLoggingHelper log) - { - return new LogCallback ((int min_verbosity, string format, object? [] arguments) => { - FileCopierLogCallback (log, min_verbosity, format, arguments); - }); - } - - protected static void FileCopierLogCallback (TaskLoggingHelper log, int min_verbosity, string format, params object? [] arguments) - { - MessageImportance importance; - if (min_verbosity <= 0) { - importance = MessageImportance.High; - } else if (min_verbosity <= 1) { - importance = MessageImportance.Normal; - } else { - importance = MessageImportance.Low; - } - log.LogMessage (importance, format, arguments); - } - - protected void FileCopierLogCallback (int min_verbosity, string format, params object? [] arguments) - { - FileCopierLogCallback (Log, min_verbosity, format, arguments); - } - protected string GetNonEmptyStringOrFallback (ITaskItem item, string metadataName, string fallbackValue, string? fallbackName = null, bool required = false) { return GetNonEmptyStringOrFallback (item, metadataName, out var _, fallbackValue, fallbackName, required); @@ -400,7 +369,8 @@ protected static string GetExecutable (List arguments, string toolName, #region Xamarin.MacDev.ICustomLogger void ICustomLogger.LogError (string message, Exception? ex) { - Log.LogError (message); + if (!string.IsNullOrEmpty (message)) + Log.LogError (message); if (ex is not null) Log.LogErrorFromException (ex); } @@ -420,5 +390,27 @@ void ICustomLogger.LogDebug (string messageFormat, params object? [] args) Log.LogMessage (MessageImportance.Low, messageFormat, args); } #endregion + + #region Xamarin.Bundler.IToolLog + void IToolLog.Log (string message) + { + ((ICustomLogger) this).LogInfo (message); + } + + void IToolLog.LogError (string message) + { + ((ICustomLogger) this).LogError (message, null); + } + + void IToolLog.LogException (Exception exception) + { + ((ICustomLogger) this).LogError ("", exception); + } + + void IToolLog.LogError (Exception exception) + { + ((ICustomLogger) this).LogError ("", exception); + } + #endregion } } diff --git a/msbuild/Xamarin.MacDev.Tasks/VerbosityUtils.cs b/msbuild/Xamarin.MacDev.Tasks/VerbosityUtils.cs index f0797ef078fd..02211f93bcc3 100644 --- a/msbuild/Xamarin.MacDev.Tasks/VerbosityUtils.cs +++ b/msbuild/Xamarin.MacDev.Tasks/VerbosityUtils.cs @@ -10,39 +10,28 @@ namespace Xamarin.MacDev.Tasks { // needs to be verbosity-aware public static class VerbosityUtils { - // verbosity can be set in multiple ways - // this makes it a consistent interpretation of them - static public string [] Merge (string extraArguments, LoggerVerbosity taskVerbosity) + public static void RenderVerbosity (IList arguments, int taskVerbosity) { - string [] result = Array.Empty (); - var empty_extra = String.IsNullOrEmpty (extraArguments); - // We give the priority to the extra arguments given to the tools - if (empty_extra || (!empty_extra && !extraArguments.Contains ("-q") && !extraArguments.Contains ("-v"))) { - // if nothing is specified fall back to msbuild settings - // first check if some were supplied on the command-line - result = GetVerbosityLevel (Environment.CommandLine); - // if not then use the default from the msbuild config files, which Visual Studio for Mac can override (to match the IDE setting for msbuild) - if (result.Length == 0) - result = GetVerbosityLevel (taskVerbosity); - } - return result; + if (taskVerbosity == 0) + return; + + for (var i = 0; i < Math.Abs (taskVerbosity); i++) + arguments.Add (taskVerbosity < 0 ? "-q" : "-v"); } // // This is an hack, since there can be multiple loggers. - // However it's the most common use case and `mtouch` logs - // are often the most important to gather and developers expect - // a single change (in verbosity) to do the job and be consistent in CI. + // However it should cover most use cases. // // msbuild argument format // -verbosity: Display this amount of information in the event log. // The available verbosity levels are: q[uiet], m[inimal], // n[ormal], d[etailed], and diag[nostic]. (Short form: -v) // - static public string [] GetVerbosityLevel (string commandLine) + public static int GetVerbosityLevel (string commandLine) { if (!StringUtils.TryParseArguments (commandLine, out var args, out _)) - return GetVerbosityLevel (LoggerVerbosity.Normal); + return 0; var hasBinaryLog = false; foreach (var arg in args) { @@ -99,20 +88,20 @@ static public string [] GetVerbosityLevel (string commandLine) // The values here come from: https://github.com/mono/monodevelop/blob/143f9b6617123a0841a5cc5a2a4e13b309535792/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/RemoteBuildEngineMessages.cs#L186 // Assume 'Normal (2)' is the default verbosity (no change), and the other values follow from there. - static public string [] GetVerbosityLevel (LoggerVerbosity v) + public static int GetVerbosityLevel (LoggerVerbosity v) { switch ((LoggerVerbosity) v) { case LoggerVerbosity.Quiet: - return new [] { "-q", "-q", "-q", "-q" }; + return -4; case LoggerVerbosity.Minimal: - return new [] { "-q", "-q" }; + return -2; case LoggerVerbosity.Normal: default: - return Array.Empty (); + return 0; case LoggerVerbosity.Detailed: - return new [] { "-v", "-v" }; + return 2; case LoggerVerbosity.Diagnostic: - return new [] { "-v", "-v", "-v", "-v" }; + return 4; } } } diff --git a/msbuild/Xamarin.MacDev.Tasks/Xamarin.MacDev.Tasks.csproj b/msbuild/Xamarin.MacDev.Tasks/Xamarin.MacDev.Tasks.csproj index 8f4555f6dfd4..713db306ebef 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Xamarin.MacDev.Tasks.csproj +++ b/msbuild/Xamarin.MacDev.Tasks/Xamarin.MacDev.Tasks.csproj @@ -75,6 +75,9 @@ JsonExtensions.cs + + IToolLog.cs + StringUtils.cs diff --git a/msbuild/Xamarin.Shared/Xamarin.Shared.targets b/msbuild/Xamarin.Shared/Xamarin.Shared.targets index 2c5f43be44b8..8307a85ebf48 100644 --- a/msbuild/Xamarin.Shared/Xamarin.Shared.targets +++ b/msbuild/Xamarin.Shared/Xamarin.Shared.targets @@ -2298,7 +2298,7 @@ Copyright (C) 2018 Microsoft. All rights reserved. NoDSymUtil="$(NoDSymUtil)" PackageDebugSymbols="$(PackageDebugSymbols)" Registrar="$(Registrar)" - Verbosity="$(_BundlerVerbosity)" + BundlerVerbosity="$(_BundlerVerbosity)" > @@ -2315,7 +2315,7 @@ Copyright (C) 2018 Microsoft. All rights reserved. - + diff --git a/src/ObjCRuntime/DynamicRegistrar.cs b/src/ObjCRuntime/DynamicRegistrar.cs index 93dc571ced6b..0c27e3d8130f 100644 --- a/src/ObjCRuntime/DynamicRegistrar.cs +++ b/src/ObjCRuntime/DynamicRegistrar.cs @@ -806,7 +806,7 @@ protected override bool TryGetAttribute (Type type, string attributeNamespace, s return attribute is not null; } - protected override void ReportError (int code, string message, params object [] args) + protected override void ReportError (int code, string message, params object? [] args) { Runtime.NSLog (String.Format (message, args)); } diff --git a/src/ObjCRuntime/Registrar.cs b/src/ObjCRuntime/Registrar.cs index 3b7c402bd3cf..d33f63746e84 100644 --- a/src/ObjCRuntime/Registrar.cs +++ b/src/ObjCRuntime/Registrar.cs @@ -2741,8 +2741,9 @@ protected string ToSignature (TType type, ObjCMember? member, ref bool success, return "#"; if (IsINativeObject (type)) { - if (!IsGenericType (type) && !IsInterface (type) && !IsNSObject (type) && IsAbstract (type)) - ErrorHelper.Show (CreateWarning (4179, member, Errors.MT4179, type.FullName, member?.FullName)); + if (!IsGenericType (type) && !IsInterface (type) && !IsNSObject (type) && IsAbstract (type)) { + ReportWarning (4179, Errors.MT4179, type.FullName, member?.FullName); + } if (IsNSObject (type) && forProperty) { return "@\"" + GetExportedTypeName (type) + "\""; } else { @@ -2811,7 +2812,7 @@ internal static void NSLog (string format, params object [] args) } #endif - protected virtual void ReportError (int code, string message, params object [] args) + protected virtual void ReportError (int code, string message, params object? [] args) { // Using Console.WriteLine here is error prone, since if we get an early error // we'll end up crashing/infinite recursion since Console.WriteLine is redirected @@ -2820,7 +2821,7 @@ protected virtual void ReportError (int code, string message, params object [] a R.NSLog (String.Format (message, args)); } - protected virtual void ReportWarning (int code, string message, params object [] args) + protected virtual void ReportWarning (int code, string message, params object? [] args) { // Using Console.WriteLine here is error prone, since if we get an early error // we'll end up crashing/infinite recursion since Console.WriteLine is redirected diff --git a/src/bgen/BindingTouch.cs b/src/bgen/BindingTouch.cs index 5afd87e41b01..72aa883e0985 100644 --- a/src/bgen/BindingTouch.cs +++ b/src/bgen/BindingTouch.cs @@ -42,7 +42,7 @@ using System.Threading; #endif -public class BindingTouch : IDisposable { +public class BindingTouch : IDisposable, IToolLog { public static ApplePlatform [] AllPlatforms = new ApplePlatform [] { ApplePlatform.iOS, ApplePlatform.MacOSX, ApplePlatform.TVOS, ApplePlatform.MacCatalyst }; public static PlatformName [] AllPlatformNames = new PlatformName [] { PlatformName.iOS, PlatformName.MacOSX, PlatformName.TvOS, PlatformName.MacCatalyst }; public PlatformName CurrentPlatform; @@ -156,8 +156,8 @@ public bool TryCreateOptionSet (BindingTouchConfig config, string [] args) { "d=", "Defines a symbol", v => config.Defines.Add (v) }, { "api=", "Adds a API definition source file", v => config.ApiSources.Add (v) }, { "s=", "Adds a source file required to build the API", v => config.CoreSources.Add (v) }, - { "q", "Quiet", v => ErrorHelper.Verbosity-- }, - { "v", "Sets verbose mode", v => ErrorHelper.Verbosity++ }, + { "q", "Quiet", v => Verbosity-- }, + { "v", "Sets verbose mode", v => Verbosity++ }, { "x=", "Adds the specified file to the build, used after the core files are compiled", v => config.ExtraSources.Add (v) }, { "e", "Generates smaller classes that can not be subclassed (previously called 'external mode')", v => config.IsExternal = true }, { "p", "Sets private mode", v => config.IsPublicMode = false }, @@ -522,7 +522,7 @@ void Compile (List arguments, int errorCode, string? tmpdir) arguments.Insert (i - 1, compile_command [i]); } - if (Driver.RunCommand (compile_command [0], arguments, null, out var compile_output, true, Driver.Verbosity) != 0) + if (Driver.RunCommand (this, compile_command [0], arguments, null, out var compile_output, true, Verbosity) != 0) throw ErrorHelper.CreateError (errorCode, $"{compiler} {StringUtils.FormatArguments (arguments)}\n{compile_output}".Replace ("\n", "\n\t")); var output = string.Join (Environment.NewLine, compile_output.ToString ().Split (new char [] { '\n' }, StringSplitOptions.RemoveEmptyEntries)); if (!string.IsNullOrEmpty (output)) @@ -537,10 +537,10 @@ bool TryLoadApi (string? name, [NotNullWhen (true)] out Assembly? assembly) try { assembly = universe?.LoadFromAssemblyPath (name); } catch (Exception e) { - if (Driver.Verbosity > 0) - Console.WriteLine (e); + if (Verbosity > 0) + Log (e.ToString ()); - Console.Error.WriteLine ("Error loading {0}", name); + LogError ($"Error loading {name}"); } return assembly is not null; @@ -575,4 +575,36 @@ public void Dispose () Dispose (disposing: true); GC.SuppressFinalize (this); } + + public void Log (string message) + { + Console.WriteLine (message); + } + + public void LogError (string message) + { + Console.Error.WriteLine (message); + } + + public void LogError (Exception exception) + { + ErrorHelper.Show (exception); + } + + public void LogException (Exception exception) + { + ErrorHelper.Show (exception); + } + + int verbosity = 0; + public int Verbosity { + get => verbosity; + set => verbosity = value; + } +} + +namespace Xamarin.Bundler { + public partial class Driver { + public static int GetDefaultVerbosity () => 0; + } } diff --git a/src/bgen/bgen.csproj b/src/bgen/bgen.csproj index 8e7a65e7d227..fa063bd5b43a 100644 --- a/src/bgen/bgen.csproj +++ b/src/bgen/bgen.csproj @@ -36,6 +36,7 @@ + diff --git a/tests/dotnet/UnitTests/BundleStructureTest.cs b/tests/dotnet/UnitTests/BundleStructureTest.cs index f01bbb58fd5f..56d6ae67f259 100644 --- a/tests/dotnet/UnitTests/BundleStructureTest.cs +++ b/tests/dotnet/UnitTests/BundleStructureTest.cs @@ -1,5 +1,7 @@ #nullable enable +using Xamarin.Bundler; + namespace Xamarin.Tests { [TestFixture] public class BundleStructureTest : TestBaseClass { @@ -793,7 +795,7 @@ static void AssertLibraryArchitectures (string appBundle, string [] runtimeIdent return false; }); foreach (var lib in libraries) { - var libArchitectures = renderArchitectures (MachO.GetArchitectures (lib)); + var libArchitectures = renderArchitectures (MachO.GetArchitectures (ConsoleLog.Instance, lib)); Assert.That (libArchitectures, Is.EqualTo (expectedArchitectures), $"Architectures in {lib}"); } } diff --git a/tests/dotnet/UnitTests/DotNetUnitTests.csproj b/tests/dotnet/UnitTests/DotNetUnitTests.csproj index f78948890a0e..e0bbb37b7ce5 100644 --- a/tests/dotnet/UnitTests/DotNetUnitTests.csproj +++ b/tests/dotnet/UnitTests/DotNetUnitTests.csproj @@ -45,6 +45,9 @@ external\Cache.cs + + external\IToolLog.cs + external\TargetFramework.cs diff --git a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ParseBundlerArgumentsTests.cs b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ParseBundlerArgumentsTests.cs index bea30b23b970..48ea288f44ad 100644 --- a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ParseBundlerArgumentsTests.cs +++ b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ParseBundlerArgumentsTests.cs @@ -250,7 +250,7 @@ public void Verbosity (string input, string output) var task = CreateTask (); task.ExtraArgs = input; ExecuteTask (task, message: input); - Assert.That (task.Verbosity, Is.EqualTo (output), "Equality"); + Assert.That (task.BundlerVerbosity, Is.EqualTo (output), "Equality"); } [TestCase ("--nowarn", "-1")] diff --git a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/VerbosityTest.cs b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/VerbosityTest.cs index 87765cf7267e..4930ef8dc74c 100644 --- a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/VerbosityTest.cs +++ b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/VerbosityTest.cs @@ -43,15 +43,15 @@ public void FromString (string commandLine, LoggerVerbosity expected) Assert.That (VerbosityUtils.GetVerbosityLevel (commandLine), Is.EqualTo (result), commandLine); } - [TestCase (LoggerVerbosity.Quiet, "-q -q -q -q")] - [TestCase (LoggerVerbosity.Minimal, "-q -q")] - [TestCase (LoggerVerbosity.Normal, "")] - [TestCase (LoggerVerbosity.Detailed, "-v -v")] - [TestCase (LoggerVerbosity.Diagnostic, "-v -v -v -v")] - [TestCase ((LoggerVerbosity) (-1), "")] - public void FromLoggerVerbosity (LoggerVerbosity v, string expectedResult) + [TestCase (LoggerVerbosity.Quiet, -4)] + [TestCase (LoggerVerbosity.Minimal, -2)] + [TestCase (LoggerVerbosity.Normal, 0)] + [TestCase (LoggerVerbosity.Detailed, 2)] + [TestCase (LoggerVerbosity.Diagnostic, 4)] + [TestCase ((LoggerVerbosity) (-1), 0)] + public void FromLoggerVerbosity (LoggerVerbosity v, int expectedResult) { - var s = String.Join (" ", VerbosityUtils.GetVerbosityLevel (v)); + var s = VerbosityUtils.GetVerbosityLevel (v); Assert.That (s, Is.EqualTo (expectedResult), v.ToString ()); } } diff --git a/tools/common/Application.cs b/tools/common/Application.cs index 56f008d19b66..6aad8900c70c 100644 --- a/tools/common/Application.cs +++ b/tools/common/Application.cs @@ -68,7 +68,7 @@ public enum RegistrarMode { TrimmableStatic, } - public partial class Application { + public partial class Application : IToolLog { public Cache? Cache; public string AppDirectory = "."; public bool DeadStrip = true; @@ -268,7 +268,7 @@ public Version GetMacCatalystiOSVersion (Version macOSVersion) #if LEGACY_TOOLS if (macOSVersion.Major >= 26 && Driver.SdkRoot is null) { // this shouldn't happen for normal builds, nor for customers, so just show an internal 99 warning. - ErrorHelper.Warning (99, Errors.MX0099, $"No Xcode configured, assuming the macOS version {macOSVersion} is identical to the Mac Catalyst/iOS version."); + ErrorHelper.Warning (this, 99, Errors.MX0099, $"No Xcode configured, assuming the macOS version {macOSVersion} is identical to the Mac Catalyst/iOS version."); return macOSVersion; } #endif @@ -286,6 +286,7 @@ public Application (LinkerConfiguration configuration) this.LinkContext = new Tuner.DerivedLinkContext (configuration, this); #endif this.StaticRegistrar = new StaticRegistrar (this); + this.Resolver = new PlatformResolver (this); } #if !LEGACY_TOOLS @@ -419,9 +420,9 @@ public string PlatformName { } } - public static bool IsUptodate (string source, string target, bool check_contents = false, bool check_stamp = true) + public static bool IsUptodate (IToolLog log, string source, string target, bool check_contents = false, bool check_stamp = true) { - return FileCopier.IsUptodate (source, target, check_contents, check_stamp); + return FileCopier.IsUptodate (log, source, target, check_contents, check_stamp); } public static void RemoveResource (ModuleDefinition module, string name) @@ -493,14 +494,14 @@ public static bool ExtractResource (ModuleDefinition module, string name, string // // If check_stamp is true, the function will use the timestamp of a "target".stamp file // if it's later than the timestamp of the "target" file itself. - public static bool IsUptodate (IEnumerable sources, IEnumerable targets, bool check_stamp = true) + public static bool IsUptodate (IToolLog log, IEnumerable sources, IEnumerable targets, bool check_stamp = true) { - return FileCopier.IsUptodate (sources, targets, check_stamp); + return FileCopier.IsUptodate (log, sources, targets, check_stamp); } - public static void UpdateDirectory (string source, string target) + public static void UpdateDirectory (IToolLog log, string source, string target) { - FileCopier.UpdateDirectory (source, target); + FileCopier.UpdateDirectory (log, source, target); } public void InitializeCommon () @@ -539,13 +540,13 @@ public void InitializeCommon () if (!package_managed_debug_symbols.HasValue) { package_managed_debug_symbols = EnableDebug; } else if (package_managed_debug_symbols.Value && IsLLVM) { - ErrorHelper.Warning (3007, Errors.MX3007); + ErrorHelper.Warning (this, 3007, Errors.MX3007); } Optimizations.Initialize (this, out var messages); - ErrorHelper.Show (messages); - if (Driver.Verbosity > 3) - Driver.Log (4, $"Enabled optimizations: {Optimizations}"); + ErrorHelper.Show (this, messages); + if (this.Verbosity > 3) + this.Log (4, $"Enabled optimizations: {Optimizations}"); } void InitializeDeploymentTarget () @@ -575,7 +576,7 @@ public void RunRegistrar () throw ErrorHelper.CreateError (99, "RegistrarOutputLibrary must be specified."); var RootAssembly = RootAssemblies [0]; var resolvedAssemblies = new Dictionary (); - var resolver = new PlatformResolver () { + var resolver = new PlatformResolver (this) { RootDirectory = Path.GetDirectoryName (RootAssembly), }; resolver.Configure (); @@ -583,7 +584,7 @@ public void RunRegistrar () var ps = new ReaderParameters (); ps.AssemblyResolver = resolver; foreach (var reference in References) { - var r = resolver.Load (reference); + var r = resolver.Load (this, reference); if (r is null) throw ErrorHelper.CreateError (2002, Errors.MT2002, reference); } @@ -598,21 +599,21 @@ public void RunRegistrar () try { AssemblyDefinition lastAssembly = ps.AssemblyResolver.Resolve (AssemblyNameReference.Parse (rootName), new ReaderParameters ()); if (lastAssembly is null) { - ErrorHelper.Warning (7, Errors.MX0007, rootName); + ErrorHelper.Warning (this, 7, Errors.MX0007, rootName); continue; } if (resolvedAssemblies.TryGetValue (rootName, out var previousAssembly)) { if (lastAssembly.MainModule.RuntimeVersion != previousAssembly.MainModule.RuntimeVersion) { - Driver.Log (2, "Attemping to load an assembly another time {0} (previous {1})", lastAssembly.FullName, previousAssembly.FullName); + this.Log (2, "Attemping to load an assembly another time {0} (previous {1})", lastAssembly.FullName, previousAssembly.FullName); } continue; } resolvedAssemblies.Add (rootName, lastAssembly); - Driver.Log (3, "Loaded {0}", lastAssembly.MainModule.FileName); + this.Log (3, "Loaded {0}", lastAssembly.MainModule.FileName); } catch (Exception ex) { - ErrorHelper.Warning (9, ex, Errors.MX0009, $"{rootName}: {ex.Message}"); + ErrorHelper.Warning (this, 9, ex, Errors.MX0009, $"{rootName}: {ex.Message}"); continue; } } @@ -1191,7 +1192,7 @@ public bool VerifyDynamicFramework (string framework_path) } if (!dynamic) - Driver.Log (1, "The framework {0} is a framework of static libraries, and will not be copied to the app.", framework_path); + this.Log (1, "The framework {0} is a framework of static libraries, and will not be copied to the app.", framework_path); return dynamic; } @@ -1209,5 +1210,31 @@ public static void SetDefaultHiddenWarnings () ErrorHelper.ParseWarningLevel (ErrorHelper.WarningLevel.Disable, "4189"); // The class '{0}' will not be registered because it has been removed from the {1} SDK. ErrorHelper.ParseWarningLevel (ErrorHelper.WarningLevel.Disable, "4190"); // The class '{0}' will not be registered because the {1} framework has been deprecated from the {2} SDK. } + + public void Log (string message) + { + Console.WriteLine (message); + } + + public void LogError (string message) + { + Console.Error.WriteLine (message); + } + + public void LogError (Exception exception) + { + ErrorHelper.Show (this, exception); + } + + public void LogException (Exception exception) + { + ErrorHelper.Show (this, exception); + } + + int verbosity = Driver.GetDefaultVerbosity (); + public int Verbosity { + get => verbosity; + set => verbosity = value; + } } } diff --git a/tools/common/Assembly.cs b/tools/common/Assembly.cs index 1ec7535fa828..6ad8d9141958 100644 --- a/tools/common/Assembly.cs +++ b/tools/common/Assembly.cs @@ -69,7 +69,7 @@ public string FullPath { #if !LEGACY_TOOLS is_framework_assembly = App.Configuration.FrameworkAssemblies.Contains (GetIdentity (full_path)); #else - var real_full_path = Application.GetRealPath (full_path); + var real_full_path = Application.GetRealPath (App, full_path); is_framework_assembly = real_full_path.StartsWith (Path.GetDirectoryName (Path.GetDirectoryName (App.Resolver.FrameworkDirectory))!, StringComparison.Ordinal); #endif } @@ -132,7 +132,7 @@ public void LoadSymbols () } } catch { // do not let stale file crash us - Driver.Log (3, "Invalid debugging symbols for {0} ignored", FullPath); + App.Log (3, "Invalid debugging symbols for {0} ignored", FullPath); } } @@ -155,12 +155,12 @@ public void ExtractNativeLinkInfo () string resourceBundlePath = Path.ChangeExtension (FullPath, ".resources"); if (Directory.Exists (resourceBundlePath)) { - Driver.Log (3, $"Found a binding resource package for the assembly '{FullPath}' in {resourceBundlePath}, so not looking for any libraries embedded in the assembly."); + App.Log (3, $"Found a binding resource package for the assembly '{FullPath}' in {resourceBundlePath}, so not looking for any libraries embedded in the assembly."); return; } var zipPath = resourceBundlePath + ".zip"; if (File.Exists (zipPath)) { - Driver.Log (3, $"Found a binding resource package for the assembly '{FullPath}' in {zipPath}, so not looking for any libraries embedded in the assembly."); + App.Log (3, $"Found a binding resource package for the assembly '{FullPath}' in {zipPath}, so not looking for any libraries embedded in the assembly."); return; } @@ -242,7 +242,7 @@ void ProcessNativeReferenceOptions (NativeReferenceMetadata metadata) { // We can't add -dead_strip if there are any LinkWith attributes where smart linking is disabled. if (!metadata.SmartLink) { - Driver.Log (3, $"The library '{metadata.LibraryName}', shipped with the assembly '{FullPath}', sets SmartLink=false, which will disable passing -dead_strip to the native linker (and make the app bigger)."); + App.Log (3, $"The library '{metadata.LibraryName}', shipped with the assembly '{FullPath}', sets SmartLink=false, which will disable passing -dead_strip to the native linker (and make the app bigger)."); App.DeadStrip = false; } @@ -290,23 +290,23 @@ bool TryExtractNativeLibrary (AssemblyDefinition assembly, NativeReferenceMetada library = null; return false; } - var path = Path.Combine (App.Cache.Location, metadata.LibraryName); + var path = Path.Combine (App.Cache.GetLocation (App), metadata.LibraryName); library = null; - if (!Application.IsUptodate (FullPath, path)) { + if (!Application.IsUptodate (App, FullPath, path)) { if (!Application.ExtractResource (assembly.MainModule, metadata.LibraryName, path, false)) { - ErrorHelper.Warning (1308, Errors.MX1308 /* Could not extract the native library '{0}' from the assembly '{1}', because it doesn't contain the resource '{2}'. */, metadata.LibraryName, FullPath, metadata.LibraryName); + ErrorHelper.Warning (App, 1308, Errors.MX1308 /* Could not extract the native library '{0}' from the assembly '{1}', because it doesn't contain the resource '{2}'. */, metadata.LibraryName, FullPath, metadata.LibraryName); return false; } - Driver.Log (3, "Extracted third-party binding '{0}' from '{1}' to '{2}'", metadata.LibraryName, FullPath, path); + App.Log (3, "Extracted third-party binding '{0}' from '{1}' to '{2}'", metadata.LibraryName, FullPath, path); LogNativeReference (metadata); } else { - Driver.Log (3, "Target '{0}' is up-to-date.", path); + App.Log (3, "Target '{0}' is up-to-date.", path); } if (!File.Exists (path)) - ErrorHelper.Warning (1302, Errors.MT1302, metadata.LibraryName, path); + ErrorHelper.Warning (App, 1302, Errors.MT1302, metadata.LibraryName, path); library = path; return true; @@ -318,39 +318,39 @@ bool TryExtractFramework (AssemblyDefinition assembly, NativeReferenceMetadata m framework = null; return false; } - var path = Path.Combine (App.Cache.Location, metadata.LibraryName); + var path = Path.Combine (App.Cache.GetLocation (App), metadata.LibraryName); var zipPath = path + ".zip"; framework = null; - if (!Application.IsUptodate (FullPath, zipPath)) { + if (!Application.IsUptodate (App, FullPath, zipPath)) { if (!Application.ExtractResource (assembly.MainModule, metadata.LibraryName, zipPath, false)) { - ErrorHelper.Warning (1307, Errors.MX1307 /* Could not extract the native framework '{0}' from the assembly '{1}', because it doesn't contain the resource '{2}'. */, metadata.LibraryName, FullPath, metadata.LibraryName); + ErrorHelper.Warning (App, 1307, Errors.MX1307 /* Could not extract the native framework '{0}' from the assembly '{1}', because it doesn't contain the resource '{2}'. */, metadata.LibraryName, FullPath, metadata.LibraryName); return false; } - Driver.Log (3, "Extracted third-party framework '{0}' from '{1}' to '{2}'", metadata.LibraryName, FullPath, zipPath); + App.Log (3, "Extracted third-party framework '{0}' from '{1}' to '{2}'", metadata.LibraryName, FullPath, zipPath); LogNativeReference (metadata); } else { - Driver.Log (3, "Target '{0}' is up-to-date.", path); + App.Log (3, "Target '{0}' is up-to-date.", path); } if (!File.Exists (zipPath)) { - ErrorHelper.Warning (1302, Errors.MT1302, metadata.LibraryName, FullPath); + ErrorHelper.Warning (App, 1302, Errors.MT1302, metadata.LibraryName, FullPath); if (assembly.MainModule.HasResources) { - Driver.Log (3, $"The assembly {FullPath} has {assembly.MainModule.Resources.Count} resources:"); + App.Log (3, $"The assembly {FullPath} has {assembly.MainModule.Resources.Count} resources:"); foreach (var res in assembly.MainModule.Resources) { - Driver.Log (3, $" {res.ResourceType}: {res.Name}"); + App.Log (3, $" {res.ResourceType}: {res.Name}"); } } else { - Driver.Log (3, $"The assembly {FullPath} does not have any resources."); + App.Log (3, $"The assembly {FullPath} does not have any resources."); } } else { if (!Directory.Exists (path)) Directory.CreateDirectory (path); - if (Driver.RunCommand ("/usr/bin/unzip", "-u", "-o", "-d", path, zipPath) != 0) + if (Driver.RunCommand (App, "/usr/bin/unzip", "-u", "-o", "-d", path, zipPath) != 0) throw ErrorHelper.CreateError (1303, Errors.MT1303, metadata.LibraryName, zipPath); } @@ -358,19 +358,19 @@ bool TryExtractFramework (AssemblyDefinition assembly, NativeReferenceMetadata m return true; } - static void LogNativeReference (NativeReferenceMetadata metadata) + void LogNativeReference (NativeReferenceMetadata metadata) { - Driver.Log (3, " LibraryName: {0}", metadata.LibraryName); - Driver.Log (3, " From: {0}", metadata.Attribute is not null ? "LinkWith" : "Binding Manifest"); - Driver.Log (3, " ForceLoad: {0}", metadata.ForceLoad); - Driver.Log (3, " Frameworks: {0}", metadata.Frameworks); - Driver.Log (3, " IsCxx: {0}", metadata.IsCxx); - Driver.Log (3, " LinkWithSwiftSystemLibraries: {0}", metadata.LinkWithSwiftSystemLibraries); - Driver.Log (3, " LinkerFlags: {0}", metadata.LinkerFlags); - Driver.Log (3, " LinkTarget: {0}", metadata.LinkTarget); - Driver.Log (3, " NeedsGccExceptionHandling: {0}", metadata.NeedsGccExceptionHandling); - Driver.Log (3, " SmartLink: {0}", metadata.SmartLink); - Driver.Log (3, " WeakFrameworks: {0}", metadata.WeakFrameworks); + App.Log (3, " LibraryName: {0}", metadata.LibraryName); + App.Log (3, " From: {0}", metadata.Attribute is not null ? "LinkWith" : "Binding Manifest"); + App.Log (3, " ForceLoad: {0}", metadata.ForceLoad); + App.Log (3, " Frameworks: {0}", metadata.Frameworks); + App.Log (3, " IsCxx: {0}", metadata.IsCxx); + App.Log (3, " LinkWithSwiftSystemLibraries: {0}", metadata.LinkWithSwiftSystemLibraries); + App.Log (3, " LinkerFlags: {0}", metadata.LinkerFlags); + App.Log (3, " LinkTarget: {0}", metadata.LinkTarget); + App.Log (3, " NeedsGccExceptionHandling: {0}", metadata.NeedsGccExceptionHandling); + App.Log (3, " SmartLink: {0}", metadata.SmartLink); + App.Log (3, " WeakFrameworks: {0}", metadata.WeakFrameworks); } public static LinkWithAttribute GetLinkWithAttribute (CustomAttribute attr) @@ -438,12 +438,12 @@ void AddFramework (string file) { if (Driver.GetFrameworks (App).TryGetValue (file, out var framework)) { if (framework.IsFrameworkUnavailable (App)) { - ErrorHelper.Warning (182, Errors.MX0182 /* Not linking with the framework {0} (referenced by a module reference in {1}) because it's not available on the current platform ({2}). */, framework.Name, FileName, App.PlatformName); + ErrorHelper.Warning (App, 182, Errors.MX0182 /* Not linking with the framework {0} (referenced by a module reference in {1}) because it's not available on the current platform ({2}). */, framework.Name, FileName, App.PlatformName); return; } if (framework.Version > App.SdkVersion) { - ErrorHelper.Warning (135, Errors.MX0135, file, FileName, App.PlatformName, framework.Version, App.SdkVersion); + ErrorHelper.Warning (App, 135, Errors.MX0135, file, FileName, App.PlatformName, framework.Version, App.SdkVersion); return; } } @@ -451,10 +451,10 @@ void AddFramework (string file) var strong = (framework is null) || (App.DeploymentTarget >= (App.IsSimulatorBuild ? framework.VersionAvailableInSimulator ?? framework.Version : framework.Version)); if (strong) { if (Frameworks.Add (file)) - Driver.Log (3, "Linking with the framework {0} because it's referenced by a module reference in {1}", file, FileName); + App.Log (3, "Linking with the framework {0} because it's referenced by a module reference in {1}", file, FileName); } else { if (WeakFrameworks.Add (file)) - Driver.Log (3, "Linking (weakly) with the framework {0} because it's referenced by a module reference in {1}", file, FileName); + App.Log (3, "Linking (weakly) with the framework {0} because it's referenced by a module reference in {1}", file, FileName); } } @@ -491,7 +491,7 @@ public void ComputeLinkerFlags () string file = Path.GetFileNameWithoutExtension (name); if (App.IsFrameworkUnavailable (file)) { - Driver.Log (3, "Not linking with {0} (referenced by a module reference in {1}) because it's not available in the current SDK.", file, FileName); + App.Log (3, "Not linking with {0} (referenced by a module reference in {1}) because it's not available in the current SDK.", file, FileName); continue; } @@ -510,12 +510,12 @@ public void ComputeLinkerFlags () break; case "sqlite3": LinkerFlags.Add ("-lsqlite3"); - Driver.Log (3, "Linking with {0} because it's referenced by a module reference in {1}", file, FileName); + App.Log (3, "Linking with {0} because it's referenced by a module reference in {1}", file, FileName); break; case "libsqlite3": // remove lib prefix LinkerFlags.Add ("-l" + file.Substring (3)); - Driver.Log (3, "Linking with {0} because it's referenced by a module reference in {1}", file, FileName); + App.Log (3, "Linking with {0} because it's referenced by a module reference in {1}", file, FileName); break; case "libcompression": LinkerFlags.Add (GetCompressionLinkingFlag ()); @@ -524,22 +524,22 @@ public void ComputeLinkerFlags () case "libGLESv2": // special case for OpenGLES.framework if (Frameworks.Add ("OpenGLES")) - Driver.Log (3, "Linking with the framework OpenGLES because {0} is referenced by a module reference in {1}", file, FileName); + App.Log (3, "Linking with the framework OpenGLES because {0} is referenced by a module reference in {1}", file, FileName); break; case "vImage": case "vecLib": // sub-frameworks if (Frameworks.Add ("Accelerate")) - Driver.Log (3, "Linking with the framework Accelerate because {0} is referenced by a module reference in {1}", file, FileName); + App.Log (3, "Linking with the framework Accelerate because {0} is referenced by a module reference in {1}", file, FileName); break; case "openal32": if (Frameworks.Add ("OpenAL")) - Driver.Log (3, "Linking with the framework OpenAL because {0} is referenced by a module reference in {1}", file, FileName); + App.Log (3, "Linking with the framework OpenAL because {0} is referenced by a module reference in {1}", file, FileName); break; #if !LEGACY_TOOLS case "Carbon": if (App.Platform != ApplePlatform.MacOSX) { - Driver.Log (3, $"Not linking with the framework {file} (referenced by a module reference in {FileName}) because it doesn't exist on the target platform."); + App.Log (3, $"Not linking with the framework {file} (referenced by a module reference in {FileName}) because it doesn't exist on the target platform."); break; } break; @@ -553,13 +553,13 @@ public void ComputeLinkerFlags () // CoreServices has multiple sub-frameworks that can be used by customer code if (path.StartsWith ("/System/Library/Frameworks/CoreServices.framework/", StringComparison.Ordinal)) { if (Frameworks.Add ("CoreServices")) - Driver.Log (3, "Linking with the framework CoreServices because {0} is referenced by a module reference in {1}", file, FileName); + App.Log (3, "Linking with the framework CoreServices because {0} is referenced by a module reference in {1}", file, FileName); break; } // ApplicationServices has multiple sub-frameworks that can be used by customer code if (path.StartsWith ("/System/Library/Frameworks/ApplicationServices.framework/", StringComparison.Ordinal)) { if (Frameworks.Add ("ApplicationServices")) - Driver.Log (3, "Linking with the framework ApplicationServices because {0} is referenced by a module reference in {1}", file, FileName); + App.Log (3, "Linking with the framework ApplicationServices because {0} is referenced by a module reference in {1}", file, FileName); break; } } @@ -572,7 +572,7 @@ public void ComputeLinkerFlags () if (UnresolvedModuleReferences is null) UnresolvedModuleReferences = new HashSet (); UnresolvedModuleReferences.Add (mr); - Driver.Log (3, "Could not resolve the module reference {0} in {1}", file, FileName); + App.Log (3, "Could not resolve the module reference {0} in {1}", file, FileName); } break; } @@ -698,14 +698,14 @@ public void Update (Application app, IEnumerable assemblies) // new assembly var asm = new Assembly (app, assembly); Add (asm); - Driver.Log (1, "The linker added the assembly '{0}' to '{1}' to satisfy a reference.", asm.Identity, app.Name); + app.Log (1, "The linker added the assembly '{0}' to '{1}' to satisfy a reference.", asm.Identity, app.Name); } else { this [identity].AssemblyDefinition = assembly; } } foreach (var removed in current) { - Driver.Log (1, "The linker removed the assembly '{0}' from '{1}' since there is no more reference to it.", this [removed].Identity, app.Name); + app.Log (1, "The linker removed the assembly '{0}' from '{1}' since there is no more reference to it.", this [removed].Identity, app.Name); Remove (removed); } } diff --git a/tools/common/CoreResolver.cs b/tools/common/CoreResolver.cs index 458da5cf086c..b35f575c9fc1 100644 --- a/tools/common/CoreResolver.cs +++ b/tools/common/CoreResolver.cs @@ -60,7 +60,7 @@ protected ReaderParameters CreateDefaultReaderParameters (string path) return parameters; } - public virtual AssemblyDefinition? Load (string fileName) + public virtual AssemblyDefinition? Load (IToolLog log, string fileName) { if (!File.Exists (fileName)) return null; @@ -70,7 +70,7 @@ protected ReaderParameters CreateDefaultReaderParameters (string path) return assembly; try { - fileName = Application.GetRealPath (fileName); + fileName = Application.GetRealPath (log, fileName); // Check the architecture-specific directory if (Path.GetDirectoryName (fileName) == FrameworkDirectory && !string.IsNullOrEmpty (ArchDirectory)) { @@ -89,14 +89,14 @@ protected ReaderParameters CreateDefaultReaderParameters (string path) // Warn about this. var pdb = Path.ChangeExtension (fileName, "pdb"); if (File.Exists (pdb)) - ErrorHelper.Show (ErrorHelper.CreateWarning (178, Errors.MX0178, fileName)); + ErrorHelper.Show (log, ErrorHelper.CreateWarning (178, Errors.MX0178, fileName)); } // Don't load native .pdb symbols, because we won't be able to write them back out again (so just drop them) if (assembly.MainModule?.SymbolReader?.GetType ()?.FullName == "Mono.Cecil.Pdb.NativePdbReader") { parameters.ReadSymbols = false; parameters.SymbolReaderProvider = null; assembly = ModuleDefinition.ReadModule (fileName, parameters).Assembly; - ErrorHelper.Show (ErrorHelper.CreateWarning (178, Errors.MX0178, fileName)); + ErrorHelper.Show (log, ErrorHelper.CreateWarning (178, Errors.MX0178, fileName)); } } catch (IOException ex) when (ex.GetType ().FullName == "Microsoft.Cci.Pdb.PdbException") { // Microsoft.Cci.Pdb.PdbException is not public, so we have to check the runtime type :/ symbolLoadFailure = true; @@ -108,7 +108,7 @@ protected ReaderParameters CreateDefaultReaderParameters (string path) parameters.SymbolReaderProvider = null; assembly = ModuleDefinition.ReadModule (fileName, parameters).Assembly; // only report the warning (on symbols) if we can actually load the assembly itself (otherwise it's more confusing than helpful) - ErrorHelper.Show (ErrorHelper.CreateWarning (129, Errors.MX0129, fileName)); + ErrorHelper.Show (log, ErrorHelper.CreateWarning (129, Errors.MX0129, fileName)); } } catch (Exception e) { throw new ProductException (9, true, e, Errors.MX0009, fileName); @@ -126,23 +126,23 @@ public AssemblyDefinition CacheAssembly (AssemblyDefinition assembly) return assembly; } - protected AssemblyDefinition? SearchDirectory (string name, string directory, string extension = ".dll") + protected AssemblyDefinition? SearchDirectory (IToolLog log, string name, string directory, string extension = ".dll") { if (!Directory.Exists (directory)) return null; - var file = DirectoryGetFile (directory, name + extension); + var file = DirectoryGetFile (log, directory, name + extension); if (file.Length > 0) - return Load (file); + return Load (log, file); return null; } - static string DirectoryGetFile (string directory, string file) + static string DirectoryGetFile (IToolLog log, string directory, string file) { var files = Directory.GetFiles (directory, file); if (files is not null && files.Length > 0) { if (files.Length > 1) { - ErrorHelper.Warning (133, Errors.MX0133, file, Environment.NewLine, string.Join ("\n", files)); + ErrorHelper.Warning (log, 133, Errors.MX0133, file, Environment.NewLine, string.Join ("\n", files)); } return files [0]; } diff --git a/tools/common/Driver.cs b/tools/common/Driver.cs index 5ff3ad4d8fe9..c04f81cd0221 100644 --- a/tools/common/Driver.cs +++ b/tools/common/Driver.cs @@ -27,12 +27,12 @@ public static int Main (string [] args) { try { Console.OutputEncoding = new UTF8Encoding (false, false); - SetCurrentLanguage (); + SetCurrentLanguage (ConsoleLog.Instance); return Main2 (args); } catch (Exception e) { - ErrorHelper.Show (e); + ErrorHelper.Show (ConsoleLog.Instance, e); } finally { - Watch ("Total time", 0); + Watch (ConsoleLog.Instance, "Total time", 0); } return 0; } @@ -40,8 +40,8 @@ public static int Main (string [] args) // Returns true if the process should exit (with a 0 exit code; failures are propagated using exceptions) static void ParseOptions (Application app, Mono.Options.OptionSet options, string [] args) { - options.Add ("v|verbose", "Specify how verbose the output should be. This can be passed multiple times to increase the verbosity.", v => Verbosity++); - options.Add ("q|quiet", "Specify how quiet the output should be. This can be passed multiple times to increase the silence.", v => Verbosity--); + options.Add ("v|verbose", "Specify how verbose the output should be. This can be passed multiple times to increase the verbosity.", v => app.Verbosity++); + options.Add ("q|quiet", "Specify how quiet the output should be. This can be passed multiple times to increase the silence.", v => app.Verbosity--); options.Add ("reference=", "Add an assembly to be processed.", v => app.References.Add (v)); options.Add ("sdkroot=", "Specify the location of Apple SDKs, default to 'xcode-select' value.", v => sdk_root = v); options.Add ("sdk=", "Specifies the SDK version to compile against (version, for example \"10.9\"). For Mac Catalyst, this is the macOS version of the SDK.", v => { @@ -85,17 +85,7 @@ static void ParseOptions (Application app, Mono.Options.OptionSet options, strin } #endif // !LEGACY_TOOLS - public static int Verbosity { - get { return ErrorHelper.Verbosity; } - set { ErrorHelper.Verbosity = value; } - } - - static Driver () - { - Verbosity = GetDefaultVerbosity (); - } - - static int GetDefaultVerbosity () + public static int GetDefaultVerbosity () { var v = 0; var fn = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.UserProfile), $".{NAME}-verbosity"); @@ -107,35 +97,6 @@ static int GetDefaultVerbosity () return v; } - public static void Log (string value) - { - Log (0, value); - } - - public static void Log (string format, params object? [] args) - { - Log (0, format, args); - } - - public static void Log (int min_verbosity, string value) - { - if (min_verbosity > Verbosity) - return; - - Console.WriteLine (value); - } - - public static void Log (int min_verbosity, string format, params object? [] args) - { - if (min_verbosity > Verbosity) - return; - - if (args.Length > 0) - Console.WriteLine (format, args); - else - Console.WriteLine (format); - } - static TargetFramework targetFramework; public static TargetFramework TargetFramework { @@ -149,7 +110,7 @@ static void FileMove (string source, string target) File.Move (source, target); } - static void MoveIfDifferent (string path, string tmp, bool use_stamp = false) + static void MoveIfDifferent (IToolLog log, string path, string tmp, bool use_stamp = false) { // Don't read the entire file into memory, it can be quite big in certain cases. @@ -158,10 +119,10 @@ static void MoveIfDifferent (string path, string tmp, bool use_stamp = false) using (var fs1 = new FileStream (path, FileMode.Open, FileAccess.Read)) { using (var fs2 = new FileStream (tmp, FileMode.Open, FileAccess.Read)) { if (fs1.Length != fs2.Length) { - Log (3, "New file '{0}' has different length, writing new file.", path); + log.Log (3, "New file '{0}' has different length, writing new file.", path); move = true; } else { - move = !Cache.CompareStreams (fs1, fs2); + move = !Cache.CompareStreams (log, fs1, fs2); } } } @@ -169,13 +130,13 @@ static void MoveIfDifferent (string path, string tmp, bool use_stamp = false) if (move) { FileMove (tmp, path); } else { - Log (3, "Target {0} is up-to-date.", path); + log.Log (3, "Target {0} is up-to-date.", path); if (use_stamp) - Driver.Touch (path + ".stamp"); + Driver.Touch (log, path + ".stamp"); } } - public static void WriteIfDifferent (string path, string contents, bool use_stamp = false) + public static void WriteIfDifferent (IToolLog log, string path, string contents, bool use_stamp = false) { var tmp = path + ".tmp"; @@ -185,36 +146,36 @@ public static void WriteIfDifferent (string path, string contents, bool use_stam if (!string.IsNullOrEmpty (dir)) Directory.CreateDirectory (dir); File.WriteAllText (path, contents); - Log (3, "File '{0}' does not exist, creating it.", path); + log.Log (3, "File '{0}' does not exist, creating it.", path); return; } File.WriteAllText (tmp, contents); - MoveIfDifferent (path, tmp, use_stamp); + MoveIfDifferent (log, path, tmp, use_stamp); } catch (Exception e) { File.WriteAllText (path, contents); - ErrorHelper.Warning (1014, e, Errors.MT1014, path, e.Message); + ErrorHelper.Warning (log, 1014, e, Errors.MT1014, path, e.Message); } finally { File.Delete (tmp); } } - public static void WriteIfDifferent (string path, byte [] contents, bool use_stamp = false) + public static void WriteIfDifferent (IToolLog log, string path, byte [] contents, bool use_stamp = false) { var tmp = path + ".tmp"; try { if (!File.Exists (path)) { File.WriteAllBytes (path, contents); - Log (3, "File '{0}' does not exist, creating it.", path); + log.Log (3, "File '{0}' does not exist, creating it.", path); return; } File.WriteAllBytes (tmp, contents); - MoveIfDifferent (path, tmp, use_stamp); + MoveIfDifferent (log, path, tmp, use_stamp); } catch (Exception e) { File.WriteAllBytes (path, contents); - ErrorHelper.Warning (1014, e, Errors.MT1014, path, e.Message); + ErrorHelper.Warning (log, 1014, e, Errors.MT1014, path, e.Message); } finally { File.Delete (tmp); } @@ -244,7 +205,7 @@ public static Version XcodeVersion { } } - static void SetCurrentLanguage () + static void SetCurrentLanguage (IToolLog log) { // There's no way to change the current culture from the command-line // without changing the system settings, so honor LANG if set. @@ -269,14 +230,14 @@ static void SetCurrentLanguage () var culture = CultureInfo.GetCultureInfo (lang); if (culture is not null) { CultureInfo.DefaultThreadCurrentCulture = culture; - Log (2, $"The current language was set to '{culture.DisplayName}' according to the LANG environment variable (LANG={lang_variable})."); + log.Log (2, $"The current language was set to '{culture.DisplayName}' according to the LANG environment variable (LANG={lang_variable})."); } } catch (Exception e) { - ErrorHelper.Warning (124, e, Errors.MT0124, lang, lang_variable, e.Message); + ErrorHelper.Warning (log, 124, e, Errors.MT0124, lang, lang_variable, e.Message); } } - public static void Touch (IEnumerable filenames, DateTime? timestamp = null) + public static void Touch (IToolLog log, IEnumerable filenames, DateTime? timestamp = null) { if (timestamp is null) timestamp = DateTime.Now; @@ -290,14 +251,14 @@ public static void Touch (IEnumerable filenames, DateTime? timestamp = n } fi.LastWriteTime = timestamp.Value; } catch (Exception e) { - ErrorHelper.Warning (128, Errors.MT0128, filename, e.Message); + ErrorHelper.Warning (log, 128, Errors.MT0128, filename, e.Message); } } } - public static void Touch (params string [] filenames) + public static void Touch (IToolLog log, params string [] filenames) { - Touch ((IEnumerable) filenames); + Touch (log, (IEnumerable) filenames); } static int watch_level; @@ -314,13 +275,11 @@ public static int WatchLevel { } } - public static void Watch (string msg, int level) + public static void Watch (IToolLog log, string msg, int level) { if ((watch is null) || (level > WatchLevel)) return; - for (int i = 0; i < level; i++) - Console.Write ("!"); - Console.WriteLine ("Timestamp {0}: {1} ms", msg, watch.ElapsedMilliseconds); + log.Log ($"{new string ('!', level)}Timestamp {msg}: {watch.ElapsedMilliseconds} ms"); } internal static PDictionary? FromPList (string name) @@ -332,11 +291,11 @@ public static void Watch (string msg, int level) const string XcodeDefault = "/Applications/Xcode.app"; - static string? FindSystemXcode () + static string? FindSystemXcode (IToolLog log) { var output = new StringBuilder (); - if (Driver.RunCommand ("xcode-select", new [] { "-p" }, output: output) != 0) { - ErrorHelper.Warning (59, Errors.MX0059, output.ToString ()); + if (Driver.RunCommand (log, "xcode-select", new [] { "-p" }, output: output) != 0) { + ErrorHelper.Warning (log, 59, Errors.MX0059, output.ToString ()); return null; } return output.ToString ().Trim (); @@ -427,31 +386,31 @@ public static string GetProductAssembly (Application app) public static void ValidateXcode (Application app, bool accept_any_xcode_version, bool warn_if_not_found) { if (sdk_root is null) { - sdk_root = FindSystemXcode (); + sdk_root = FindSystemXcode (app); if (sdk_root is null) { // FindSystemXcode showed a warning in this case. In particular do not use 'string.IsNullOrEmpty' here, // because FindSystemXcode may return an empty string (with no warning printed) if the xcode-select command // succeeds, but returns nothing. sdk_root = null; } else if (!Directory.Exists (sdk_root)) { - ErrorHelper.Warning (60, Errors.MX0060, sdk_root); + ErrorHelper.Warning (app, 60, Errors.MX0060, sdk_root); sdk_root = null; } else { if (!accept_any_xcode_version) - ErrorHelper.Warning (61, Errors.MT0061, sdk_root); + ErrorHelper.Warning (app, 61, Errors.MT0061, sdk_root); } if (sdk_root is null) { sdk_root = XcodeDefault; if (!Directory.Exists (sdk_root)) { if (warn_if_not_found) { // mmp: and now we give up, but don't throw like mtouch, because we don't want to change behavior (this sometimes worked it appears) - ErrorHelper.Warning (56, Errors.MX0056); + ErrorHelper.Warning (app, 56, Errors.MX0056); return; // Can't validate the version below if we can't even find Xcode... } throw ErrorHelper.CreateError (56, Errors.MX0056); } - ErrorHelper.Warning (62, Errors.MT0062, sdk_root); + ErrorHelper.Warning (app, 62, Errors.MT0062, sdk_root); } } else if (!Directory.Exists (sdk_root)) { throw ErrorHelper.CreateError (55, Errors.MT0055, sdk_root); @@ -481,7 +440,7 @@ public static void ValidateXcode (Application app, bool accept_any_xcode_version throw ErrorHelper.CreateError (58, Errors.MT0058, Path.GetDirectoryName (Path.GetDirectoryName (DeveloperDirectory)), plist_path); } - Driver.Log (1, "Using Xcode {0} ({2}) found in {1}", XcodeVersion, sdk_root, XcodeProductVersion); + app.Log (1, "Using Xcode {0} ({2}) found in {1}", XcodeVersion, sdk_root, XcodeProductVersion); } internal static bool TryParseBool (string value, out bool result) @@ -607,7 +566,7 @@ static bool XcrunFind (Application app, ApplePlatform platform, bool is_simulato // We also want to print out what happened if something went wrong, and in that case we don't want stdout // and stderr captured separately, because related lines could end up printed far from eachother in time, // and that's confusing. So capture stdout and stderr by themselves, and also capture both together. - int ret = RunCommand ("xcrun", args, env, + int ret = RunCommand (app, "xcrun", args, env, (v) => { lock (both) { both.AppendLine (v); @@ -624,11 +583,11 @@ static bool XcrunFind (Application app, ApplePlatform platform, bool is_simulato if (ret == 0) { path = stdout.ToString ().Trim (); if (!File.Exists (path)) { - ErrorHelper.Warning (5315, Errors.MX5315 /* The tool xcrun failed to return a valid result (the file {0} does not exist). Check build log for details. */, tool, path); + ErrorHelper.Warning (app, 5315, Errors.MX5315 /* The tool xcrun failed to return a valid result (the file {0} does not exist). Check build log for details. */, tool, path); return false; } } else { - Log (1, "Failed to locate the developer tool '{0}', 'xcrun {1}' returned with the exit code {2}:\n{3}", tool, string.Join (" ", args), ret, both.ToString ()); + app.Log (1, "Failed to locate the developer tool '{0}', 'xcrun {1}' returned with the exit code {2}:\n{3}", tool, string.Join (" ", args), ret, both.ToString ()); } return ret == 0; @@ -642,7 +601,7 @@ public static void RunXcodeTool (Application app, string tool, params string [] public static void RunXcodeTool (Application app, string tool, IList arguments) { var executable = FindTool (app, tool); - var rv = RunCommand (executable, arguments); + var rv = RunCommand (app, executable, arguments); if (rv != 0) throw ErrorHelper.CreateError (5309, Errors.MX5309 /* Failed to execute the tool '{0}', it failed with an error code '{1}'. Please check the build log for details. */, tool, rv); } @@ -695,7 +654,7 @@ public static void RunLipoAndCreateDsym (Application app, string output, IEnumer if (Directory.Exists (outputDsymDir)) Directory.Delete (outputDsymDir, true); Directory.Move (dsymFolders [0], outputDsymDir); - RunCommand ("/usr/bin/mdimport", outputDsymDir); + RunCommand (app, "/usr/bin/mdimport", outputDsymDir); } } @@ -707,7 +666,7 @@ public static void RunLipo (Application app, IList options) public static void CreateDsym (Application app, string output_dir, string appname, string dsym_dir) { RunDsymUtil (app, Path.Combine (output_dir, appname), "-num-threads", "4", "-z", "-o", dsym_dir); - RunCommand ("/usr/bin/mdimport", dsym_dir); + RunCommand (app, "/usr/bin/mdimport", dsym_dir); } public static void RunDsymUtil (Application app, params string [] options) diff --git a/tools/common/Driver.execution.cs b/tools/common/Driver.execution.cs index bb576ccec230..72c09d3a3d7f 100644 --- a/tools/common/Driver.execution.cs +++ b/tools/common/Driver.execution.cs @@ -18,76 +18,76 @@ namespace Xamarin.Bundler { public partial class Driver { - public static int RunCommand (string path, IList args, Dictionary? env, out StringBuilder output, bool suppressPrintOnErrors, int verbose) + public static int RunCommand (IToolLog log, string path, IList args, Dictionary? env, out StringBuilder output, bool suppressPrintOnErrors, int verbose) { output = new StringBuilder (); - return RunCommand (path, args, env, output, suppressPrintOnErrors, verbose); + return RunCommand (log, path, args, env, output, suppressPrintOnErrors, verbose); } - public static int RunCommand (string path, IList args, Dictionary? env, out StringBuilder output, bool suppressPrintOnErrors) + public static int RunCommand (IToolLog log, string path, IList args, Dictionary? env, out StringBuilder output, bool suppressPrintOnErrors) { output = new StringBuilder (); - return RunCommand (path, args, env, output, suppressPrintOnErrors, Verbosity); + return RunCommand (log, path, args, env, output, suppressPrintOnErrors, log.Verbosity); } - public static int RunCommand (string path, IList args, Dictionary? env, StringBuilder output, bool suppressPrintOnErrors, int verbosity) + public static int RunCommand (IToolLog log, string path, IList args, Dictionary? env, StringBuilder output, bool suppressPrintOnErrors, int verbosity) { - return RunCommand (path, args, env, output, output, suppressPrintOnErrors, verbosity); + return RunCommand (log, path, args, env, output, output, suppressPrintOnErrors, verbosity); } - public static int RunCommand (string path, IList args, Dictionary? env, StringBuilder output, bool suppressPrintOnErrors = false) + public static int RunCommand (IToolLog log, string path, IList args, Dictionary? env, StringBuilder output, bool suppressPrintOnErrors = false) { - return RunCommand (path, args, env, output, output, suppressPrintOnErrors, Verbosity); + return RunCommand (log, path, args, env, output, output, suppressPrintOnErrors, log.Verbosity); } - public static int RunCommand (string path, params string [] args) + public static int RunCommand (IToolLog log, string path, params string [] args) { - return RunCommand (path, args, null, (Action?) null, (Action?) null, false, Verbosity); + return RunCommand (log, path, args, null, (Action?) null, (Action?) null, false, log.Verbosity); } - public static int RunCommand (string path, IList args) + public static int RunCommand (IToolLog log, string path, IList args) { - return RunCommand (path, args, null, (Action?) null, (Action?) null, false, Verbosity); + return RunCommand (log, path, args, null, (Action?) null, (Action?) null, false, log.Verbosity); } - public static int RunCommand (string path, IList args, StringBuilder? output) + public static int RunCommand (IToolLog log, string path, IList args, StringBuilder? output) { - return RunCommand (path, args, null, output, output, false, Verbosity); + return RunCommand (log, path, args, null, output, output, false, log.Verbosity); } - public static int RunCommand (string path, IList args, StringBuilder? output, bool suppressPrintOnErrors) + public static int RunCommand (IToolLog log, string path, IList args, StringBuilder? output, bool suppressPrintOnErrors) { - return RunCommand (path, args, null, output, output, suppressPrintOnErrors, Verbosity); + return RunCommand (log, path, args, null, output, output, suppressPrintOnErrors, log.Verbosity); } - public static int RunCommand (string path, IList args, StringBuilder? output, StringBuilder? error) + public static int RunCommand (IToolLog log, string path, IList args, StringBuilder? output, StringBuilder? error) { - return RunCommand (path, args, null, output, error, false, Verbosity); + return RunCommand (log, path, args, null, output, error, false, log.Verbosity); } - public static int RunCommand (string path, IList args, StringBuilder? output, StringBuilder? error, bool suppressPrintOnErrors) + public static int RunCommand (IToolLog log, string path, IList args, StringBuilder? output, StringBuilder? error, bool suppressPrintOnErrors) { - return RunCommand (path, args, null, output, error, suppressPrintOnErrors, Verbosity); + return RunCommand (log, path, args, null, output, error, suppressPrintOnErrors, log.Verbosity); } - public static int RunCommand (string path, IList args, Dictionary? env, StringBuilder? output, StringBuilder? error, bool suppressPrintOnErrors, int verbosity) + public static int RunCommand (IToolLog log, string path, IList args, Dictionary? env, StringBuilder? output, StringBuilder? error, bool suppressPrintOnErrors, int verbosity) { var output_received = output is null ? null : new Action ((v) => { if (v is not null) output.AppendLine (v); }); var error_received = error is null ? null : new Action ((v) => { if (v is not null) error.AppendLine (v); }); - return RunCommand (path, args, env, output_received, error_received, suppressPrintOnErrors, verbosity); + return RunCommand (log, path, args, env, output_received, error_received, suppressPrintOnErrors, verbosity); } - static int RunCommand (string path, IList args, Dictionary? env, Action? output_received, bool suppressPrintOnErrors) + static int RunCommand (IToolLog log, string path, IList args, Dictionary? env, Action? output_received, bool suppressPrintOnErrors) { - return RunCommand (path, args, env, output_received, output_received, suppressPrintOnErrors, Verbosity); + return RunCommand (log, path, args, env, output_received, output_received, suppressPrintOnErrors, log.Verbosity); } - static int RunCommand (string path, IList args, Dictionary? env, Action? output_received, Action? error_received) + static int RunCommand (IToolLog log, string path, IList args, Dictionary? env, Action? output_received, Action? error_received) { - return RunCommand (path, args, env, output_received, error_received, false, Verbosity); + return RunCommand (log, path, args, env, output_received, error_received, false, log.Verbosity); } - static int RunCommand (string path, IList args, Dictionary? env, Action? output_received, Action? error_received, bool suppressPrintOnErrors, int verbosity) + static int RunCommand (IToolLog log, string path, IList args, Dictionary? env, Action? output_received, Action? error_received, bool suppressPrintOnErrors, int verbosity) { var output = new StringBuilder (); var outputCallback = new Action ((line) => { @@ -103,8 +103,7 @@ static int RunCommand (string path, IList args, Dictionary 0) - Console.WriteLine ($"{path} {StringUtils.FormatArguments (args)}"); + log.Log (1, $"{path} {StringUtils.FormatArguments (args)}"); var p = Execution.RunWithCallbacksAsync (path, args, env, outputCallback, errorCallback).Result; @@ -118,33 +117,27 @@ static int RunCommand (string path, IList args, Dictionary 0 && output?.Length > 0 && !suppressPrintOnErrors) { - Console.WriteLine (output.ToString ()); + log.Log (output.ToString ()); } return p.ExitCode; } - public static Task RunCommandAsync (string path, IList args, Dictionary? env = null, Action? output_received = null, bool suppressPrintOnErrors = false, int? verbosity = null) + public static Task RunCommandAsync (IToolLog log, string path, IList args, Dictionary? env = null, Action? output_received = null, bool suppressPrintOnErrors = false, int? verbosity = null) { - return Task.Run (() => RunCommand (path, args, env, output_received, output_received, suppressPrintOnErrors, verbosity ?? Verbosity)); + return Task.Run (() => RunCommand (log, path, args, env, output_received, output_received, suppressPrintOnErrors, verbosity ?? log.Verbosity)); } - public static Task RunCommandAsync (string path, IList args, Dictionary? env = null, StringBuilder? output = null, bool suppressPrintOnErrors = false, int? verbosity = null) + public static Task RunCommandAsync (IToolLog log, string path, IList args, Dictionary? env = null, StringBuilder? output = null, bool suppressPrintOnErrors = false, int? verbosity = null) { - return Task.Run (() => RunCommand (path, args, env, output, output, suppressPrintOnErrors, verbosity ?? Verbosity)); + return Task.Run (() => RunCommand (log, path, args, env, output, output, suppressPrintOnErrors, verbosity ?? log.Verbosity)); } - -#if BGENERATOR - internal static int Verbosity => ErrorHelper.Verbosity; -#elif !LEGACY_TOOLS && !BUNDLER - internal static int Verbosity; -#endif } } diff --git a/tools/common/ErrorHelper.tools.cs b/tools/common/ErrorHelper.tools.cs index cbfef0ddb7d0..275f0d04a894 100644 --- a/tools/common/ErrorHelper.tools.cs +++ b/tools/common/ErrorHelper.tools.cs @@ -39,7 +39,6 @@ public enum WarningLevel { } static Dictionary? warning_levels; - public static int Verbosity { get; set; } #pragma warning disable 649 public static Func? IsExpectedException; @@ -253,18 +252,18 @@ public static ProductException Create (Application app, int code, bool error, Ex return e; } - public static void Warning (int code, string message, params object [] args) + public static void Warning (IToolLog log, int code, string message, params object [] args) { - Show (new ProductException (code, false, message, args)); + Show (log, new ProductException (code, false, message, args)); } - public static void Warning (int code, Exception innerException, string message, params object [] args) + public static void Warning (IToolLog log, int code, Exception innerException, string message, params object [] args) { - Show (new ProductException (code, false, innerException, message, args)); + Show (log, new ProductException (code, false, innerException, message, args)); } // Shows any warnings, and if there are any errors, throws an AggregateException. - public static void ThrowIfErrors (IList exceptions) + public static void ThrowIfErrors (IToolLog log, IList exceptions) { if (exceptions?.Any () != true) return; @@ -274,28 +273,28 @@ public static void ThrowIfErrors (IList exceptions) var warnings = grouped.SingleOrDefault ((v) => v.Key); if (warnings?.Any () == true) - Show (warnings); + Show (log, warnings); var errors = grouped.SingleOrDefault ((v) => !v.Key); if (errors?.Any () == true) throw new AggregateException (errors); } - public static void Show (IEnumerable list) + public static void Show (IToolLog log, IEnumerable list) { var exceptions = CollectExceptions (list); bool error = false; foreach (var ex in exceptions) - error |= ShowInternal (ex); + error |= ShowInternal (log, ex); if (error) Exit (1); } - public static void Show (Exception e) + public static void Show (IToolLog log, Exception e) { - Show (new Exception [] { e }); + Show (log, new Exception [] { e }); } static void Exit (int exitCode) @@ -305,7 +304,7 @@ static void Exit (int exitCode) Environment.Exit (exitCode); } - static bool ShowInternal (Exception e) + static bool ShowInternal (IToolLog log, Exception e) { var mte = e as ProductException; bool error = true; @@ -316,39 +315,39 @@ static bool ShowInternal (Exception e) if (!error && GetWarningLevel (mte.Code) == WarningLevel.Disable) return false; // This is an ignored warning. - Console.Error.WriteLine (mte.ToString ()); + log.LogError (mte.ToString ()); - ShowInner (e); + ShowInner (log, e); - if (Verbosity > 2 && !string.IsNullOrEmpty (e.StackTrace)) - Console.Error.WriteLine (e.StackTrace); + if (log.Verbosity > 2 && !string.IsNullOrEmpty (e.StackTrace)) + log.LogError (e.StackTrace); } else if (IsExpectedException is null || !IsExpectedException (e)) { - Console.Error.WriteLine ("error " + Prefix + "0000: Unexpected error - Please file a bug report at https://github.com/dotnet/macios/issues/new"); - Console.Error.WriteLine (e.ToString ()); + log.LogError ("error " + Prefix + "0000: Unexpected error - Please file a bug report at https://github.com/dotnet/macios/issues/new"); + log.LogError (e.ToString ()); } else { - Console.Error.WriteLine (e.ToString ()); - ShowInner (e); - if (Verbosity > 2 && !string.IsNullOrEmpty (e.StackTrace)) - Console.Error.WriteLine (e.StackTrace); + log.LogError (e.ToString ()); + ShowInner (log, e); + if (log.Verbosity > 2 && !string.IsNullOrEmpty (e.StackTrace)) + log.LogError (e.StackTrace); } return error; } - static void ShowInner (Exception e) + static void ShowInner (IToolLog log, Exception e) { var ie = e.InnerException; if (ie is null) return; - if (Verbosity > 3) { - Console.Error.WriteLine ("--- inner exception"); - Console.Error.WriteLine (ie); - Console.Error.WriteLine ("---"); - } else if (Verbosity > 0 || ie is ProductException) { - Console.Error.WriteLine ("\t{0}", ie.Message); + if (log.Verbosity > 3) { + log.LogError ("--- inner exception"); + log.LogError (ie.ToString ()); + log.LogError ("---"); + } else if (log.Verbosity > 0 || ie is ProductException) { + log.LogError ($"\t{ie.Message}"); } - ShowInner (ie); + ShowInner (log, ie); } } } diff --git a/tools/common/FileCopier.cs b/tools/common/FileCopier.cs index 06d1176453bc..d10e93b59827 100644 --- a/tools/common/FileCopier.cs +++ b/tools/common/FileCopier.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -27,6 +28,7 @@ enum CopyFileFlags : uint { enum CopyFileState : uint { StatusCB = 6, + StatusCtx = 7, } enum CopyFileStep { @@ -57,81 +59,39 @@ enum CopyFileWhat { [DllImport ("/usr/lib/libSystem.dylib")] static extern int copyfile_state_free (IntPtr state); +#if NET [DllImport ("/usr/lib/libSystem.dylib")] - static extern int copyfile_state_set (IntPtr state, CopyFileState flag, IntPtr value); - + static extern unsafe int copyfile_state_set (IntPtr state, CopyFileState flag, delegate* unmanaged value); +#else delegate CopyFileResult CopyFileCallbackDelegate (CopyFileWhat what, CopyFileStep stage, IntPtr state, string src, string dst, IntPtr ctx); +#endif + + [DllImport ("/usr/lib/libSystem.dylib")] + static extern unsafe int copyfile_state_set (IntPtr state, CopyFileState flag, IntPtr value); [DllImport ("/usr/lib/libSystem.dylib", SetLastError = true)] static extern int copyfile (string @from, string @to, IntPtr state, CopyFileFlags flags); - // This code is shared between our packaging tools (mmp\mtouch) and msbuild tasks - public delegate void LogCallback (int verbosity, string format, params object? [] arguments); - public delegate void ReportErrorCallback (int code, string format, params object? [] arguments); - - [ThreadStatic] - static LogCallback? logCallback; - - [ThreadStatic] - static ReportErrorCallback? reportErrorCallback; - - static void Log (int min_verbosity, string format, params object? [] arguments) + static void ReportError (IToolLog log, int code, string format, params object? [] arguments) { - if (logCallback is not null) { - logCallback (min_verbosity, format, arguments); - return; - } - #if LEGACY_TOOLS || BUNDLER - // LogMessage and LogError are instance objects on the tasks themselves and bubbling an event up is not ideal - Driver.Log (min_verbosity, format, arguments); + log.LogError (ErrorHelper.CreateError (code, format, arguments)); #else - Console.WriteLine (format, arguments); + log.LogError (new Exception ($"{code} {string.Format (format, arguments)}")); #endif } - static void ReportError (int code, string format, params object? [] arguments) - { - if (reportErrorCallback is not null) { - reportErrorCallback (code, format, arguments); - return; - } - -#if LEGACY_TOOLS || BUNDLER - throw ErrorHelper.CreateError (code, format, arguments); -#else - // msbuild handles uncaught exceptions as a task error - throw new Exception ($"{code} {string.Format (format, arguments)}"); -#endif - } - - public static void UpdateDirectory (string source, string target, ReportErrorCallback reportErrorCallback, LogCallback logCallback) - { - try { - FileCopier.reportErrorCallback = reportErrorCallback; - FileCopier.logCallback = logCallback; - UpdateDirectory (source, target); - } finally { - FileCopier.reportErrorCallback = null; - FileCopier.logCallback = null; - } - } - -#if LEGACY_TOOLS || BUNDLER - public static void UpdateDirectory (string source, string target) -#else - static void UpdateDirectory (string source, string target) -#endif + public static void UpdateDirectory (IToolLog log, string source, string target) { // first chance, try to update existing content inside `target` - if (TryUpdateDirectory (source, target, out var err)) + if (TryUpdateDirectory (log, source, target, out var err)) return; // 2nd chance, remove `target` then copy everything - Log (1, "Could not update `{0}` content (error #{1} : {2}), trying to overwrite everything...", target, err, strerror (err)); + log.Log (1, "Could not update `{0}` content (error #{1} : {2}), trying to overwrite everything...", target, err, strerror (err)); Directory.Delete (target, true); - if (!TryUpdateDirectory (source, target, out err)) - ReportError (1022, Errors.MT1022, source, target, err, strerror (err)); + if (!TryUpdateDirectory (log, source, target, out err)) + ReportError (log, 1022, Errors.MT1022, source, target, err, strerror (err)); } static bool? use_managed_copying; @@ -150,16 +110,16 @@ static bool UseManagedCopying { } } - static bool TryUpdateDirectory (string source, string target, out int errno) + static bool TryUpdateDirectory (IToolLog log, string source, string target, out int errno) { if (UseManagedCopying) - return TryUpdateDirectoryWindows (source, target, out errno); - return TryUpdateDirectoryMacOS (source, target, out errno); + return TryUpdateDirectoryWindows (log, source, target, out errno); + return TryUpdateDirectoryMacOS (log, source, target, out errno); } - static bool TryUpdateDirectoryWindows (string source, string target, out int errno) + static bool TryUpdateDirectoryWindows (IToolLog log, string source, string target, out int errno) { - Log (1, $"Copying {source} to {target} recursively"); + log.Log (1, $"Copying {source} to {target} recursively"); errno = 0; Directory.CreateDirectory (target); @@ -170,39 +130,47 @@ static bool TryUpdateDirectoryWindows (string source, string target, out int err foreach (var sourceFile in dir.GetFiles ()) { var sourcePath = sourceFile.FullName; var targetPath = Path.Combine (target, Path.GetFileName (source), sourceFile.Name); - CopyIfNeeded (sourcePath, targetPath); + CopyIfNeeded (log, sourcePath, targetPath); } foreach (var subdir in dir.GetDirectories ()) { - rv &= TryUpdateDirectoryWindows (Path.Combine (source, subdir.Name), Path.Combine (target, Path.GetFileName (source)), out errno); + rv &= TryUpdateDirectoryWindows (log, Path.Combine (source, subdir.Name), Path.Combine (target, Path.GetFileName (source)), out errno); } } else { var targetPath = Path.Combine (target, Path.GetFileName (source)); - CopyIfNeeded (source, targetPath); + CopyIfNeeded (log, source, targetPath); } return rv; } - static void CopyIfNeeded (string source, string target) + static void CopyIfNeeded (IToolLog log, string source, string target) { - if (IsUptodate (source, target)) { - Log (3, "Target '{0}' is up-to-date", target); + if (IsUptodate (log, source, target)) { + log.Log (3, "Target '{0}' is up-to-date", target); } else { Directory.CreateDirectory (Path.GetDirectoryName (target)!); File.Copy (source, target, true); - Log (1, "Copied {0} to {1}", source, target); + log.Log (1, "Copied {0} to {1}", source, target); } } - static bool TryUpdateDirectoryMacOS (string source, string target, out int errno) + static bool TryUpdateDirectoryMacOS (IToolLog log, string source, string target, out int errno) { Directory.CreateDirectory (target); // Mono's File.Copy can't handle symlinks (the symlinks are followed instead of copied), // so we need to use native functions directly. Luckily OSX provides exactly what we need. IntPtr state = copyfile_state_alloc (); + var logHandle = GCHandle.Alloc (log); try { +#if NET + unsafe { + copyfile_state_set (state, CopyFileState.StatusCB, &CopyFileCallback); + } +#else CopyFileCallbackDelegate del = CopyFileCallback; copyfile_state_set (state, CopyFileState.StatusCB, Marshal.GetFunctionPointerForDelegate (del)); +#endif + copyfile_state_set (state, CopyFileState.StatusCtx, (IntPtr) logHandle); int rv = copyfile (source, target, state, CopyFileFlags.Data | CopyFileFlags.Recursive | CopyFileFlags.Nofollow | CopyFileFlags.Clone); if (rv == 0) { errno = 0; // satisfy compiler and make sure not to pick up some older error code @@ -212,25 +180,36 @@ static bool TryUpdateDirectoryMacOS (string source, string target, out int errno return false; } } finally { + logHandle.Free (); copyfile_state_free (state); } } // do not call `Marshal.GetLastWin32Error` inside this method since it's called while the p/invoke is executing and will return `260` +#if NET + [UnmanagedCallersOnly] + static CopyFileResult CopyFileCallback (CopyFileWhat what, CopyFileStep stage, IntPtr state, IntPtr sourcePtr, IntPtr targetPtr, IntPtr ctx) + { + var source = Marshal.PtrToStringUTF8 (sourcePtr)!; + var target = Marshal.PtrToStringUTF8 (targetPtr)!; +#else static CopyFileResult CopyFileCallback (CopyFileWhat what, CopyFileStep stage, IntPtr state, string source, string target, IntPtr ctx) { - // Console.WriteLine ("CopyFileCallback ({0}, {1}, 0x{2}, {3}, {4}, 0x{5})", what, stage, state.ToString ("x"), source, target, ctx.ToString ("x")); +#endif + var log = (IToolLog) GCHandle.FromIntPtr (ctx).Target!; + + // log.Log ("CopyFileCallback ({0}, {1}, 0x{2}, {3}, {4}, 0x{5})", what, stage, state.ToString ("x"), source, target, ctx.ToString ("x")); switch (what) { case CopyFileWhat.File: - if (!IsUptodate (source, target)) { + if (!IsUptodate (log, source, target)) { if (stage == CopyFileStep.Finish) - Log (1, "Copied {0} to {1}", source, target); + log.Log (1, "Copied {0} to {1}", source, target); else if (stage == CopyFileStep.Err) { - Log (1, "Could not copy the file '{0}' to '{1}'", source, target); + log.Log (1, "Could not copy the file '{0}' to '{1}'", source, target); return CopyFileResult.Quit; } else if (stage == CopyFileStep.Start) { if (File.Exists (target) || Directory.Exists (target)) { - Log (1, "Deleted target {0}, it's not up-to-date", target); + log.Log (1, "Deleted target {0}, it's not up-to-date", target); // This callback won't be called for directories, but we can get here for symlinks to directories. // This means that File.Delete should always work (no need to check for a directory to call Directory.Delete) File.Delete (target); @@ -238,7 +217,7 @@ static CopyFileResult CopyFileCallback (CopyFileWhat what, CopyFileStep stage, I } return CopyFileResult.Continue; } else { - Log (3, "Target '{0}' is up-to-date", target); + log.Log (3, "Target '{0}' is up-to-date", target); return CopyFileResult.Skip; } case CopyFileWhat.Dir: @@ -247,36 +226,20 @@ static CopyFileResult CopyFileCallback (CopyFileWhat what, CopyFileStep stage, I case CopyFileWhat.CopyXattr: return CopyFileResult.Continue; case CopyFileWhat.Error: - Log (1, "Could not copy the file '{0}' to '{1}'", source, target); + log.Log (1, "Could not copy the file '{0}' to '{1}'", source, target); return CopyFileResult.Quit; default: return CopyFileResult.Continue; } } - public static bool IsUptodate (string source, string target, ReportErrorCallback reportErrorCallback, LogCallback logCallback, bool check_contents = false, bool check_stamp = true) - { - try { - FileCopier.reportErrorCallback = reportErrorCallback; - FileCopier.logCallback = logCallback; - return IsUptodate (source, target, check_contents, check_stamp); - } finally { - FileCopier.reportErrorCallback = null; - FileCopier.logCallback = null; - } - } - // Checks if the source file has a time stamp later than the target file. // // Optionally check if the contents of the files are different after checking the timestamp. // // If check_stamp is true, the function will use the timestamp of a "target".stamp file // if it's later than the timestamp of the "target" file itself. -#if LEGACY_TOOLS || BUNDLER - public static bool IsUptodate (string source, string target, bool check_contents = false, bool check_stamp = true) -#else - static bool IsUptodate (string source, string target, bool check_contents = false, bool check_stamp = true) -#endif + public static bool IsUptodate (IToolLog log, string source, string target, bool check_contents = false, bool check_stamp = true) { #if LEGACY_TOOLS || BUNDLER // msbuild does not have force if (Driver.Force) @@ -286,14 +249,14 @@ static bool IsUptodate (string source, string target, bool check_contents = fals var tfi = new FileInfo (target); if (!tfi.Exists) { - Log (3, "Target '{0}' does not exist.", target); + log.Log (3, "Target '{0}' does not exist.", target); return false; } if (check_stamp) { var tfi_stamp = new FileInfo (target + ".stamp"); if (tfi_stamp.Exists && tfi_stamp.LastWriteTimeUtc > tfi.LastWriteTimeUtc) { - Log (3, "Target '{0}' has a stamp file with newer timestamp ({1} > {2}), using the stamp file's timestamp", target, tfi_stamp.LastWriteTimeUtc, tfi.LastWriteTimeUtc); + log.Log (3, "Target '{0}' has a stamp file with newer timestamp ({1} > {2}), using the stamp file's timestamp", target, tfi_stamp.LastWriteTimeUtc, tfi.LastWriteTimeUtc); tfi = tfi_stamp; } } @@ -301,13 +264,13 @@ static bool IsUptodate (string source, string target, bool check_contents = fals var sfi = new FileInfo (source); if (sfi.LastWriteTimeUtc <= tfi.LastWriteTimeUtc) { - Log (3, "Prerequisite '{0}' is older than the target '{1}'.", source, target); + log.Log (3, "Prerequisite '{0}' is older than the target '{1}'.", source, target); return true; } #if LEGACY_TOOLS || BUNDLER // msbuild usages do not require CompareFiles optimization - if (check_contents && Cache.CompareFiles (source, target)) { - Log (3, "Prerequisite '{0}' is newer than the target '{1}', but the contents are identical.", source, target); + if (check_contents && Cache.CompareFiles (log, source, target)) { + log.Log (3, "Prerequisite '{0}' is newer than the target '{1}', but the contents are identical.", source, target); return true; } #else @@ -315,31 +278,15 @@ static bool IsUptodate (string source, string target, bool check_contents = fals throw new NotImplementedException ("Checking file contents is not supported"); #endif - Log (3, "Prerequisite '{0}' is newer than the target '{1}'.", source, target); + log.Log (3, "Prerequisite '{0}' is newer than the target '{1}'.", source, target); return false; } - public static bool IsUptodate (IEnumerable sources, IEnumerable targets, ReportErrorCallback reportErrorCallback, LogCallback logCallback, bool check_stamp = true) - { - try { - FileCopier.reportErrorCallback = reportErrorCallback; - FileCopier.logCallback = logCallback; - return IsUptodate (sources, targets, check_stamp); - } finally { - FileCopier.reportErrorCallback = null; - FileCopier.logCallback = null; - } - } - // Checks if any of the source files have a time stamp later than any of the target files. // // If check_stamp is true, the function will use the timestamp of a "target".stamp file // if it's later than the timestamp of the "target" file itself. -#if LEGACY_TOOLS || BUNDLER - public static bool IsUptodate (IEnumerable sources, IEnumerable targets, bool check_stamp = true) -#else - static bool IsUptodate (IEnumerable sources, IEnumerable targets, bool check_stamp = true) -#endif + public static bool IsUptodate (IToolLog log, IEnumerable sources, IEnumerable targets, bool check_stamp = true) { #if LEGACY_TOOLS || BUNDLER // msbuild does not have force if (Driver.Force) @@ -355,7 +302,7 @@ static bool IsUptodate (IEnumerable sources, IEnumerable targets foreach (var s in sources) { var sfi = new FileInfo (s); if (!sfi.Exists) { - Log (3, "Prerequisite '{0}' does not exist.", s); + log.Log (3, "Prerequisite '{0}' does not exist.", s); return false; } @@ -370,26 +317,26 @@ static bool IsUptodate (IEnumerable sources, IEnumerable targets foreach (var t in targets) { var tfi = new FileInfo (t); if (!tfi.Exists) { - Log (3, "Target '{0}' does not exist.", t); + log.Log (3, "Target '{0}' does not exist.", t); return false; } if (check_stamp) { var tfi_stamp = new FileInfo (t + ".stamp"); if (tfi_stamp.Exists && tfi_stamp.LastWriteTimeUtc > tfi.LastWriteTimeUtc) { - Log (3, "Target '{0}' has a stamp file with newer timestamp ({1} > {2}), using the stamp file's timestamp", t, tfi_stamp.LastWriteTimeUtc, tfi.LastWriteTimeUtc); + log.Log (3, "Target '{0}' has a stamp file with newer timestamp ({1} > {2}), using the stamp file's timestamp", t, tfi_stamp.LastWriteTimeUtc, tfi.LastWriteTimeUtc); tfi = tfi_stamp; } } var lwt = tfi.LastWriteTimeUtc; if (max_source > lwt) { - Log (3, "Prerequisite '{0}' is newer than target '{1}' ({2} vs {3}).", max_s, t, max_source, lwt); + log.Log (3, "Prerequisite '{0}' is newer than target '{1}' ({2} vs {3}).", max_s, t, max_source, lwt); return false; } } - Log (3, "Prerequisite(s) '{0}' are all older than the target(s) '{1}'.", string.Join ("', '", sources.ToArray ()), string.Join ("', '", targets.ToArray ())); + log.Log (3, "Prerequisite(s) '{0}' are all older than the target(s) '{1}'.", string.Join ("', '", sources.ToArray ()), string.Join ("', '", targets.ToArray ())); return true; } diff --git a/tools/common/Frameworks.cs b/tools/common/Frameworks.cs index 8f626d2d973e..3970120f17fa 100644 --- a/tools/common/Frameworks.cs +++ b/tools/common/Frameworks.cs @@ -841,7 +841,7 @@ static void Gather (Application app, IEnumerable assemblies, static bool FilterFrameworks (Application app, Framework framework) { if (framework.IsFrameworkUnavailable (app)) { - Driver.Log (3, "Not linking with the framework {0} because it's not available in the current SDK.", framework.Name); + app.Log (3, "Not linking with the framework {0} because it's not available in the current SDK.", framework.Name); return false; } diff --git a/tools/common/IToolLog.cs b/tools/common/IToolLog.cs new file mode 100644 index 000000000000..1bc5a9c41d31 --- /dev/null +++ b/tools/common/IToolLog.cs @@ -0,0 +1,68 @@ +namespace Xamarin.Bundler; + +public interface IToolLog { + int Verbosity { get; } + void Log (string message); + void LogError (string message); + // Log an error we raise ourselves (through an exception) + void LogError (Exception exception); + // Log an unexpected exception + void LogException (Exception exception); +} + +public static class IToolLogExtensions { + public static void Log (this IToolLog log, string format, params object? [] args) + { + log.Log (string.Format (format, args)); + } + + public static void Log (this IToolLog log, int min_verbosity, string message) + { + if (min_verbosity > log.Verbosity) + return; + + log.Log (message); + } + + public static void Log (this IToolLog log, int min_verbosity, string format, params object? [] args) + { + if (min_verbosity > log.Verbosity) + return; + + Log (log, format, args); + } +} + +#if !MSBUILD_TASKS +public class ConsoleLog : IToolLog { + public readonly static IToolLog Instance = new ConsoleLog (); + +#if TESTS + int verbosity = 0; +#else + int verbosity = Driver.GetDefaultVerbosity (); +#endif + + public int Verbosity { get => verbosity; } + + public void Log (string message) + { + Console.WriteLine (message); + } + + public void LogError (string message) + { + Console.Error.WriteLine (message); + } + + public void LogError (Exception exception) + { + Console.Error.WriteLine (exception); + } + + public void LogException (Exception exception) + { + Console.Error.WriteLine (exception); + } +} +#endif // !MSBUILD_TASKS diff --git a/tools/common/MachO.cs b/tools/common/MachO.cs index 0aa183a1cffc..ff06994660b7 100644 --- a/tools/common/MachO.cs +++ b/tools/common/MachO.cs @@ -269,7 +269,7 @@ public static IEnumerable Read (string filename) } } - public static List GetArchitectures (string file) + public static List GetArchitectures (IToolLog log, string file) { var result = new List (); @@ -309,7 +309,7 @@ public static List GetArchitectures (string file) result.Add (GetArch (System.Net.IPAddress.NetworkToHostOrder (reader.ReadInt32 ()), System.Net.IPAddress.NetworkToHostOrder (reader.ReadInt32 ()))); break; default: - Console.WriteLine ("File '{0}' is neither a Universal binary nor a Mach-O binary (magic: 0x{1})", file, magic.ToString ("x")); + log.Log ("File '{0}' is neither a Universal binary nor a Mach-O binary (magic: 0x{1})", file, magic.ToString ("x")); break; } } @@ -1023,10 +1023,10 @@ public MachO.LoadCommands Command { } #if DEBUG - public virtual void Dump () + public virtual void Dump (IToolLog log) { - Console.WriteLine (" cmd: {0}", cmd); - Console.WriteLine (" cmdsize: {0}", cmdsize); + log.Log (" cmd: {0}", cmd); + log.Log (" cmdsize: {0}", cmdsize); } #endif } @@ -1038,13 +1038,13 @@ public class DylibLoadCommand : LoadCommand { public uint compatibility_version; #if DEBUG - public override void Dump () + public override void Dump (IToolLog log) { - base.Dump (); - Console.WriteLine (" name: {0}", name); - Console.WriteLine (" timestamp: {0}", timestamp); - Console.WriteLine (" current_version: {0}", current_version); - Console.WriteLine (" compatibility_version: {0}", compatibility_version); + base.Dump (log); + log.Log (" name: {0}", name); + log.Log (" timestamp: {0}", timestamp); + log.Log (" current_version: {0}", current_version); + log.Log (" compatibility_version: {0}", compatibility_version); } #endif } @@ -1056,13 +1056,13 @@ public class DylibIdCommand : LoadCommand { public uint compatibility_version; #if DEBUG - public override void Dump () + public override void Dump (IToolLog log) { - base.Dump (); - Console.WriteLine (" name: {0}", name); - Console.WriteLine (" timestamp: {0}", timestamp); - Console.WriteLine (" current_version: {0}", current_version); - Console.WriteLine (" compatibility_version: {0}", compatibility_version); + base.Dump (log); + log.Log (" name: {0}", name); + log.Log (" timestamp: {0}", timestamp); + log.Log (" current_version: {0}", current_version); + log.Log (" compatibility_version: {0}", compatibility_version); } #endif } @@ -1071,11 +1071,11 @@ public class UuidCommand : LoadCommand { public byte []? uuid; #if DEBUG - public override void Dump () + public override void Dump (IToolLog log) { - base.Dump (); - Console.WriteLine (" cmd: {0}", cmd); - Console.WriteLine (" uuid: {0}", uuid); + base.Dump (log); + log.Log (" cmd: {0}", cmd); + log.Log (" uuid: {0}", uuid); } #endif } diff --git a/tools/common/PInvokeWrapperGenerator.cs b/tools/common/PInvokeWrapperGenerator.cs index a0c4592a5ccb..d8ba33801ac6 100644 --- a/tools/common/PInvokeWrapperGenerator.cs +++ b/tools/common/PInvokeWrapperGenerator.cs @@ -70,8 +70,8 @@ public void End () Registrar.GeneratePInvokeWrappersEnd (); - Driver.WriteIfDifferent (HeaderPath, hdr.ToString () + "\n" + decls.ToString () + "\n" + ifaces.ToString () + "\n", true); - Driver.WriteIfDifferent (SourcePath, mthds.ToString () + "\n" + sb.ToString () + "\n", true); + Driver.WriteIfDifferent (App, HeaderPath, hdr.ToString () + "\n" + decls.ToString () + "\n" + ifaces.ToString () + "\n", true); + Driver.WriteIfDifferent (App, SourcePath, mthds.ToString () + "\n" + sb.ToString () + "\n", true); } public void ProcessMethod (MethodDefinition method) diff --git a/tools/common/StaticRegistrar.cs b/tools/common/StaticRegistrar.cs index 9891a0c2ec6e..be257833d956 100644 --- a/tools/common/StaticRegistrar.cs +++ b/tools/common/StaticRegistrar.cs @@ -726,14 +726,14 @@ protected override bool LaxMode { } } - protected override void ReportError (int code, string message, params object [] args) + protected override void ReportError (int code, string message, params object? [] args) { throw ErrorHelper.CreateError (code, message, args); } - protected override void ReportWarning (int code, string message, params object [] args) + protected override void ReportWarning (int code, string message, params object? [] args) { - ErrorHelper.Show (ErrorHelper.CreateWarning (code, message, args)); + ErrorHelper.Show (App, ErrorHelper.CreateWarning (code, message, args)); } public static int GetValueTypeSize (TypeDefinition type) @@ -2103,7 +2103,7 @@ void CheckNamespace (string ns, List exceptions) namespaces.Add (ns); if (Driver.GetFrameworks (App).TryGetValue (ns, out var fw) && fw.IsFrameworkUnavailable (App)) { - Driver.Log (5, "Not importing the framework {0} in the generated registrar code because it's not available in the current platform.", ns); + App.Log (5, "Not importing the framework {0} in the generated registrar code because it's not available in the current platform.", ns); return; } @@ -2742,9 +2742,9 @@ public void Rewrite () var rewriter = new Rewriter (map_dict, GetAssemblies (), LinkContext); var result = rewriter.Process (); if (!string.IsNullOrEmpty (result)) { - Driver.Log (5, $"Not redirecting class handles because {result}"); + App.Log (5, $"Not redirecting class handles because {result}"); } - ErrorHelper.ThrowIfErrors (exceptions); + ErrorHelper.ThrowIfErrors (App, exceptions); } #endif } @@ -3201,7 +3201,7 @@ void Specialize (AutoIndentStringBuilder sb, out string initialization_method) sb.WriteLine (map.ToString ()); sb.WriteLine (map_init.ToString ()); - ErrorHelper.ThrowIfErrors (exceptions); + ErrorHelper.ThrowIfErrors (App, exceptions); } bool TryGetIntPtrBoolCtor (TypeDefinition type, List exceptions, [NotNullWhen (true)] out MethodDefinition? ctor) @@ -4400,11 +4400,11 @@ void SpecializePrepareReturnValue (AutoIndentStringBuilder sb, ObjCMethod method var token = "INVALID_TOKEN_REF"; if (App.Optimizations.OptimizeBlockLiteralSetupBlock == true) { if (type.Is ("System", "Delegate") || type.Is ("System", "MulticastDelegate")) { - ErrorHelper.Show (ErrorHelper.CreateWarning (App, 4173, method.Method, Errors.MT4173, type.FullName, descriptiveMethodName)); + ErrorHelper.Show (App, ErrorHelper.CreateWarning (App, 4173, method.Method, Errors.MT4173, type.FullName, descriptiveMethodName)); } else { var delegateMethod = type.Resolve ().GetMethods ().FirstOrDefault ((v) => v.Name == "Invoke"); if (delegateMethod is null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (App, 4173, method.Method, Errors.MT4173_A, type.FullName, descriptiveMethodName)); + ErrorHelper.Show (App, ErrorHelper.CreateWarning (App, 4173, method.Method, Errors.MT4173_A, type.FullName, descriptiveMethodName)); } else { signature = "\"" + ComputeSignature (method.DeclaringType.Type, null, method, isBlockSignature: true) + "\""; } @@ -4689,7 +4689,7 @@ public bool TryFindMethod (MethodDefinition method, [NotNullWhen (true)] out Obj // One common variation is that the IDE will add the BlockProxy attribute found in base methods when the user overrides those methods, // which unfortunately doesn't compile (because the type passed to the BlockProxy attribute is internal), and then // the user just modifies the attribute to something that compiles. - ErrorHelper.Show (ErrorHelper.CreateWarning (App, 4175, method, $"{(string.IsNullOrEmpty (param.Name) ? $"Parameter #{param.Index + 1}" : $"The parameter '{param.Name}'")} in the method '{GetTypeFullName (method.DeclaringType)}.{GetDescriptiveMethodName (method)}' has an invalid BlockProxy attribute (the type passed to the attribute does not have a 'Create' method).")); + ErrorHelper.Show (App, ErrorHelper.CreateWarning (App, 4175, method, $"{(string.IsNullOrEmpty (param.Name) ? $"Parameter #{param.Index + 1}" : $"The parameter '{param.Name}'")} in the method '{GetTypeFullName (method.DeclaringType)}.{GetDescriptiveMethodName (method)}' has an invalid BlockProxy attribute (the type passed to the attribute does not have a 'Create' method).")); // Returning null will make the caller look for the attribute in the base implementation. } return createMethod; @@ -4966,7 +4966,7 @@ void GenerateConversionToManaged (TypeReference inputType, TypeReference outputT func = GetNSStringToSmartEnumFunc (underlyingManagedType, inputType, outputType, descriptiveMethodName, managedClassExpression, out nativeTypeName); if (!IsSmartEnum (underlyingManagedType, out var _, out var getValueMethod)) { // method linked away!? this should already be verified - ErrorHelper.Show (ErrorHelper.CreateWarning (99, Errors.MX0099, $"the smart enum {underlyingManagedType.FullName} doesn't seem to be a smart enum after all")); + ErrorHelper.Show (App, ErrorHelper.CreateWarning (99, Errors.MX0099, $"the smart enum {underlyingManagedType.FullName} doesn't seem to be a smart enum after all")); token = "INVALID_TOKEN_REF"; } else if (TryCreateTokenReference (getValueMethod, TokenType.Method, out var get_value_method_token_ref, out _)) { token = $"0x{get_value_method_token_ref:X} /* {getValueMethod.FullName} */"; @@ -5062,7 +5062,7 @@ void GenerateConversionToNative (TypeReference inputType, TypeReference outputTy func = GetSmartEnumToNSStringFunc (underlyingManagedType, inputType, outputType, descriptiveMethodName, classVariableName); if (!IsSmartEnum (underlyingManagedType, out var getConstantMethod, out var _)) { // method linked away!? this should already be verified - ErrorHelper.Show (ErrorHelper.CreateWarning (99, Errors.MX0099, $"the smart enum {underlyingManagedType.FullName} doesn't seem to be a smart enum after all")); + ErrorHelper.Show (App, ErrorHelper.CreateWarning (99, Errors.MX0099, $"the smart enum {underlyingManagedType.FullName} doesn't seem to be a smart enum after all")); token = "INVALID_TOKEN_REF"; } else if (TryCreateTokenReference (getConstantMethod, TokenType.Method, out var get_constant_method_token_ref, out _)) { token = $"0x{get_constant_method_token_ref:X} /* {getConstantMethod.FullName} */"; @@ -5340,7 +5340,7 @@ string TryGeneratePInvokeWrapper (PInvokeWrapperGenerator state, MethodDefinitio sb.WriteLine ("}"); sb.WriteLine (); } else { - // Console.WriteLine ("Signature already processed: {0} for {1}.{2}", signature.ToString (), method.DeclaringType.FullName, method.Name); + // App.Log ("Signature already processed: {0} for {1}.{2}", signature.ToString (), method.DeclaringType.FullName, method.Name); } return wrapperName; @@ -5383,7 +5383,7 @@ public void Register (PlatformResolver? resolver, IEnumerable exceptions) [DllImport ("libc", SetLastError = true)] static extern string realpath (string path, IntPtr zero); - public static string GetRealPath (string path, bool warnIfNoSuchPathExists = true) + public static string GetRealPath (IToolLog log, string path, bool warnIfNoSuchPathExists = true) { // For some reason realpath doesn't always like filenames only, and will randomly fail. // Prepend the current directory if there's no directory specified. @@ -80,7 +80,7 @@ public static string GetRealPath (string path, bool warnIfNoSuchPathExists = tru var errno = Marshal.GetLastWin32Error (); if (warnIfNoSuchPathExists || (errno != 2)) - ErrorHelper.Warning (54, Errors.MT0054, path, FileCopier.strerror (errno), errno); + ErrorHelper.Warning (log, 54, Errors.MT0054, path, FileCopier.strerror (errno), errno); return path; } @@ -135,7 +135,7 @@ public void ComputeLinkerFlags () sb.AppendLine ("}"); sb.AppendLine (); - Driver.WriteIfDifferent (reference_m, sb.ToString (), true); + Driver.WriteIfDifferent (App, reference_m, sb.ToString (), true); return reference_m; } @@ -193,7 +193,7 @@ public void GenerateMain (StringBuilder sb, ApplePlatform platform, Abi abi, str throw ErrorHelper.CreateError (71, Errors.MX0071, platform, App.ProductName); } } - Driver.WriteIfDifferent (main_source, sb.ToString (), true); + Driver.WriteIfDifferent (App, main_source, sb.ToString (), true); } catch (ProductException) { throw; } catch (Exception e) { @@ -315,7 +315,7 @@ void GenerateMainImpl (StringWriter sw, Abi abi) sw.WriteLine ("\txamarin_executable_name = \"{0}\";", assembly_name); if (app.XamarinRuntime == XamarinRuntime.MonoVM) sw.WriteLine ("\tmono_use_llvm = {0};", enable_llvm ? "TRUE" : "FALSE"); - sw.WriteLine ("\txamarin_log_level = {0};", Driver.Verbosity.ToString (CultureInfo.InvariantCulture)); + sw.WriteLine ("\txamarin_log_level = {0};", Verbosity.ToString (CultureInfo.InvariantCulture)); sw.WriteLine ("\txamarin_arch_name = \"{0}\";", abi.AsArchString ()); if (!app.IsDefaultMarshalManagedExceptionMode) sw.WriteLine ("\txamarin_marshal_managed_exception_mode = MarshalManagedExceptionMode{0};", app.MarshalManagedExceptions); diff --git a/tools/common/cache.cs b/tools/common/cache.cs index a89b6db7753b..1dcba0073ef9 100644 --- a/tools/common/cache.cs +++ b/tools/common/cache.cs @@ -33,77 +33,79 @@ public bool IsCacheTemporary { } // see --cache=DIR - public string Location { - get { - if (cache_dir is null) { - do { - cache_dir = Path.Combine (Path.GetTempPath (), NAME + ".cache", Path.GetRandomFileName ()); - if (File.Exists (cache_dir) || Directory.Exists (cache_dir)) - continue; - Directory.CreateDirectory (cache_dir); - break; - } while (true); - - cache_dir = Application.GetRealPath (cache_dir); - - temporary_cache = true; - if (!Directory.Exists (cache_dir)) - Directory.CreateDirectory (cache_dir); -#if DEBUG - Console.WriteLine ("Cache defaults to {0}", cache_dir); -#endif - } - return cache_dir; - } - set { - cache_dir = value; + public string GetLocation (IToolLog log) + { + if (cache_dir is null) { + do { + cache_dir = Path.Combine (Path.GetTempPath (), NAME + ".cache", Path.GetRandomFileName ()); + if (File.Exists (cache_dir) || Directory.Exists (cache_dir)) + continue; + Directory.CreateDirectory (cache_dir); + break; + } while (true); + + cache_dir = Application.GetRealPath (log, cache_dir); + + temporary_cache = true; if (!Directory.Exists (cache_dir)) Directory.CreateDirectory (cache_dir); - cache_dir = Application.GetRealPath (Path.GetFullPath (cache_dir)); +#if DEBUG + log.Log ("Cache defaults to {0}", cache_dir); +#endif } + return cache_dir; + } + + public void SetLocation (IToolLog log, string value) + { + cache_dir = value; + if (!Directory.Exists (cache_dir)) + Directory.CreateDirectory (cache_dir); + cache_dir = Application.GetRealPath (log, Path.GetFullPath (cache_dir)); } - public void Clean () + public void Clean (IToolLog log) { + var location = GetLocation (log); #if DEBUG - Console.WriteLine ("Cache.Clean: {0}", Location); + log.Log ("Cache.Clean: {0}", location); #endif - Directory.Delete (Location, true); - Directory.CreateDirectory (Location); + Directory.Delete (location, true); + Directory.CreateDirectory (location); } - public static bool CompareFiles (string a, string b, bool ignore_cache = false) + public static bool CompareFiles (IToolLog log, string a, string b, bool ignore_cache = false) { if (Driver.Force && !ignore_cache) { - Driver.Log (6, "Files {0} and {1} are considered different because -f was passed to " + NAME + ".", a, b); + log.Log (6, "Files {0} and {1} are considered different because -f was passed to " + NAME + ".", a, b); return false; } if (!File.Exists (b)) { - Driver.Log (6, "Files {0} and {1} are considered different because the latter doesn't exist.", a, b); + log.Log (6, "Files {0} and {1} are considered different because the latter doesn't exist.", a, b); return false; } using (var astream = new FileStream (a, FileMode.Open, FileAccess.Read, FileShare.Read)) { using (var bstream = new FileStream (b, FileMode.Open, FileAccess.Read, FileShare.Read)) { bool rv; - Driver.Log (6, "Comparing files {0} and {1}...", a, b); - rv = CompareStreams (astream, bstream, ignore_cache); - Driver.Log (6, " > {0}", rv ? "Identical" : "Different"); + log.Log (6, "Comparing files {0} and {1}...", a, b); + rv = CompareStreams (log, astream, bstream, ignore_cache); + log.Log (6, " > {0}", rv ? "Identical" : "Different"); return rv; } } } - public unsafe static bool CompareStreams (Stream astream, Stream bstream, bool ignore_cache = false) + public unsafe static bool CompareStreams (IToolLog log, Stream astream, Stream bstream, bool ignore_cache = false) { if (Driver.Force && !ignore_cache) { - Driver.Log (6, " > streams are considered different because -f was passed to " + NAME + "."); + log.Log (6, " > streams are considered different because -f was passed to " + NAME + "."); return false; } if (astream.Length != bstream.Length) { - Driver.Log (6, " > streams are considered different because their lengths do not match."); + log.Log (6, " > streams are considered different because their lengths do not match."); return false; } @@ -151,20 +153,20 @@ void CollectArgumentsForCache (IList args, int firstArgument, StringBuil public bool IsCacheValid (Application app) { var name = "arguments"; - var pcache = Path.Combine (Location, name); + var pcache = Path.Combine (GetLocation (app), name); if (!File.Exists (pcache)) { - Driver.Log (3, "A full rebuild will be performed because the cache is either incomplete or entirely missing."); + app.Log (3, "A full rebuild will be performed because the cache is either incomplete or entirely missing."); return false; } else if (GetArgumentsForCacheData (app) != File.ReadAllText (pcache)) { - Driver.Log (3, "A full rebuild will be performed because the arguments to " + NAME + " has changed with regards to the cached data."); + app.Log (3, "A full rebuild will be performed because the arguments to " + NAME + " has changed with regards to the cached data."); return false; } // Check if mtouch/mmp has been modified. var executable = System.Reflection.Assembly.GetExecutingAssembly ().Location; - if (!Application.IsUptodate (executable, pcache)) { - Driver.Log (3, "A full rebuild will be performed because " + NAME + " has been modified."); + if (!Application.IsUptodate (app, executable, pcache)) { + app.Log (3, "A full rebuild will be performed because " + NAME + " has been modified."); return false; } @@ -174,7 +176,7 @@ public bool IsCacheValid (Application app) public bool VerifyCache (Application app) { if (!IsCacheValid (app)) { - Clean (); + Clean (app); return false; } @@ -184,54 +186,7 @@ public bool VerifyCache (Application app) public void ValidateCache (Application app) { var name = "arguments"; - var pcache = Path.Combine (Location, name); + var pcache = Path.Combine (GetLocation (app), name); File.WriteAllText (pcache, GetArgumentsForCacheData (app)); } - -#if false - static public void ComputeDependencies (IEnumerable assemblies, MonoTouchResolver resolver) - { - // note: Parallel.ForEach (with lock to add on 'digests') turns out (much) slower - // (linksdk.app with 20 assemblies) - // likely because it's faster (using commoncrypto) than it seems - foreach (string a in assemblies) { - string key = Path.GetFileNameWithoutExtension (a); - using (Stream fs = File.OpenRead (a)) { - string digest = ComputeDigest (fs, 140); - digests.Add (key, digest); - } - } - - Dictionary> dependencies = new Dictionary> (); - foreach (string a in assemblies) { - HashSet references; - AssemblyDefinition ad = resolver.Load (a); - foreach (AssemblyNameReference ar in ad.MainModule.AssemblyReferences) { - if (!dependencies.TryGetValue (ar.Name, out references)) { - references = new HashSet (); - dependencies.Add (ar.Name, references); - } - references.Add (ad.Name.Name); - } - } -#if DEBUG - foreach (var kvp in dependencies) { - Console.WriteLine ("The following assemblies depends on {0}", kvp.Key); - foreach (var s in kvp.Value) - Console.WriteLine ("\t{0}", s); - } -#endif - // if a dependency has changed everything that depends on it must be cleaned - foreach (var kvp in dependencies) { - string cname = kvp.Key + ".*.cache." + GetDigestForAssembly (kvp.Key) + ".o"; - var files = Directory.GetFiles (Location, cname); - if (files.Length != 0) - continue; - - Clean (kvp.Key + "*"); - foreach (var deps in kvp.Value) - Clean (deps + "*"); - } - } -#endif } diff --git a/tools/dotnet-linker/BackingFieldDelayHandler.cs b/tools/dotnet-linker/BackingFieldDelayHandler.cs index be9b46adf828..c77ad969e721 100644 --- a/tools/dotnet-linker/BackingFieldDelayHandler.cs +++ b/tools/dotnet-linker/BackingFieldDelayHandler.cs @@ -68,6 +68,7 @@ protected override void Process (MethodDefinition method) public static void ReapplyDisposedFields (DerivedLinkContext context, string operation) { // note: all methods in the dictionary are marked (since they were added from an IMarkHandler) + var app = context.App; foreach ((var method, var body) in dispose) { foreach (var ins in body.Instructions) { switch (ins.OpCode.OperandType) { @@ -79,9 +80,9 @@ public static void ReapplyDisposedFields (DerivedLinkContext context, string ope var store_field = ins; var load_null = ins.Previous; var load_this = ins.Previous.Previous; - if (OptimizeGeneratedCodeHandler.ValidateInstruction (method, store_field, operation, Code.Stfld) && - OptimizeGeneratedCodeHandler.ValidateInstruction (method, load_null, operation, Code.Ldnull) && - OptimizeGeneratedCodeHandler.ValidateInstruction (method, load_this, operation, Code.Ldarg_0)) { + if (OptimizeGeneratedCodeHandler.ValidateInstruction (app, method, store_field, operation, Code.Stfld) && + OptimizeGeneratedCodeHandler.ValidateInstruction (app, method, load_null, operation, Code.Ldnull) && + OptimizeGeneratedCodeHandler.ValidateInstruction (app, method, load_this, operation, Code.Ldarg_0)) { store_field.OpCode = OpCodes.Nop; load_null.OpCode = OpCodes.Nop; load_this.OpCode = OpCodes.Nop; diff --git a/tools/dotnet-linker/DotNetResolver.cs b/tools/dotnet-linker/DotNetResolver.cs index 37e04bb9e1f9..f71c2ec5b4cb 100644 --- a/tools/dotnet-linker/DotNetResolver.cs +++ b/tools/dotnet-linker/DotNetResolver.cs @@ -8,6 +8,10 @@ namespace Xamarin.Linker { public class DotNetResolver : CoreResolver { + public DotNetResolver (Application app) + { + } + public override AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters) { throw new NotImplementedException ($"Unable to resolve the assembly reference {name}"); diff --git a/tools/dotnet-linker/LinkerConfiguration.cs b/tools/dotnet-linker/LinkerConfiguration.cs index b42db901cbad..a7d9909785ed 100644 --- a/tools/dotnet-linker/LinkerConfiguration.cs +++ b/tools/dotnet-linker/LinkerConfiguration.cs @@ -47,7 +47,7 @@ public class LinkerConfiguration { public Version? SdkVersion { get; private set; } public string SdkRootDirectory { get; private set; } = string.Empty; public string TypeMapFilePath { get; set; } = string.Empty; - public int Verbosity => Driver.Verbosity; + public int Verbosity => Application.Verbosity; public string XamarinNativeLibraryDirectory { get; private set; } = string.Empty; static ConditionalWeakTable configurations = new ConditionalWeakTable (); @@ -397,7 +397,7 @@ public static LinkerConfiguration GetInstance (LinkContext context) case "Verbosity": if (!int.TryParse (value, out var verbosity)) throw new InvalidOperationException ($"Invalid Verbosity '{value}' in {linker_file}"); - Driver.Verbosity += verbosity; + Application.Verbosity += verbosity; break; case "Warn": try { @@ -438,7 +438,7 @@ public static LinkerConfiguration GetInstance (LinkContext context) if (!StringUtils.IsNullOrEmpty (user_optimize_flags)) { var messages = new List (); Application.Optimizations.Parse (Application.Platform, user_optimize_flags, messages); - ErrorHelper.Show (messages); + ErrorHelper.Show (Application, messages); } if (use_llvm) { @@ -447,7 +447,7 @@ public static LinkerConfiguration GetInstance (LinkContext context) Application.CreateCache (significantLines.ToArray ()); if (Application.Cache is not null) - Application.Cache.Location = CacheDirectory; + Application.Cache.SetLocation (Application, CacheDirectory); if (DeploymentTarget is not null) Application.DeploymentTarget = DeploymentTarget; if (SdkVersion is not null) { @@ -472,7 +472,7 @@ public static LinkerConfiguration GetInstance (LinkContext context) throw ErrorHelper.CreateError (99, "Inconsistent platforms. TargetFramework={0}, Platform={1}", Driver.TargetFramework.Platform, Platform); if (Application.XamarinRuntime != XamarinRuntime.MonoVM && Application.UseInterpreter) { - Driver.Log (4, "The interpreter is enabled, but the current runtime isn't MonoVM. The interpreter settings will be ignored."); + Application.Log (4, "The interpreter is enabled, but the current runtime isn't MonoVM. The interpreter settings will be ignored."); Application.UnsetInterpreter (); } @@ -539,52 +539,52 @@ AssemblyBuildTarget ParseLinkMode (string value, string variableName) public void Write () { if (Verbosity > 0) { - Console.WriteLine ($"LinkerConfiguration:"); - Console.WriteLine ($" ABI: {Abi.AsArchString ()}"); - Console.WriteLine ($" AOTArguments: {string.Join (", ", Application.AotArguments)}"); - Console.WriteLine ($" AOTOutputDirectory: {AOTOutputDirectory}"); - Console.WriteLine ($" DedupAssembly: {DedupAssembly}"); - Console.WriteLine ($" AppBundleManifestPath: {Application.InfoPListPath}"); - Console.WriteLine ($" AreAnyAssembliesTrimmed: {Application.AreAnyAssembliesTrimmed}"); - Console.WriteLine ($" AssemblyName: {Application.AssemblyName}"); - Console.WriteLine ($" CacheDirectory: {CacheDirectory}"); - Console.WriteLine ($" Debug: {Application.EnableDebug}"); - Console.WriteLine ($" Dlsym: {Application.DlsymOptions} {(Application.DlsymAssemblies is not null ? string.Join (" ", Application.DlsymAssemblies.Select (v => (v.Item2 ? "+" : "-") + v.Item1)) : string.Empty)}"); - Console.WriteLine ($" DeploymentTarget: {DeploymentTarget}"); - Console.WriteLine ($" EnableSGenConc {Application.EnableSGenConc}"); - Console.WriteLine ($" InlineDlfcnMethods: {InlineDlfcnMethods}"); - Console.WriteLine ($" IntermediateLinkDir: {IntermediateLinkDir}"); - Console.WriteLine ($" IntermediateOutputPath: {IntermediateOutputPath}"); - Console.WriteLine ($" InterpretedAssemblies: {string.Join (", ", Application.InterpretedAssemblies)}"); - Console.WriteLine ($" ItemsDirectory: {ItemsDirectory}"); - Console.WriteLine ($" {FrameworkAssemblies.Count} framework assemblies:"); + Application.Log ($"LinkerConfiguration:"); + Application.Log ($" ABI: {Abi.AsArchString ()}"); + Application.Log ($" AOTArguments: {string.Join (", ", Application.AotArguments)}"); + Application.Log ($" AOTOutputDirectory: {AOTOutputDirectory}"); + Application.Log ($" DedupAssembly: {DedupAssembly}"); + Application.Log ($" AppBundleManifestPath: {Application.InfoPListPath}"); + Application.Log ($" AreAnyAssembliesTrimmed: {Application.AreAnyAssembliesTrimmed}"); + Application.Log ($" AssemblyName: {Application.AssemblyName}"); + Application.Log ($" CacheDirectory: {CacheDirectory}"); + Application.Log ($" Debug: {Application.EnableDebug}"); + Application.Log ($" Dlsym: {Application.DlsymOptions} {(Application.DlsymAssemblies is not null ? string.Join (" ", Application.DlsymAssemblies.Select (v => (v.Item2 ? "+" : "-") + v.Item1)) : string.Empty)}"); + Application.Log ($" DeploymentTarget: {DeploymentTarget}"); + Application.Log ($" EnableSGenConc {Application.EnableSGenConc}"); + Application.Log ($" InlineDlfcnMethods: {InlineDlfcnMethods}"); + Application.Log ($" IntermediateLinkDir: {IntermediateLinkDir}"); + Application.Log ($" IntermediateOutputPath: {IntermediateOutputPath}"); + Application.Log ($" InterpretedAssemblies: {string.Join (", ", Application.InterpretedAssemblies)}"); + Application.Log ($" ItemsDirectory: {ItemsDirectory}"); + Application.Log ($" {FrameworkAssemblies.Count} framework assemblies:"); foreach (var fw in FrameworkAssemblies.OrderBy (v => v)) - Console.WriteLine ($" {fw}"); - Console.WriteLine ($" IsSimulatorBuild: {IsSimulatorBuild}"); - Console.WriteLine ($" MarshalManagedExceptions: {Application.MarshalManagedExceptions} (IsDefault: {Application.IsDefaultMarshalManagedExceptionMode})"); - Console.WriteLine ($" MarshalObjectiveCExceptions: {Application.MarshalObjectiveCExceptions}"); - Console.WriteLine ($" {Application.MonoLibraries.Count} mono libraries:"); + Application.Log ($" {fw}"); + Application.Log ($" IsSimulatorBuild: {IsSimulatorBuild}"); + Application.Log ($" MarshalManagedExceptions: {Application.MarshalManagedExceptions} (IsDefault: {Application.IsDefaultMarshalManagedExceptionMode})"); + Application.Log ($" MarshalObjectiveCExceptions: {Application.MarshalObjectiveCExceptions}"); + Application.Log ($" {Application.MonoLibraries.Count} mono libraries:"); foreach (var lib in Application.MonoLibraries.OrderBy (v => v)) - Console.WriteLine ($" {lib}"); - Console.WriteLine ($" Optimize: {user_optimize_flags} => {Application.Optimizations}"); - Console.WriteLine ($" PartialStaticRegistrarLibrary: {PartialStaticRegistrarLibrary}"); - Console.WriteLine ($" Platform: {Platform}"); - Console.WriteLine ($" PlatformAssembly: {PlatformAssembly}.dll"); - Console.WriteLine ($" RelativeAppBundlePath: {RelativeAppBundlePath}"); - Console.WriteLine ($" Registrar: {Application.Registrar} (Options: {Application.RegistrarOptions})"); - Console.WriteLine ($" RuntimeConfigurationFile: {Application.RuntimeConfigurationFile}"); - Console.WriteLine ($" RequirePInvokeWrappers: {Application.RequiresPInvokeWrappers}"); - Console.WriteLine ($" SdkDevPath: {Driver.SdkRoot}"); - Console.WriteLine ($" SdkRootDirectory: {SdkRootDirectory}"); - Console.WriteLine ($" SdkVersion: {SdkVersion}"); - Console.WriteLine ($" TypeMapAssemblyName: {Application.TypeMapAssemblyName}"); - Console.WriteLine ($" TypeMapFilePath: {TypeMapFilePath}"); - Console.WriteLine ($" TypeMapOutputDirectory: {Application.TypeMapOutputDirectory}"); - Console.WriteLine ($" UseInterpreter: {Application.UseInterpreter}"); - Console.WriteLine ($" UseLlvm: {Application.IsLLVM}"); - Console.WriteLine ($" Verbosity: {Verbosity}"); - Console.WriteLine ($" XamarinNativeLibraryDirectory: {XamarinNativeLibraryDirectory}"); - Console.WriteLine ($" XamarinRuntime: {Application.XamarinRuntime}"); + Application.Log ($" {lib}"); + Application.Log ($" Optimize: {user_optimize_flags} => {Application.Optimizations}"); + Application.Log ($" PartialStaticRegistrarLibrary: {PartialStaticRegistrarLibrary}"); + Application.Log ($" Platform: {Platform}"); + Application.Log ($" PlatformAssembly: {PlatformAssembly}.dll"); + Application.Log ($" RelativeAppBundlePath: {RelativeAppBundlePath}"); + Application.Log ($" Registrar: {Application.Registrar} (Options: {Application.RegistrarOptions})"); + Application.Log ($" RuntimeConfigurationFile: {Application.RuntimeConfigurationFile}"); + Application.Log ($" RequirePInvokeWrappers: {Application.RequiresPInvokeWrappers}"); + Application.Log ($" SdkDevPath: {Driver.SdkRoot}"); + Application.Log ($" SdkRootDirectory: {SdkRootDirectory}"); + Application.Log ($" SdkVersion: {SdkVersion}"); + Application.Log ($" TypeMapAssemblyName: {Application.TypeMapAssemblyName}"); + Application.Log ($" TypeMapFilePath: {TypeMapFilePath}"); + Application.Log ($" TypeMapOutputDirectory: {Application.TypeMapOutputDirectory}"); + Application.Log ($" UseInterpreter: {Application.UseInterpreter}"); + Application.Log ($" UseLlvm: {Application.IsLLVM}"); + Application.Log ($" Verbosity: {Verbosity}"); + Application.Log ($" XamarinNativeLibraryDirectory: {XamarinNativeLibraryDirectory}"); + Application.Log ($" XamarinRuntime: {Application.XamarinRuntime}"); } } @@ -644,7 +644,7 @@ public static void Report (LinkContext context, IList exceptions) context.LogMessage (msg); } // ErrorHelper.Show will print our errors and warnings to stderr. - ErrorHelper.Show (list); + ErrorHelper.Show (ConsoleLog.Instance, list); } public IEnumerable GetNonDeletedAssemblies (BaseStep step) diff --git a/tools/dotnet-linker/PreserveSmartEnumConversionsStep.cs b/tools/dotnet-linker/PreserveSmartEnumConversionsStep.cs index 75ebca8360dd..7dae7ba20e13 100644 --- a/tools/dotnet-linker/PreserveSmartEnumConversionsStep.cs +++ b/tools/dotnet-linker/PreserveSmartEnumConversionsStep.cs @@ -94,6 +94,7 @@ class PreserveSmartEnumConversion { Dictionary> cache = new (); public DerivedLinkContext LinkContext { get; private set; } + public Application App => LinkContext.App; Func, bool, MethodDefinition? [], bool> preserve { get; set; } @@ -122,14 +123,14 @@ public bool ProcessAttributeProvider (ICustomAttributeProvider provider, params continue; if (ca.ConstructorArguments.Count != 1) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 4124, provider, Errors.MT4124_E, provider.AsString (), ca.ConstructorArguments.Count)); + ErrorHelper.Show (App, ErrorHelper.CreateWarning (LinkContext.App, 4124, provider, Errors.MT4124_E, provider.AsString (), ca.ConstructorArguments.Count)); continue; } var managedType = ca.ConstructorArguments [0].Value as TypeReference; var managedEnumType = managedType?.GetElementType ().Resolve (); if (managedEnumType is null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 4124, provider, Errors.MT4124_H, provider.AsString (), managedType?.FullName ?? "(null)")); + ErrorHelper.Show (App, ErrorHelper.CreateWarning (LinkContext.App, 4124, provider, Errors.MT4124_H, provider.AsString (), managedType?.FullName ?? "(null)")); continue; } @@ -155,7 +156,7 @@ public bool ProcessAttributeProvider (ICustomAttributeProvider provider, params break; } if (extensionType is null) { - Driver.Log (1, $"Could not find a smart extension type for the enum {managedEnumType.FullName} (due to BindAs attribute on {provider.AsString ()}): most likely this is because the enum isn't a smart enum."); + App.Log (1, $"Could not find a smart extension type for the enum {managedEnumType.FullName} (due to BindAs attribute on {provider.AsString ()}): most likely this is because the enum isn't a smart enum."); continue; } @@ -184,12 +185,12 @@ public bool ProcessAttributeProvider (ICustomAttributeProvider provider, params } if (getConstant is null) { - Driver.Log (1, $"Could not find the GetConstant method on the supposedly smart extension type {extensionType.FullName} for the enum {managedEnumType.FullName} (due to BindAs attribute on {provider.AsString ()}): most likely this is because the enum isn't a smart enum."); + App.Log (1, $"Could not find the GetConstant method on the supposedly smart extension type {extensionType.FullName} for the enum {managedEnumType.FullName} (due to BindAs attribute on {provider.AsString ()}): most likely this is because the enum isn't a smart enum."); continue; } if (getValue is null) { - Driver.Log (1, $"Could not find the GetValue method on the supposedly smart extension type {extensionType.FullName} for the enum {managedEnumType.FullName} (due to BindAs attribute on {provider.AsString ()}): most likely this is because the enum isn't a smart enum."); + App.Log (1, $"Could not find the GetValue method on the supposedly smart extension type {extensionType.FullName} for the enum {managedEnumType.FullName} (due to BindAs attribute on {provider.AsString ()}): most likely this is because the enum isn't a smart enum."); continue; } diff --git a/tools/dotnet-linker/Steps/ExceptionalMarkHandler.cs b/tools/dotnet-linker/Steps/ExceptionalMarkHandler.cs index 4eda1e9f18f3..25153aca76fa 100644 --- a/tools/dotnet-linker/Steps/ExceptionalMarkHandler.cs +++ b/tools/dotnet-linker/Steps/ExceptionalMarkHandler.cs @@ -34,6 +34,8 @@ public virtual void Initialize (LinkContext context) protected Profile Profile => Configuration.Profile; + protected Application App => Configuration.Application; + public void ProcessAssembly (AssemblyDefinition assembly) { try { diff --git a/tools/dotnet-linker/Steps/InlineClassGetHandleStep.cs b/tools/dotnet-linker/Steps/InlineClassGetHandleStep.cs index 1a48cba7b3b0..4cb66ed8fd27 100644 --- a/tools/dotnet-linker/Steps/InlineClassGetHandleStep.cs +++ b/tools/dotnet-linker/Steps/InlineClassGetHandleStep.cs @@ -61,7 +61,7 @@ protected override void TryProcess () Frameworks.TryGetFramework (App, td, out string? framework); sb.AppendLine ($"Class={info.ExportedName}|Framework={framework}|Introduced={introduced}|IsWrapper={info.IsWrapper}|IsStubClass={info.IsStubClass}"); } - Driver.WriteIfDifferent (Configuration.TypeMapFilePath, sb.ToString ()); + Driver.WriteIfDifferent (App, Configuration.TypeMapFilePath, sb.ToString ()); } base.TryProcess (); @@ -153,17 +153,17 @@ bool isOurOwnCode () var ldstr = instr.Previous; if (ldstr.OpCode != OpCodes.Ldstr) { if (!isOurOwnCode ()) - Driver.Log (3, "Unknown or unsupported pattern in call to Class.GetHandle in '{0}': {1}. The call will not be inlined.", FormatMethod (method), ldstr); + App.Log (3, "Unknown or unsupported pattern in call to Class.GetHandle in '{0}': {1}. The call will not be inlined.", FormatMethod (method), ldstr); continue; } if (ldstr.Operand is not string objectiveCClassName) { if (!isOurOwnCode ()) - Driver.Log (3, "Unknown or unsupported pattern in call to Class.GetHandle in '{0}': {1}. The call will not be inlined.", FormatMethod (method), ldstr.Operand); + App.Log (3, "Unknown or unsupported pattern in call to Class.GetHandle in '{0}': {1}. The call will not be inlined.", FormatMethod (method), ldstr.Operand); continue; } if (!objectiveCTypeMap.TryGetValue (objectiveCClassName, out var objCType)) { - Driver.Log (3, "Could not find a managed type for the Objective-C type '{1}' in the call to Class.GetHandle in '{0}', assuming the Objective-C type is available in the simulator.", FormatMethod (method), objectiveCClassName); + App.Log (3, "Could not find a managed type for the Objective-C type '{1}' in the call to Class.GetHandle in '{0}', assuming the Objective-C type is available in the simulator.", FormatMethod (method), objectiveCClassName); } if (!strictMode) { @@ -171,12 +171,12 @@ bool isOurOwnCode () // This is a call to Class.GetHandle for the same class that it's being called from, this is OK. } else if (ListExportedSymbols.TryGetRequiredObjectiveCType (DerivedLinkContext, method.DeclaringType, out var exportedName)) { if (exportedName != objectiveCClassName) { - Driver.Log (3, "The call to Class.GetHandle in '{0}' is trying to get the handle for the Objective-C class '{1}', but the declaring type's exported name is '{2}', not '{1}'. Since we're in compat mode, we're assuming the class should not be preserved.", FormatMethod (method), objectiveCClassName, exportedName); + App.Log (3, "The call to Class.GetHandle in '{0}' is trying to get the handle for the Objective-C class '{1}', but the declaring type's exported name is '{2}', not '{1}'. Since we're in compat mode, we're assuming the class should not be preserved.", FormatMethod (method), objectiveCClassName, exportedName); continue; } } else { if (App.StaticRegistrar.GetCategoryAttribute (method.DeclaringType) is not null) - Driver.Log (3, "The call to Class.GetHandle in '{0}' is trying to get the handle for the Objective-C class '{1}', but we couldn't determine whether this class should be statically preserved or not. Since we're in compat mode, we're assuming the class should not be statically preserved.", FormatMethod (method), objectiveCClassName); + App.Log (3, "The call to Class.GetHandle in '{0}' is trying to get the handle for the Objective-C class '{1}', but we couldn't determine whether this class should be statically preserved or not. Since we're in compat mode, we're assuming the class should not be statically preserved.", FormatMethod (method), objectiveCClassName); continue; } } @@ -184,20 +184,20 @@ bool isOurOwnCode () // Check if the Objective-C class is listed as a ReferenceNativeSymbol with Ignore mode, and if so, don't inline the call to Class.GetHandle (because the native symbol won't be available at link time) var existingSymbol = DerivedLinkContext.RequiredSymbols.Find (Symbol.ObjectiveCPrefix + objectiveCClassName); if (existingSymbol is not null && existingSymbol.Type == SymbolType.ObjectiveCClass && existingSymbol.Mode == SymbolMode.Ignore) { - Driver.Log (3, "Not inlining the call to Class.GetHandle (\"{0}\") in method {1} because the class is listed as a ReferenceNativeSymbol with Ignore mode.", objectiveCClassName, FormatMethod (method)); + App.Log (3, "Not inlining the call to Class.GetHandle (\"{0}\") in method {1} because the class is listed as a ReferenceNativeSymbol with Ignore mode.", objectiveCClassName, FormatMethod (method)); continue; } if (objCType is not null) { if (DerivedLinkContext.App.IsSimulatorBuild) { if (DerivedLinkContext.HasAvailabilityAttributesShowingUnavailableInSimulator (objCType.Type.Resolve (), method)) { - Driver.Log (3, "Not inlining the call to Class.GetHandle (\"{0}\") in method {1} because the type is marked with an attribute indicating it's not available in the simulator.", objectiveCClassName, FormatMethod (method)); + App.Log (3, "Not inlining the call to Class.GetHandle (\"{0}\") in method {1} because the type is marked with an attribute indicating it's not available in the simulator.", objectiveCClassName, FormatMethod (method)); continue; } } if (IsUnsupported (objCType.Type.Resolve ())) { - Driver.Log (3, "Not inlining the call to Class.GetHandle (\"{0}\") in method {1} because the type is marked with an [UnsupportedOSPlatform] attribute.", objectiveCClassName, FormatMethod (method)); + App.Log (3, "Not inlining the call to Class.GetHandle (\"{0}\") in method {1} because the type is marked with an [UnsupportedOSPlatform] attribute.", objectiveCClassName, FormatMethod (method)); continue; } @@ -207,7 +207,7 @@ bool isOurOwnCode () } if (Frameworks.TryGetFramework (App, objCType.Type.Resolve (), out Framework? framework) && framework.IsFrameworkUnavailable (App)) { - Driver.Log (3, "Not inlining the call to Class.GetHandle (\"{0}\") in method {1} because the framework {2} is unavailable.", objectiveCClassName, FormatMethod (method), framework.Name); + App.Log (3, "Not inlining the call to Class.GetHandle (\"{0}\") in method {1} because the framework {2} is unavailable.", objectiveCClassName, FormatMethod (method), framework.Name); continue; } diff --git a/tools/dotnet-linker/Steps/InlineDlfcnMethodsStep.cs b/tools/dotnet-linker/Steps/InlineDlfcnMethodsStep.cs index 99b67d4a9984..95913f88ead9 100644 --- a/tools/dotnet-linker/Steps/InlineDlfcnMethodsStep.cs +++ b/tools/dotnet-linker/Steps/InlineDlfcnMethodsStep.cs @@ -43,7 +43,7 @@ protected override bool ProcessType (TypeDefinition type) var modified = false; if (type.HasMethods) { if (Frameworks.TryGetFramework (App, type, out Framework? framework) && framework.IsFrameworkUnavailable (App)) { - Driver.Log (3, $"Type {type.FullName} appears to be part of the '{framework.Name}' framework, which is not available in the current SDK. Skipping inlining Dlfcn calls for this type."); + App.Log (3, $"Type {type.FullName} appears to be part of the '{framework.Name}' framework, which is not available in the current SDK. Skipping inlining Dlfcn calls for this type."); return modified; } @@ -525,11 +525,11 @@ protected override bool ProcessMethod (MethodDefinition method) if (DerivedLinkContext.App.IsSimulatorBuild) { // if the method or its declaring type aren't available in the simulator, and we're building for the simulator, then don't inline. if (DerivedLinkContext.HasAvailabilityAttributesShowingUnavailableInSimulator (method, method)) { - Driver.Log (3, $"Method {method.FullName} is not available in the simulator. Skipping inlining Dlfcn calls for this method."); + App.Log (3, $"Method {method.FullName} is not available in the simulator. Skipping inlining Dlfcn calls for this method."); return modified; } if (DerivedLinkContext.HasAvailabilityAttributesShowingUnavailableInSimulator (method.DeclaringType, method)) { - Driver.Log (3, $"Type {method.DeclaringType.FullName} is not available in the simulator. Skipping inlining Dlfcn calls for this type."); + App.Log (3, $"Type {method.DeclaringType.FullName} is not available in the simulator. Skipping inlining Dlfcn calls for this type."); return modified; } } diff --git a/tools/dotnet-linker/Steps/ManagedRegistrarLookupTablesStep.cs b/tools/dotnet-linker/Steps/ManagedRegistrarLookupTablesStep.cs index a0d764222e29..68bbe468e009 100644 --- a/tools/dotnet-linker/Steps/ManagedRegistrarLookupTablesStep.cs +++ b/tools/dotnet-linker/Steps/ManagedRegistrarLookupTablesStep.cs @@ -330,7 +330,7 @@ void GenerateConstructNSObject (TypeDefinition registrarType) foreach (var type in types) { var ctorRef = FindNSObjectConstructor (type); if (ctorRef is null) { - Driver.Log (9, $"Cannot include {type.FullName} in ConstructNSObject because it doesn't have a suitable constructor"); + App.Log (9, $"Cannot include {type.FullName} in ConstructNSObject because it doesn't have a suitable constructor"); continue; } @@ -652,7 +652,7 @@ void GenerateLookupUnmanagedFunction (TypeDefinition registrar_type, IList 0) { // All the methods in a given assembly will have consecutive IDs (but might not start at 0). if (trampolineInfos.First ().Id + trampolineInfos.Count - 1 != trampolineInfos.Last ().Id) diff --git a/tools/dotnet-linker/Steps/ManagedRegistrarStep.cs b/tools/dotnet-linker/Steps/ManagedRegistrarStep.cs index cfc23620e1d7..70c4cc72a7bf 100644 --- a/tools/dotnet-linker/Steps/ManagedRegistrarStep.cs +++ b/tools/dotnet-linker/Steps/ManagedRegistrarStep.cs @@ -1315,7 +1315,7 @@ void GenerateConversionToNative (MethodDefinition method, ILProcessor il, TypeRe } else if (underlyingNativeType.Is ("Foundation", "NSString")) { if (!StaticRegistrar.IsSmartEnum (underlyingManagedType, out var getConstantMethod, out var getValueMethod)) { // method linked away!? this should already be verified - ErrorHelper.Show (ErrorHelper.CreateError (99, Errors.MX0099, $"the smart enum {underlyingManagedType.FullName} doesn't seem to be a smart enum after all")); + ErrorHelper.Show (App, ErrorHelper.CreateError (99, Errors.MX0099, $"the smart enum {underlyingManagedType.FullName} doesn't seem to be a smart enum after all")); return; } diff --git a/tools/dotnet-linker/dotnet-linker.csproj b/tools/dotnet-linker/dotnet-linker.csproj index 853bba62528f..c3ff1f126681 100644 --- a/tools/dotnet-linker/dotnet-linker.csproj +++ b/tools/dotnet-linker/dotnet-linker.csproj @@ -71,6 +71,9 @@ external/tools/common/FileUtils.cs + + external/tools/common/IToolLog.cs + external/tools/common/LinkMode.cs diff --git a/tools/linker/CoreOptimizeGeneratedCode.cs b/tools/linker/CoreOptimizeGeneratedCode.cs index 85cf938052a0..c33d231aeb66 100644 --- a/tools/linker/CoreOptimizeGeneratedCode.cs +++ b/tools/linker/CoreOptimizeGeneratedCode.cs @@ -67,28 +67,33 @@ static protected void Nop (Instruction ins) ins.Operand = null; } - internal static bool ValidateInstruction (MethodDefinition caller, Instruction ins, string operation, Code expected) + internal static bool ValidateInstruction (OptimizeGeneratedCodeData data, MethodDefinition caller, Instruction ins, string operation, Code expected) + { + return ValidateInstruction (data.App, caller, ins, operation, expected); + } + + internal static bool ValidateInstruction (IToolLog log, MethodDefinition caller, Instruction ins, string operation, Code expected) { if (ins.OpCode.Code != expected) { - Driver.Log (1, "Could not {0} in {1} at offset {2}, expected {3} got {4}", operation, caller, ins.Offset, expected, ins); + log.Log (1, "Could not {0} in {1} at offset {2}, expected {3} got {4}", operation, caller, ins.Offset, expected, ins); return false; } return true; } - internal static bool ValidateInstruction (MethodDefinition caller, Instruction ins, string operation, params Code [] expected) + internal static bool ValidateInstruction (OptimizeGeneratedCodeData data, MethodDefinition caller, Instruction ins, string operation, params Code [] expected) { foreach (var code in expected) { if (ins.OpCode.Code == code) return true; } - Driver.Log (1, "Could not {0} in {1} at offset {2}, expected any of [{3}] got {4}", operation, caller, ins.Offset, string.Join (", ", expected), ins); + data.App.Log (1, "Could not {0} in {1} at offset {2}, expected any of [{3}] got {4}", operation, caller, ins.Offset, string.Join (", ", expected), ins); return false; } - static int? GetConstantValue (Instruction? ins) + static int? GetConstantValue (OptimizeGeneratedCodeData data, Instruction? ins) { if (ins is null) return null; @@ -159,13 +164,13 @@ internal static bool ValidateInstruction (MethodDefinition caller, Instruction i #endif default: #if DEBUG - Driver.Log (9, "Unknown conditional instruction: {0}", ins); + data.App.Log (9, "Unknown conditional instruction: {0}", ins); #endif return null; } } - static bool MarkInstructions (MethodDefinition method, Mono.Collections.Generic.Collection instructions, bool [] reachable, int start, int end) + static bool MarkInstructions (OptimizeGeneratedCodeData data, MethodDefinition method, Mono.Collections.Generic.Collection instructions, bool [] reachable, int start, int end) { if (reachable [start]) return true; // We've already marked this section of code @@ -178,7 +183,7 @@ static bool MarkInstructions (MethodDefinition method, Mono.Collections.Generic. case FlowControl.Branch: // Unconditional branch, we continue marking from the instruction that we branch to. var br_target = (Instruction) ins.Operand; - return MarkInstructions (method, instructions, reachable, instructions.IndexOf (br_target), instructions.Count); + return MarkInstructions (data, method, instructions, reachable, instructions.IndexOf (br_target), instructions.Count); case FlowControl.Cond_Branch: // Conditional instruction, we need to check if we can calculate a constant value for the condition var cond_target = ins.Operand as Instruction; @@ -190,26 +195,26 @@ static bool MarkInstructions (MethodDefinition method, Mono.Collections.Generic. // FIXME: calculate the potential constant branch (currently there are no optimizable methods where the switch condition is constant, so this is not needed for now) var targets = ins.Operand as Instruction []; if (targets is null) { - Driver.Log (4, "Can't optimize {0} because of unknown target of branch instruction {1} {2}", method, ins, ins.Operand); + data.App.Log (4, "Can't optimize {0} because of unknown target of branch instruction {1} {2}", method, ins, ins.Operand); return false; } foreach (var target in targets) { // not constant, continue marking both this code sequence and the branched sequence - if (!MarkInstructions (method, instructions, reachable, instructions.IndexOf (target), end)) + if (!MarkInstructions (data, method, instructions, reachable, instructions.IndexOf (target), end)) return false; } - return MarkInstructions (method, instructions, reachable, instructions.IndexOf (ins.Next), end); + return MarkInstructions (data, method, instructions, reachable, instructions.IndexOf (ins.Next), end); } if (cond_target is null) { - Driver.Log (4, "Can't optimize {0} because of unknown target of branch instruction {1} {2}", method, ins, ins.Operand); + data.App.Log (4, "Can't optimize {0} because of unknown target of branch instruction {1} {2}", method, ins, ins.Operand); return false; } switch (ins.OpCode.Code) { case Code.Brtrue: case Code.Brtrue_S: { - var v = GetConstantValue (ins.Previous); + var v = GetConstantValue (data, ins.Previous); if (v.HasValue) branch = v.Value != 0; cond_instruction_count = 2; @@ -217,7 +222,7 @@ static bool MarkInstructions (MethodDefinition method, Mono.Collections.Generic. } case Code.Brfalse: case Code.Brfalse_S: { - var v = GetConstantValue (ins.Previous); + var v = GetConstantValue (data, ins.Previous); if (v.HasValue) branch = v.Value == 0; cond_instruction_count = 2; @@ -225,8 +230,8 @@ static bool MarkInstructions (MethodDefinition method, Mono.Collections.Generic. } case Code.Beq: case Code.Beq_S: { - var x1 = GetConstantValue (ins.Previous?.Previous); - var x2 = GetConstantValue (ins.Previous); + var x1 = GetConstantValue (data, ins.Previous?.Previous); + var x2 = GetConstantValue (data, ins.Previous); if (x1.HasValue && x2.HasValue) branch = x1.Value == x2.Value; cond_instruction_count = 3; @@ -234,8 +239,8 @@ static bool MarkInstructions (MethodDefinition method, Mono.Collections.Generic. } case Code.Bne_Un: case Code.Bne_Un_S: { - var x1 = GetConstantValue (ins.Previous?.Previous); - var x2 = GetConstantValue (ins.Previous); + var x1 = GetConstantValue (data, ins.Previous?.Previous); + var x2 = GetConstantValue (data, ins.Previous); if (x1.HasValue && x2.HasValue) branch = x1.Value != x2.Value; cond_instruction_count = 3; @@ -245,8 +250,8 @@ static bool MarkInstructions (MethodDefinition method, Mono.Collections.Generic. case Code.Ble_S: case Code.Ble_Un: case Code.Ble_Un_S: { - var x1 = GetConstantValue (ins.Previous?.Previous); - var x2 = GetConstantValue (ins.Previous); + var x1 = GetConstantValue (data, ins.Previous?.Previous); + var x2 = GetConstantValue (data, ins.Previous); if (x1.HasValue && x2.HasValue) branch = x1.Value <= x2.Value; cond_instruction_count = 3; @@ -256,8 +261,8 @@ static bool MarkInstructions (MethodDefinition method, Mono.Collections.Generic. case Code.Blt_S: case Code.Blt_Un: case Code.Blt_Un_S: { - var x1 = GetConstantValue (ins.Previous?.Previous); - var x2 = GetConstantValue (ins.Previous); + var x1 = GetConstantValue (data, ins.Previous?.Previous); + var x2 = GetConstantValue (data, ins.Previous); if (x1.HasValue && x2.HasValue) branch = x1.Value < x2.Value; cond_instruction_count = 3; @@ -267,8 +272,8 @@ static bool MarkInstructions (MethodDefinition method, Mono.Collections.Generic. case Code.Bge_S: case Code.Bge_Un: case Code.Bge_Un_S: { - var x1 = GetConstantValue (ins.Previous?.Previous); - var x2 = GetConstantValue (ins.Previous); + var x1 = GetConstantValue (data, ins.Previous?.Previous); + var x2 = GetConstantValue (data, ins.Previous); if (x1.HasValue && x2.HasValue) branch = x1.Value >= x2.Value; cond_instruction_count = 3; @@ -278,15 +283,15 @@ static bool MarkInstructions (MethodDefinition method, Mono.Collections.Generic. case Code.Bgt_S: case Code.Bgt_Un: case Code.Bgt_Un_S: { - var x1 = GetConstantValue (ins.Previous?.Previous); - var x2 = GetConstantValue (ins.Previous); + var x1 = GetConstantValue (data, ins.Previous?.Previous); + var x2 = GetConstantValue (data, ins.Previous); if (x1.HasValue && x2.HasValue) branch = x1.Value > x2.Value; cond_instruction_count = 3; break; } default: - Driver.Log ("Can't optimize {0} because of unknown branch instruction: {1}", method, ins); + data.App.Log ("Can't optimize {0} because of unknown branch instruction: {1}", method, ins); break; } @@ -294,13 +299,13 @@ static bool MarkInstructions (MethodDefinition method, Mono.Collections.Generic. // Make sure nothing else in the method branches into the middle of our supposedly constant condition, // bypassing our constant calculation. Note that it's not a bad to branch to the _first_ instruction in // the sequence (thus the +2 here), just into the middle of it. - if (AnyBranchTo (instructions, instructions [i - cond_instruction_count + 2], ins)) + if (AnyBranchTo (data, instructions, instructions [i - cond_instruction_count + 2], ins)) branch = null; } if (!branch.HasValue) { // not constant, continue marking both this code sequence and the branched sequence - if (!MarkInstructions (method, instructions, reachable, instructions.IndexOf (cond_target), end)) + if (!MarkInstructions (data, method, instructions, reachable, instructions.IndexOf (cond_target), end)) return false; } else { // we can remove the branch (and the code that loads the condition), so we mark those instructions as dead. @@ -310,7 +315,7 @@ static bool MarkInstructions (MethodDefinition method, Mono.Collections.Generic. // Now continue marking according to whether we branched or not if (branch.Value) { // branch always taken - return MarkInstructions (method, instructions, reachable, instructions.IndexOf (cond_target), end); + return MarkInstructions (data, method, instructions, reachable, instructions.IndexOf (cond_target), end); } else { // branch never taken // continue looping @@ -329,7 +334,7 @@ static bool MarkInstructions (MethodDefinition method, Mono.Collections.Generic. case FlowControl.Meta: case FlowControl.Phi: default: - Driver.Log (4, "Can't optimize {0} because of unknown flow control for: {1}", method, ins); + data.App.Log (4, "Can't optimize {0} because of unknown flow control for: {1}", method, ins); return false; } } @@ -338,10 +343,10 @@ static bool MarkInstructions (MethodDefinition method, Mono.Collections.Generic. } // Check if there are any branches in the instructions that branch to anywhere between 'first' and 'last' instructions (both inclusive). - static bool AnyBranchTo (Mono.Collections.Generic.Collection instructions, Instruction first, Instruction last) + static bool AnyBranchTo (OptimizeGeneratedCodeData data, Mono.Collections.Generic.Collection instructions, Instruction first, Instruction last) { if (first.Offset > last.Offset) { - Driver.Log ($"Broken assumption: {first} is after {last}"); + data.App.Log ($"Broken assumption: {first} is after {last}"); return true; // This is the safe thing to do, since it will prevent inlining } @@ -373,7 +378,7 @@ static bool EliminateDeadCode (OptimizeGeneratedCodeData data, MethodDefinition // marking all reachable instructions. Any non-reachable instructions at the end // can be removed. - if (!MarkInstructions (caller, instructions, reachable, 0, instructions.Count)) + if (!MarkInstructions (data, caller, instructions, reachable, 0, instructions.Count)) return modified; // Handle exception handlers specially, they do not follow normal code flow. @@ -407,19 +412,19 @@ static bool EliminateDeadCode (OptimizeGeneratedCodeData data, MethodDefinition } } if (!allNops) { - if (!MarkInstructions (caller, instructions, reachable, instructions.IndexOf (eh.HandlerStart), instructions.IndexOf (eh.HandlerEnd))) + if (!MarkInstructions (data, caller, instructions, reachable, instructions.IndexOf (eh.HandlerStart), instructions.IndexOf (eh.HandlerEnd))) return modified; } break; case ExceptionHandlerType.Finally: // finally clauses are always executed, even if the protected region is empty - if (!MarkInstructions (caller, instructions, reachable, instructions.IndexOf (eh.HandlerStart), instructions.IndexOf (eh.HandlerEnd))) + if (!MarkInstructions (data, caller, instructions, reachable, instructions.IndexOf (eh.HandlerStart), instructions.IndexOf (eh.HandlerEnd))) return modified; break; case ExceptionHandlerType.Fault: case ExceptionHandlerType.Filter: // FIXME: and until fixed, exit gracefully without doing anything - Driver.Log (4, "Unhandled exception handler: {0}, skipping dead code elimination for {1}", eh.HandlerType, caller); + data.App.Log (4, "Unhandled exception handler: {0}, skipping dead code elimination for {1}", eh.HandlerType, caller); return modified; } } @@ -475,7 +480,7 @@ static bool EliminateDeadCode (OptimizeGeneratedCodeData data, MethodDefinition case FlowControl.Cond_Branch: var target = (Instruction) ins.Operand; if (target.Offset > last_reachable_offset) { - Driver.Log (4, "Can't optimize {0} because of branching beyond last instruction alive: {1}", caller, ins); + data.App.Log (4, "Can't optimize {0} because of branching beyond last instruction alive: {1}", caller, ins); return modified; } break; @@ -483,13 +488,13 @@ static bool EliminateDeadCode (OptimizeGeneratedCodeData data, MethodDefinition } } #if false - Console.WriteLine ($"{caller.FullName}:"); + data.App.Log ($"{caller.FullName}:"); for (int i = 0; i < reachable.Length; i++) { - Console.WriteLine ($"{(reachable [i] ? " " : "- ")} {instructions [i]}"); + data.App.Log ($"{(reachable [i] ? " " : "- ")} {instructions [i]}"); if (!reachable [i]) Nop (instructions [i]); } - Console.WriteLine (); + data.App.Log (""); #endif // Exterminate, exterminate, exterminate @@ -648,7 +653,7 @@ static bool ProcessEnsureUIThread (OptimizeGeneratedCodeData data, MethodDefinit // Verify a few assumptions before doing anything const string operation = "remove calls to [NS|UI]Application::EnsureUIThread"; - if (!ValidateInstruction (caller, ins, operation, Code.Call)) + if (!ValidateInstruction (data, caller, ins, operation, Code.Call)) return false; // This is simple: just remove the call @@ -680,10 +685,10 @@ static bool ProcessIsDirectBinding (OptimizeGeneratedCodeData data, MethodDefini return false; // Verify a few assumptions before doing anything - if (!ValidateInstruction (caller, ins.Previous, operation, Code.Ldarg_0)) + if (!ValidateInstruction (data, caller, ins.Previous, operation, Code.Ldarg_0)) return false; - if (!ValidateInstruction (caller, ins, operation, Code.Call)) + if (!ValidateInstruction (data, caller, ins, operation, Code.Call)) return false; // Clearing the branch succeeded, so clear the condition too @@ -742,10 +747,10 @@ static bool ProcessSetupBlock (OptimizeGeneratedCodeData data, MethodDefinition prev = prev.Previous; // Skip any nops. if (prev.OpCode.StackBehaviourPush != StackBehaviour.Push1) { //todo: localize mmp error 2106 - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106, caller, ins.Offset, mr.Name, prev)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106, caller, ins.Offset, mr.Name, prev)); return false; } else if (prev.OpCode.StackBehaviourPop != StackBehaviour.Pop0) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106, caller, ins.Offset, mr.Name, prev)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106, caller, ins.Offset, mr.Name, prev)); return false; } @@ -756,22 +761,22 @@ static bool ProcessSetupBlock (OptimizeGeneratedCodeData data, MethodDefinition // Then find the type of the previous instruction (the first argument to SetupBlock[Unsafe]) var trampolineDelegateType = GetPushedType (caller, loadTrampolineInstruction); if (trampolineDelegateType is null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106_A, caller, ins.Offset, mr.Name, loadTrampolineInstruction)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106_A, caller, ins.Offset, mr.Name, loadTrampolineInstruction)); return false; } if (trampolineDelegateType.Is ("System", "Delegate") || trampolineDelegateType.Is ("System", "MulticastDelegate")) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106_B, caller, trampolineDelegateType.FullName, mr.Name)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106_B, caller, trampolineDelegateType.FullName, mr.Name)); return false; } if (!data.LinkContext.App.StaticRegistrar.TryComputeBlockSignature (caller, trampolineDelegateType, out var exception, out signature)) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, exception, caller, ins, Errors.MM2106_D, caller, ins.Offset, exception.Message)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, exception, caller, ins, Errors.MM2106_D, caller, ins.Offset, exception.Message)); return false; } } catch (Exception e) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, e, caller, ins, Errors.MM2106_D, caller, ins.Offset, e.Message)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, e, caller, ins, Errors.MM2106_D, caller, ins.Offset, e.Message)); return false; } @@ -854,34 +859,34 @@ static bool ProcessBlockLiteralConstructor (OptimizeGeneratedCodeData data, Meth // Verify 'ldstr ...' var loadString = GetPreviousSkippingNops (ins); if (loadString.OpCode != OpCodes.Ldstr) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, loadString)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, loadString)); return false; } // Verify 'call System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)' var callGetTypeFromHandle = GetPreviousSkippingNops (loadString); if (callGetTypeFromHandle.OpCode != OpCodes.Call || !(callGetTypeFromHandle.Operand is MethodReference methodOperand) || methodOperand.Name != "GetTypeFromHandle" || !methodOperand.DeclaringType.Is ("System", "Type")) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, callGetTypeFromHandle)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, callGetTypeFromHandle)); return false; } // Verify 'ldtoken ...' var loadType = GetPreviousSkippingNops (callGetTypeFromHandle); if (loadType.OpCode != OpCodes.Ldtoken) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, loadType)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, loadType)); return false; } // Then find the type of the previous instruction var trampolineContainerTypeReference = loadType.Operand as TypeReference; if (trampolineContainerTypeReference is null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, loadType.Operand)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, loadType.Operand)); return false; } var trampolineContainerType = trampolineContainerTypeReference.Resolve (); if (trampolineContainerType is null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, trampolineContainerTypeReference)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, trampolineContainerTypeReference)); return false; } @@ -889,15 +894,15 @@ static bool ProcessBlockLiteralConstructor (OptimizeGeneratedCodeData data, Meth var trampolineMethodName = (string) loadString.Operand; var trampolineMethods = trampolineContainerType.Methods.Where (v => v.Name == trampolineMethodName).ToArray (); if (!trampolineMethods.Any ()) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_E1 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because no method named '{3}' was found in the type '{4}'. */, caller, ins.Offset, mr.Name, trampolineMethodName, trampolineContainerType.FullName)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_E1 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because no method named '{3}' was found in the type '{4}'. */, caller, ins.Offset, mr.Name, trampolineMethodName, trampolineContainerType.FullName)); return false; } else if (trampolineMethods.Count () > 1) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_E2 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because more than one method named '{3}' was found in the type '{4}'. */, caller, ins.Offset, mr.Name, trampolineMethodName, trampolineContainerType.FullName)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_E2 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because more than one method named '{3}' was found in the type '{4}'. */, caller, ins.Offset, mr.Name, trampolineMethodName, trampolineContainerType.FullName)); return false; } var trampolineMethod = trampolineMethods [0]; if (!trampolineMethod.HasParameters || trampolineMethod.Parameters.Count < 1) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_F /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the method '{3}' must have at least one parameter. */, caller, ins.Offset, mr.Name, trampolineContainerType.FullName + "::" + trampolineMethodName)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_F /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the method '{3}' must have at least one parameter. */, caller, ins.Offset, mr.Name, trampolineContainerType.FullName + "::" + trampolineMethodName)); return false; } @@ -908,18 +913,18 @@ static bool ProcessBlockLiteralConstructor (OptimizeGeneratedCodeData data, Meth } else if (firstParameterType is PointerType ptrType) { var ptrTargetType = ptrType.ElementType; if (!(ptrTargetType.Is ("System", "Void") || ptrTargetType.Is ("ObjCRuntime", "BlockLiteral"))) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_G /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the first parameter in the method '{3}' isn't 'System.IntPtr', 'void*' or 'ObjCRuntime.BlockLiteral*' (it's '{4}') */, caller, ins.Offset, mr.Name, trampolineContainerType.FullName + "::" + trampolineMethodName, firstParameterType.FullName)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_G /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the first parameter in the method '{3}' isn't 'System.IntPtr', 'void*' or 'ObjCRuntime.BlockLiteral*' (it's '{4}') */, caller, ins.Offset, mr.Name, trampolineContainerType.FullName + "::" + trampolineMethodName, firstParameterType.FullName)); return false; } // ok } else { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_G /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the first parameter in the method '{3}' isn't 'System.IntPtr', 'void*' or 'ObjCRuntime.BlockLiteral*' (it's '{4}') */, caller, ins.Offset, mr.Name, trampolineContainerType.FullName + "::" + trampolineMethodName, firstParameterType.FullName)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_G /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the first parameter in the method '{3}' isn't 'System.IntPtr', 'void*' or 'ObjCRuntime.BlockLiteral*' (it's '{4}') */, caller, ins.Offset, mr.Name, trampolineContainerType.FullName + "::" + trampolineMethodName, firstParameterType.FullName)); return false; } // Check that the method has [UnmanagedCallersOnly] if (!trampolineMethod.HasCustomAttributes || !trampolineMethod.CustomAttributes.Any (v => v.AttributeType.Is ("System.Runtime.InteropServices", "UnmanagedCallersOnlyAttribute"))) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_H /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the method '{3}' does not have an [UnmanagedCallersOnly] attribute. */, caller, ins.Offset, mr.Name, trampolineContainerType.FullName + "::" + trampolineMethodName)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_H /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the method '{3}' does not have an [UnmanagedCallersOnly] attribute. */, caller, ins.Offset, mr.Name, trampolineContainerType.FullName + "::" + trampolineMethodName)); return false; } @@ -933,7 +938,7 @@ static bool ProcessBlockLiteralConstructor (OptimizeGeneratedCodeData data, Meth } if (userMethod is null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106_D, caller, ins.Offset, "Could not find delegate invoke method")); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106_D, caller, ins.Offset, "Could not find delegate invoke method")); return false; } @@ -945,7 +950,7 @@ static bool ProcessBlockLiteralConstructor (OptimizeGeneratedCodeData data, Meth sequenceStart = loadType; } catch (Exception e) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, e, caller, ins, Errors.MM2106_D, caller, ins.Offset, e.Message)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2106, e, caller, ins, Errors.MM2106_D, caller, ins.Offset, e.Message)); return false; } @@ -963,7 +968,7 @@ static bool ProcessBlockLiteralConstructor (OptimizeGeneratedCodeData data, Meth // Change the call to call the ctor with the string signature parameter instead ins.Operand = GetBlockLiteralConstructor (data, caller, ins); - Driver.Log (4, "Optimized call to BlockLiteral..ctor in {0} at offset {1} with signature {2}", caller, ins.Offset, signature); + data.App.Log (4, "Optimized call to BlockLiteral..ctor in {0} at offset {1} with signature {2}", caller, ins.Offset, signature); instructionsAddedOrRemoved = instructionDiff; return true; } @@ -1004,7 +1009,7 @@ static bool ProcessIsARM64CallingConvention (OptimizeGeneratedCodeData data, Met if (fr is null || !fr.DeclaringType.Is (Namespaces.ObjCRuntime, "Runtime")) return false; - if (!ValidateInstruction (caller, ins, operation, Code.Ldsfld)) + if (!ValidateInstruction (data, caller, ins, operation, Code.Ldsfld)) return false; // We're fine, inline the Runtime.IsARM64CallingConvention value @@ -1027,7 +1032,7 @@ static bool ProcessRuntimeArch (OptimizeGeneratedCodeData data, MethodDefinition return false; // Verify a few assumptions before doing anything - if (!ValidateInstruction (caller, ins, operation, Code.Ldsfld)) + if (!ValidateInstruction (data, caller, ins, operation, Code.Ldsfld)) return false; // We're fine, inline the Runtime.Arch condition @@ -1153,52 +1158,52 @@ static bool ProcessProtocolInterfaceStaticConstructor (OptimizeGeneratedCodeData return false; if (data.Optimizations.RegisterProtocols != true) { - Driver.Log (4, "Did not optimize static constructor in the protocol interface {0}: the 'register-protocols' optimization is disabled.", method.DeclaringType.FullName); + data.App.Log (4, "Did not optimize static constructor in the protocol interface {0}: the 'register-protocols' optimization is disabled.", method.DeclaringType.FullName); return false; } if (!method.DeclaringType.HasCustomAttributes || !method.DeclaringType.CustomAttributes.Any (v => v.AttributeType.Is ("Foundation", "ProtocolAttribute"))) { - Driver.Log (4, "Did not optimize static constructor in the protocol interface {0}: no Protocol attribute found.", method.DeclaringType.FullName); + data.App.Log (4, "Did not optimize static constructor in the protocol interface {0}: no Protocol attribute found.", method.DeclaringType.FullName); return false; } var ins = SkipNops (method.Body.Instructions.First ()); if (ins is null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_A /* Could not optimize the static constructor in the interface {0} because it did not have the expected instruction sequence (found end of method too soon). */, method.DeclaringType.FullName)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_A /* Could not optimize the static constructor in the interface {0} because it did not have the expected instruction sequence (found end of method too soon). */, method.DeclaringType.FullName)); return false; } else if (ins.OpCode != OpCodes.Ldnull) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_B /* Could not optimize the static constructor in the interface {0} because it had an unexpected instruction {1} at offset {2}. */, method.DeclaringType.FullName, ins.OpCode, ins.Offset)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_B /* Could not optimize the static constructor in the interface {0} because it had an unexpected instruction {1} at offset {2}. */, method.DeclaringType.FullName, ins.OpCode, ins.Offset)); return false; } ins = SkipNops (ins.Next); if (ins is null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_A /* Could not optimize the static constructor in the interface {0} because it did not have the expected instruction sequence (found end of method too soon). */, method.DeclaringType.FullName)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_A /* Could not optimize the static constructor in the interface {0} because it did not have the expected instruction sequence (found end of method too soon). */, method.DeclaringType.FullName)); return false; } var callGCKeepAlive = ins; if (callGCKeepAlive.OpCode != OpCodes.Call || !(callGCKeepAlive.Operand is MethodReference methodOperand) || methodOperand.Name != "KeepAlive" || !methodOperand.DeclaringType.Is ("System", "GC")) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_B /* Could not optimize the static constructor in the interface {0} because it had an unexpected instruction {1} at offset {2}. */, method.DeclaringType.FullName, ins.OpCode, ins.Offset)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_B /* Could not optimize the static constructor in the interface {0} because it had an unexpected instruction {1} at offset {2}. */, method.DeclaringType.FullName, ins.OpCode, ins.Offset)); return false; } ins = SkipNops (ins.Next); if (ins is null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_A /* Could not optimize the static constructor in the interface {0} because it did not have the expected instruction sequence (found end of method too soon). */, method.DeclaringType.FullName)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_A /* Could not optimize the static constructor in the interface {0} because it did not have the expected instruction sequence (found end of method too soon). */, method.DeclaringType.FullName)); return false; } else if (ins.OpCode != OpCodes.Ret) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_B /* Could not optimize the static constructor in the interface {0} because it had an unexpected instruction {1} at offset {2}. */, method.DeclaringType.FullName, ins.OpCode, ins.Offset)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_B /* Could not optimize the static constructor in the interface {0} because it had an unexpected instruction {1} at offset {2}. */, method.DeclaringType.FullName, ins.OpCode, ins.Offset)); return false; } ins = SkipNops (ins.Next); if (ins is not null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_B /* Could not optimize the static constructor in the interface {0} because it had an unexpected instruction {1} at offset {2}. */, method.DeclaringType.FullName, ins.OpCode, ins.Offset)); + ErrorHelper.Show (data.App, ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_B /* Could not optimize the static constructor in the interface {0} because it had an unexpected instruction {1} at offset {2}. */, method.DeclaringType.FullName, ins.OpCode, ins.Offset)); return false; } // We can just remove the entire method, however that confuses the linker later on, so just empty it out and remove all the attributes. - Driver.Log (4, "Optimized static constructor in the protocol interface {0} (static constructor was cleared and custom attributes removed)", method.DeclaringType.FullName); + data.App.Log (4, "Optimized static constructor in the protocol interface {0} (static constructor was cleared and custom attributes removed)", method.DeclaringType.FullName); method.Body.Instructions.Clear (); method.Body.Instructions.Add (Instruction.Create (OpCodes.Ret)); @@ -1234,6 +1239,8 @@ public class OptimizeGeneratedCodeData { public MethodDefinition? SetupBlockImplDefinition; public MethodDefinition? BlockCtorDefinition; public bool? InlineIsArm64CallingConvention; + + public Application App => LinkContext.App; } } diff --git a/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs b/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs index b74d18cddca7..bb2193ab35ad 100644 --- a/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs +++ b/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs @@ -201,18 +201,18 @@ bool ProcessMethod (MethodDefinition method) if (Configuration.InlineClassGetHandle != InlineClassGetHandleMode.Disabled && pinfo.EntryPoint.StartsWith (InlineClassGetHandleStep.PInvokePrefix, StringComparison.Ordinal)) break; } - Driver.Log (4, "Adding native reference to {0} in {1} because it's referenced by {2} in {3}.", pinfo.EntryPoint, pinfo.Module.Name, method.FullName, method.Module.Name); + DerivedLinkContext.App.Log (4, "Adding native reference to {0} in {1} because it's referenced by {2} in {3}.", pinfo.EntryPoint, pinfo.Module.Name, method.FullName, method.Module.Name); DerivedLinkContext.RequiredSymbols.AddFunction (pinfo.EntryPoint).AddMember (method); break; default: if (!addPInvokeSymbol) - Driver.Log (4, "Did not add native reference to {0} in {1} referenced by {2} in {3}.", pinfo.EntryPoint, pinfo.Module.Name, method.FullName, method.Module.Name); + DerivedLinkContext.App.Log (4, "Did not add native reference to {0} in {1} referenced by {2} in {3}.", pinfo.EntryPoint, pinfo.Module.Name, method.FullName, method.Module.Name); break; } if (addPInvokeSymbol) { - Driver.Log (4, "Adding native reference to {0} in {1} because it's referenced by {2} in {3}.", pinfo.EntryPoint, pinfo.Module.Name, method.FullName, method.Module.Name); + DerivedLinkContext.App.Log (4, "Adding native reference to {0} in {1} because it's referenced by {2} in {3}.", pinfo.EntryPoint, pinfo.Module.Name, method.FullName, method.Module.Name); DerivedLinkContext.RequireMonoNative = true; if (DerivedLinkContext.App.Platform != ApplePlatform.MacOSX && DerivedLinkContext.App.LibMonoNativeLinkMode == AssemblyBuildTarget.StaticObject) { diff --git a/tools/linker/RegistrarRemovalTrackingStep.cs b/tools/linker/RegistrarRemovalTrackingStep.cs index f88452f3ccd0..b2cd10bc170c 100644 --- a/tools/linker/RegistrarRemovalTrackingStep.cs +++ b/tools/linker/RegistrarRemovalTrackingStep.cs @@ -153,7 +153,7 @@ bool RequiresDynamicRegistrar (AssemblyDefinition assembly, bool warnIfRequired) void Warn (AssemblyDefinition assembly, MemberReference mr) { - ErrorHelper.Warning (WarnCode, Errors.MM2107, assembly.Name.Name, mr.DeclaringType.FullName, mr.Name, string.Join (", ", ((MethodReference) mr).Parameters.Select ((v) => v.ParameterType.FullName))); + ErrorHelper.Warning (App, WarnCode, Errors.MM2107, assembly.Name.Name, mr.DeclaringType.FullName, mr.Name, string.Join (", ", ((MethodReference) mr).Parameters.Select ((v) => v.ParameterType.FullName))); } protected override void TryEndProcess () @@ -164,7 +164,7 @@ protected override void TryEndProcess () Optimizations.RemoveDynamicRegistrar = !dynamic_registration_support_required; } - Driver.Log (4, "Optimization dynamic registrar removal: {0}", Optimizations.RemoveDynamicRegistrar.Value ? "enabled" : "disabled"); + App.Log (4, "Optimization dynamic registrar removal: {0}", Optimizations.RemoveDynamicRegistrar.Value ? "enabled" : "disabled"); if (Optimizations.RemoveDynamicRegistrar.Value) { // ILLink will optimize `Runtime.Initialize` based on `DynamicRegistrationSupported` returning a constant (`true`) diff --git a/tools/mtouch/AssemblyResolver.cs b/tools/mtouch/AssemblyResolver.cs index f63d6ebebbe7..e5386afd5df5 100644 --- a/tools/mtouch/AssemblyResolver.cs +++ b/tools/mtouch/AssemblyResolver.cs @@ -22,10 +22,6 @@ #nullable enable namespace MonoTouch.Tuner { - - public partial class MonoTouchManifestResolver : MonoTouchResolver { - } - // recent cecil removed some overloads - https://github.com/mono/cecil/commit/42db79cc16f1cbe8dbab558904e188352dba2b41 public static class AssemblyResolverRocks { @@ -41,6 +37,12 @@ public static AssemblyDefinition Resolve (this IAssemblyResolver self, string fu } public class MonoTouchResolver : CoreResolver { + Application app; + + public MonoTouchResolver (Application app) + { + this.app = app; + } public IEnumerable GetAssemblies () { @@ -63,29 +65,29 @@ public void Add (AssemblyDefinition assembly) if (FrameworkDirectory is not null) { var facadeDir = Path.Combine (FrameworkDirectory, "Facades"); - assembly = SearchDirectory (aname, facadeDir); + assembly = SearchDirectory (app, aname, facadeDir); if (assembly is not null) return assembly; } if (ArchDirectory is not null) { - assembly = SearchDirectory (aname, ArchDirectory); + assembly = SearchDirectory (app, aname, ArchDirectory); if (assembly is not null) return assembly; } if (FrameworkDirectory is not null) { - assembly = SearchDirectory (aname, FrameworkDirectory); + assembly = SearchDirectory (app, aname, FrameworkDirectory); if (assembly is not null) return assembly; } if (RootDirectory is not null) { - assembly = SearchDirectory (aname, RootDirectory); + assembly = SearchDirectory (app, aname, RootDirectory); if (assembly is not null) return assembly; - assembly = SearchDirectory (aname, RootDirectory, ".exe"); + assembly = SearchDirectory (app, aname, RootDirectory, ".exe"); if (assembly is not null) return assembly; } diff --git a/tools/mtouch/mtouch.csproj b/tools/mtouch/mtouch.csproj index a11f77fcc353..d9ba0a1ea76e 100644 --- a/tools/mtouch/mtouch.csproj +++ b/tools/mtouch/mtouch.csproj @@ -171,6 +171,9 @@ external/tools/common/XamarinRuntime.cs + + external/tools/common/IToolLog.cs + From d1a9361cd94e6d2b3aab38b18c2c451c9d171bf9 Mon Sep 17 00:00:00 2001 From: Alex Soto Date: Tue, 2 Jun 2026 12:21:12 -0400 Subject: [PATCH 55/79] [devops] yaml-templates go back to use main (#25600) --- tools/devops/automation/build-pipeline.yml | 2 +- tools/devops/automation/build-pull-request.yml | 2 +- tools/devops/automation/publish-pr-html-results.yml | 2 +- tools/devops/automation/run-nightly-codeql.yml | 2 +- .../devops/automation/templates/pipelines/api-diff-pipeline.yml | 2 +- tools/devops/automation/templates/pipelines/build-pipeline.yml | 2 +- tools/devops/automation/templates/pipelines/run-api-scan.yml | 2 +- .../automation/templates/pipelines/run-tests-pipeline.yml | 2 +- tools/devops/automation/vs-insertion.yml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/devops/automation/build-pipeline.yml b/tools/devops/automation/build-pipeline.yml index d3e3f8b93895..97824c8abe3b 100644 --- a/tools/devops/automation/build-pipeline.yml +++ b/tools/devops/automation/build-pipeline.yml @@ -45,7 +45,7 @@ resources: - repository: yaml-templates type: git name: xamarin.yaml-templates - ref: e30b445c2ffcbebe75efdd69546d7196a20c5a43 + ref: refs/heads/main - repository: CustomPipelineTemplates type: git diff --git a/tools/devops/automation/build-pull-request.yml b/tools/devops/automation/build-pull-request.yml index b6e7f76620cf..8d71f0ad7231 100644 --- a/tools/devops/automation/build-pull-request.yml +++ b/tools/devops/automation/build-pull-request.yml @@ -36,7 +36,7 @@ resources: - repository: yaml-templates type: git name: xamarin.yaml-templates - ref: e30b445c2ffcbebe75efdd69546d7196a20c5a43 + ref: refs/heads/main - repository: CustomPipelineTemplates type: git diff --git a/tools/devops/automation/publish-pr-html-results.yml b/tools/devops/automation/publish-pr-html-results.yml index c1b7f08c1b2b..254babc35993 100644 --- a/tools/devops/automation/publish-pr-html-results.yml +++ b/tools/devops/automation/publish-pr-html-results.yml @@ -14,7 +14,7 @@ resources: - repository: yaml-templates type: git name: xamarin.yaml-templates - ref: e30b445c2ffcbebe75efdd69546d7196a20c5a43 + ref: refs/heads/main # we need all stages to be completed, else we do not have the test results, this trigger is just for CI, because we have # but because we have device issues, and it needs to be gree to trigger, we will deal with it later diff --git a/tools/devops/automation/run-nightly-codeql.yml b/tools/devops/automation/run-nightly-codeql.yml index 1e4144a6028e..f5375ab46ea2 100644 --- a/tools/devops/automation/run-nightly-codeql.yml +++ b/tools/devops/automation/run-nightly-codeql.yml @@ -21,7 +21,7 @@ resources: - repository: yaml-templates type: git name: xamarin.yaml-templates - ref: e30b445c2ffcbebe75efdd69546d7196a20c5a43 + ref: refs/heads/main variables: - template: /tools/devops/automation/templates/variables/common.yml diff --git a/tools/devops/automation/templates/pipelines/api-diff-pipeline.yml b/tools/devops/automation/templates/pipelines/api-diff-pipeline.yml index 5b81d42282de..26bb8c290581 100644 --- a/tools/devops/automation/templates/pipelines/api-diff-pipeline.yml +++ b/tools/devops/automation/templates/pipelines/api-diff-pipeline.yml @@ -31,7 +31,7 @@ resources: - repository: yaml-templates type: git name: xamarin.yaml-templates - ref: e30b445c2ffcbebe75efdd69546d7196a20c5a43 + ref: refs/heads/main variables: - template: ../variables/common.yml diff --git a/tools/devops/automation/templates/pipelines/build-pipeline.yml b/tools/devops/automation/templates/pipelines/build-pipeline.yml index 0d7af904d237..ebd3ee2f4ae7 100644 --- a/tools/devops/automation/templates/pipelines/build-pipeline.yml +++ b/tools/devops/automation/templates/pipelines/build-pipeline.yml @@ -59,7 +59,7 @@ resources: - repository: yaml-templates type: git name: xamarin.yaml-templates - ref: e30b445c2ffcbebe75efdd69546d7196a20c5a43 + ref: refs/heads/main variables: - ${{ if eq(parameters.isPR, false) }}: diff --git a/tools/devops/automation/templates/pipelines/run-api-scan.yml b/tools/devops/automation/templates/pipelines/run-api-scan.yml index 116c6fcbeb61..44fd63925e64 100644 --- a/tools/devops/automation/templates/pipelines/run-api-scan.yml +++ b/tools/devops/automation/templates/pipelines/run-api-scan.yml @@ -25,7 +25,7 @@ resources: - repository: yaml-templates type: git name: xamarin.yaml-templates - ref: e30b445c2ffcbebe75efdd69546d7196a20c5a43 + ref: refs/heads/main - repository: release-scripts type: github diff --git a/tools/devops/automation/templates/pipelines/run-tests-pipeline.yml b/tools/devops/automation/templates/pipelines/run-tests-pipeline.yml index a3ef20748f63..47e83ca8c65f 100644 --- a/tools/devops/automation/templates/pipelines/run-tests-pipeline.yml +++ b/tools/devops/automation/templates/pipelines/run-tests-pipeline.yml @@ -45,7 +45,7 @@ resources: - repository: yaml-templates type: git name: xamarin.yaml-templates - ref: e30b445c2ffcbebe75efdd69546d7196a20c5a43 + ref: refs/heads/main variables: - template: ../variables/common.yml diff --git a/tools/devops/automation/vs-insertion.yml b/tools/devops/automation/vs-insertion.yml index 90eef300b8a1..f98208720aed 100644 --- a/tools/devops/automation/vs-insertion.yml +++ b/tools/devops/automation/vs-insertion.yml @@ -12,7 +12,7 @@ resources: - repository: yaml-templates type: git name: xamarin.yaml-templates - ref: e30b445c2ffcbebe75efdd69546d7196a20c5a43 + ref: refs/heads/main # we need all stages to be completed, else we do not have the test results, this trigger is just for CI, because we have # but because we have device issues, and it needs to be gree to trigger, we will deal with it later From c3564fff2e05dc3403037e2fe158d76cf309a8e4 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 2 Jun 2026 19:06:45 +0200 Subject: [PATCH 56/79] [tests] Update NUnit packages to latest stable versions (#25516) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the following package versions: - NUnit: 4.4.0 → 4.6.1 - NUnit3TestAdapter: 6.1.0 → 6.2.0 - NUnitAnalyzers: 4.7.0 → 4.13.0 - NUnitXmlTestLogger: 3.1.15 → 8.0.0 - NUnitLite: 3.12.0 → 4.6.1 - NUnitV2ResultWriter: 3.6.0 → 3.8.0 Also remove the tools/nunit3-console* scripts, they're no longer needed. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Directory.Build.props | 12 ++++++------ tests/common/shared-dotnet.csproj | 14 ++++++++++++++ tests/dotnet/UnitTests/BundleStructureTest.cs | 1 + tests/monotouch-test/AppKit/NSPasteboard.cs | 6 +++--- tests/monotouch-test/AppKit/NSView.cs | 5 +++++ tests/monotouch-test/Foundation/ObjectTest.cs | 1 - .../ObjCRuntime/DelegateAndDataSourceTest.cs | 2 ++ tests/monotouch-test/ObjCRuntime/RegistrarTest.cs | 2 +- .../Security/SecSharedCredentialTest.cs | 8 -------- .../VTFrameRateConversionConfigurationTest.cs | 6 +++--- .../VideoToolbox/VTMotionBlurConfigurationTest.cs | 8 +++++--- .../VideoToolbox/VTOpticalFlowConfigurationTest.cs | 8 +++++--- tests/nunit.framework.targets | 2 +- tools/nunit3-console-3.10.0 | 10 ---------- tools/nunit3-console-3.11.1 | 10 ---------- tools/nunit3-console-3.12.0 | 10 ---------- tools/nunit3-console-3.9.0 | 10 ---------- 17 files changed, 46 insertions(+), 69 deletions(-) delete mode 100755 tools/nunit3-console-3.10.0 delete mode 100755 tools/nunit3-console-3.11.1 delete mode 100755 tools/nunit3-console-3.12.0 delete mode 100755 tools/nunit3-console-3.9.0 diff --git a/Directory.Build.props b/Directory.Build.props index f054d696a5d3..ac90e9e40ed2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -8,7 +8,7 @@ $(MicrosoftBuildPackageVersion) $(MicrosoftBuildPackageVersion) 3.22.0 - 3.1.15 + 8.0.0 4.7.2 @@ -19,11 +19,11 @@ 21.1.8 18.0.1 0.11.6 - 6.1.0 - 4.7.0 - 4.4.0 - 3.12.0 - 3.6.0 + 6.2.0 + 4.13.0 + 4.6.1 + $(NUnitPackageVersion) + 3.8.0 latest diff --git a/tests/common/shared-dotnet.csproj b/tests/common/shared-dotnet.csproj index 8c3e83197315..27ee7da59ae3 100644 --- a/tests/common/shared-dotnet.csproj +++ b/tests/common/shared-dotnet.csproj @@ -80,6 +80,11 @@ $(DefineConstants);NATIVEAOT false + + true + false @@ -101,8 +106,12 @@ true + + true + + @@ -126,6 +135,11 @@ $(DefineConstants);NATIVEAOT false + + true + false + NU5123;$(NoWarn) From e39f97b0e4a9b419e987b86260d61ba0ac34ce73 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 3 Jun 2026 09:26:55 +0200 Subject: [PATCH 61/79] [src] Mark MKDistanceFormatter as thread-safe. Fixes #25617. (#25620) Fixes https://github.com/dotnet/macios/issues/25617. --- src/mapkit.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mapkit.cs b/src/mapkit.cs index 1b4f59ee6e06..bc577b1f9f5f 100644 --- a/src/mapkit.cs +++ b/src/mapkit.cs @@ -2067,6 +2067,7 @@ partial interface MKRouteStep { [BaseType (typeof (NSFormatter))] [MacCatalyst (13, 1)] + [ThreadSafe] partial interface MKDistanceFormatter { [Export ("stringFromDistance:")] From 621547e5569c9e8e3de28449e5761916f669cfd1 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Jun 2026 07:33:56 +0000 Subject: [PATCH 62/79] [github] Disable signed-commit replay in Code Radiator merge PR pushes (#25577) Code Radiator failed to create the `main -> xcode26.5` merge PR because safe-outputs attempted signed commit replay, then refused unsigned fallback when a submodule update was present. This change switches the merge workflow to explicit unsigned push behavior for PR branch updates. - **Root cause addressed** - Configure safe-outputs PR write paths to avoid `pushSignedCommits` replay for merge branches that can include unsupported commit shapes (notably submodule bumps). - **Workflow source updates (`code-radiator.md`)** - Set `signed-commits: false` under: - `safe-outputs.create-pull-request` - `safe-outputs.push-to-pull-request-branch` - **Compiled workflow parity (`code-radiator.lock.yml`)** - Updated generated safe-outputs handler/config payloads to include: - `"signed_commits": false` for `create_pull_request` - `"signed_commits": false` for `push_to_pull_request_branch` ```yaml safe-outputs: create-pull-request: max: 10 signed-commits: false push-to-pull-request-branch: max: 10 signed-commits: false ``` --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: rolfbjarne <249268+rolfbjarne@users.noreply.github.com> --- .github/workflows/code-radiator.lock.yml | 34 ++++++++++++------------ .github/workflows/code-radiator.md | 2 ++ 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/.github/workflows/code-radiator.lock.yml b/.github/workflows/code-radiator.lock.yml index 39492934fcb3..76736b1d9f98 100644 --- a/.github/workflows/code-radiator.lock.yml +++ b/.github/workflows/code-radiator.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"beeb14d4346fb0901da58ea798ac8ec711e3ed27c3b1143809bcf69e85a5d8f8","body_hash":"b966744ea05e67fd471e10231f2cce01d0525af33bc90ff317655344e889cc92","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.5"} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"1ba69a47e6cfbd91cfe78d637aac47fa364d969c56a89ac3523d5c936169f250","body_hash":"b966744ea05e67fd471e10231f2cce01d0525af33bc90ff317655344e889cc92","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.5"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"3ea13c02d765410340d533515cb31a7eef2baaf0","version":"v0.77.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.58"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.22"},{"image":"ghcr.io/github/github-mcp-server:v1.1.0"},{"image":"node:lts-alpine","digest":"sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14","pinned_image":"node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14"}]} # ___ _ _ # / _ \ | | (_) @@ -192,24 +192,24 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_1f8577744e570dc4_EOF' + cat << 'GH_AW_PROMPT_686c839083e52610_EOF' - GH_AW_PROMPT_1f8577744e570dc4_EOF + GH_AW_PROMPT_686c839083e52610_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_1f8577744e570dc4_EOF' + cat << 'GH_AW_PROMPT_686c839083e52610_EOF' Tools: add_comment(max:10), create_pull_request(max:10), update_pull_request(max:10), add_labels(max:10), push_to_pull_request_branch(max:10), missing_tool, missing_data, noop - GH_AW_PROMPT_1f8577744e570dc4_EOF + GH_AW_PROMPT_686c839083e52610_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_push_to_pr_branch.md" - cat << 'GH_AW_PROMPT_1f8577744e570dc4_EOF' + cat << 'GH_AW_PROMPT_686c839083e52610_EOF' - GH_AW_PROMPT_1f8577744e570dc4_EOF + GH_AW_PROMPT_686c839083e52610_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_1f8577744e570dc4_EOF' + cat << 'GH_AW_PROMPT_686c839083e52610_EOF' The following GitHub context information is available for this workflow: {{#if github.actor}} @@ -241,12 +241,12 @@ jobs: - **Note**: If a branch you need is not in the list above and is not listed as an additional fetched ref, it has NOT been checked out. For private repositories you cannot fetch it without proper authentication. If the branch is required and not available, exit with an error and ask the user to add it to the `fetch:` option of the `checkout:` configuration (e.g., `fetch: ["refs/pulls/open/*"]` for all open PR refs, or `fetch: ["main", "feature/my-branch"]` for specific branches). - GH_AW_PROMPT_1f8577744e570dc4_EOF + GH_AW_PROMPT_686c839083e52610_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_1f8577744e570dc4_EOF' + cat << 'GH_AW_PROMPT_686c839083e52610_EOF' {{#runtime-import .github/workflows/code-radiator.md}} - GH_AW_PROMPT_1f8577744e570dc4_EOF + GH_AW_PROMPT_686c839083e52610_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -461,9 +461,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_752f6925d3d27640_EOF' - {"add_comment":{"max":10,"target":"*"},"add_labels":{"max":10,"target":"*"},"create_pull_request":{"allowed_base_branches":["net*.0","xcode*","xcode*.*"],"max":10,"max_patch_files":1000,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"protected_files_policy":"request_review"},"create_report_incomplete_issue":{},"merge_pull_request":{"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_to_pull_request_branch":{"if_no_changes":"warn","max":10,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"target":"*","title_prefix":"🤖 Merge 'main' =\u003e '"},"report_incomplete":{},"update_pull_request":{"allow_body":true,"allow_title":true,"max":10,"update_branch":false}} - GH_AW_SAFE_OUTPUTS_CONFIG_752f6925d3d27640_EOF + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_9a64ac31bc43ee2c_EOF' + {"add_comment":{"max":10,"target":"*"},"add_labels":{"max":10,"target":"*"},"create_pull_request":{"allowed_base_branches":["net*.0","xcode*","xcode*.*"],"max":10,"max_patch_files":1000,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"protected_files_policy":"request_review","signed_commits":false},"create_report_incomplete_issue":{},"merge_pull_request":{"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_to_pull_request_branch":{"if_no_changes":"warn","max":10,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"signed_commits":false,"target":"*","title_prefix":"🤖 Merge 'main' =\u003e '"},"report_incomplete":{},"update_pull_request":{"allow_body":true,"allow_title":true,"max":10,"update_branch":false}} + GH_AW_SAFE_OUTPUTS_CONFIG_9a64ac31bc43ee2c_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -806,7 +806,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_d132526c2d13876f_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_ecdd849dd0c87e2d_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -850,7 +850,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_d132526c2d13876f_EOF + GH_AW_MCP_CONFIG_ecdd849dd0c87e2d_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1594,7 +1594,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10,\"target\":\"*\"},\"add_labels\":{\"max\":10,\"target\":\"*\"},\"create_pull_request\":{\"allowed_base_branches\":[\"net*.0\",\"xcode*\",\"xcode*.*\"],\"max\":10,\"max_patch_files\":1000,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"protected_files_policy\":\"request_review\"},\"create_report_incomplete_issue\":{},\"merge_pull_request\":{\"max\":10},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max\":10,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"target\":\"*\",\"title_prefix\":\"🤖 Merge 'main' =\\u003e '\"},\"report_incomplete\":{},\"update_pull_request\":{\"allow_body\":true,\"allow_title\":true,\"max\":10,\"update_branch\":false}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10,\"target\":\"*\"},\"add_labels\":{\"max\":10,\"target\":\"*\"},\"create_pull_request\":{\"allowed_base_branches\":[\"net*.0\",\"xcode*\",\"xcode*.*\"],\"max\":10,\"max_patch_files\":1000,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"protected_files_policy\":\"request_review\",\"signed_commits\":false},\"create_report_incomplete_issue\":{},\"merge_pull_request\":{\"max\":10},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max\":10,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"signed_commits\":false,\"target\":\"*\",\"title_prefix\":\"🤖 Merge 'main' =\\u003e '\"},\"report_incomplete\":{},\"update_pull_request\":{\"allow_body\":true,\"allow_title\":true,\"max\":10,\"update_branch\":false}}" GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }} with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/code-radiator.md b/.github/workflows/code-radiator.md index 5d4d18d5a0e7..e1fe86d0d97d 100644 --- a/.github/workflows/code-radiator.md +++ b/.github/workflows/code-radiator.md @@ -28,6 +28,7 @@ safe-outputs: max-patch-files: 1000 create-pull-request: max: 10 + signed-commits: false allowed-base-branches: - "net*.0" - "xcode*" @@ -42,6 +43,7 @@ safe-outputs: max: 10 push-to-pull-request-branch: max: 10 + signed-commits: false target: "*" required-title-prefix: "🤖 Merge 'main' => '" update-pull-request: From bb479716b669b3d2c8869b666aa58d24bd37b9b3 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 3 Jun 2026 10:44:39 +0200 Subject: [PATCH 63/79] [sharpie] Fix a parallel build issue by building before packing. (#25616) --------- Co-authored-by: Rolf Bjarne Kvinge Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tools/sharpie/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/sharpie/Makefile b/tools/sharpie/Makefile index 56e53de56372..a2c79f4e8727 100644 --- a/tools/sharpie/Makefile +++ b/tools/sharpie/Makefile @@ -21,7 +21,7 @@ SHARPIE_BIND_TOOL_NUPKG_NAME=Sharpie.Bind.Tool.$(SHARPIE_BIND_TOOL_NUGET_VERSION SHARPIE_BIND_TOOL_NUPKG=Sharpie.Bind.Tool/bin/Release/$(SHARPIE_BIND_TOOL_NUPKG_NAME) pack: $(SHARPIE_BIND_TOOL_NUPKG) -$(SHARPIE_BIND_TOOL_NUPKG): $(Sharpie.Bind_dependencies) +$(SHARPIE_BIND_TOOL_NUPKG): $(Sharpie.Bind_dependencies) | $(SHARPIE_BIND_TOOL_DEBUG) $(Q_BUILD) $(DOTNET) pack Sharpie.Bind.Tool/Sharpie.Bind.Tool.csproj "/p:Version=$(SHARPIE_VERSION)" "/p:CurrentBranch=$(CURRENT_BRANCH)" "/p:CurrentHash=$(CURRENT_HASH_LONG)" $(DOTNET_PACK_VERBOSITY) -bl:$@.binlog all-local:: $(DOTNET_NUPKG_DIR)/$(SHARPIE_BIND_TOOL_NUPKG_NAME) From ad81f10dde27e116916126b0438e090d1ed4ae17 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 3 Jun 2026 20:47:44 +0200 Subject: [PATCH 64/79] [apidiff] Add nullability support to mono-api-info and mono-api-html (#25603) mono-api-info now reads NullableAttribute and NullableContextAttribute metadata from assemblies and appends '?' to type names for nullable reference types in the XML output (parameters, return types, properties, fields, and events). mono-api-html now: - Strips nullability annotations when matching methods (so nullability- only changes don't cause false removed/added entries) - Detects nullability-only changes and renders them under a separate '(nullability)' subsection header to indicate they are non-breaking - Handles the '?' suffix in type name resolution (GetTypeName) --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tools/api-tools/mono-api-html/ApiChange.cs | 127 +++++++++++++++- .../mono-api-html/ConstructorComparer.cs | 2 +- .../api-tools/mono-api-html/EventComparer.cs | 2 +- .../api-tools/mono-api-html/FieldComparer.cs | 2 +- tools/api-tools/mono-api-html/Formatter.cs | 7 + tools/api-tools/mono-api-html/Helpers.cs | 36 +++++ .../api-tools/mono-api-html/MemberComparer.cs | 23 ++- .../api-tools/mono-api-html/MethodComparer.cs | 2 +- .../mono-api-html/PropertyComparer.cs | 4 +- .../api-tools/mono-api-info/mono-api-info.cs | 142 +++++++++++++++++- 10 files changed, 330 insertions(+), 17 deletions(-) diff --git a/tools/api-tools/mono-api-html/ApiChange.cs b/tools/api-tools/mono-api-html/ApiChange.cs index 3bf895a47b00..18cc90285b5c 100644 --- a/tools/api-tools/mono-api-html/ApiChange.cs +++ b/tools/api-tools/mono-api-html/ApiChange.cs @@ -10,6 +10,7 @@ class ApiChange { public string Header = ""; public TextChunk Member = new TextChunk (); public bool AnyChange; + public bool IsNullabilityChange; public string SourceDescription; public State State; @@ -45,6 +46,76 @@ public ApiChange AppendModified (string old, string @new) AnyChange = true; return this; } + + // Renders a type change: uses inline nullability rendering if the only + // difference is '?' annotations, otherwise falls back to full modification. + public ApiChange AppendTypeModified (string old, string @new) + { + if (Helper.DiffersOnlyByNullability (old, @new)) + return AppendNullabilityModified (old, @new); + return AppendModified (old, @new); + } + + // Renders a type modification where only nullability annotations ('?') differ. + // Only the added/removed '?' characters are highlighted; the rest is plain text. + public ApiChange AppendNullabilityModified (string old, string @new) + { + int si = 0; + int ti = 0; + while (si < old.Length && ti < @new.Length) { + if (old [si] == @new [ti]) { + Member.Append (old [si]); + si++; + ti++; + } else if (old [si] == '?' && IsNullabilitySuffix (old, si)) { + State.Formatter.DiffRemoval (Member, "?"); + AnyChange = true; + si++; + } else if (@new [ti] == '?' && IsNullabilitySuffix (@new, ti)) { + State.Formatter.DiffAddition (Member, "?"); + AnyChange = true; + ti++; + } else { + // Shouldn't happen for nullability-only diffs, fall back to full modification + State.Formatter.DiffModification (Member, old.Substring (si), @new.Substring (ti)); + AnyChange = true; + return this; + } + } + // Handle remaining characters + while (si < old.Length) { + if (old [si] == '?' && IsNullabilitySuffix (old, si)) { + State.Formatter.DiffRemoval (Member, "?"); + AnyChange = true; + si++; + } else { + Member.Append (old [si]); + si++; + } + } + while (ti < @new.Length) { + if (@new [ti] == '?' && IsNullabilitySuffix (@new, ti)) { + State.Formatter.DiffAddition (Member, "?"); + AnyChange = true; + ti++; + } else { + Member.Append (@new [ti]); + ti++; + } + } + return this; + } + + static bool IsNullabilitySuffix (string text, int index) + { + // A '?' is a nullability suffix if it's at the end, or before a type separator. + // The input type names are already formatted (via GetTypeName + Formatter), so generic + // brackets appear as HTML entities (< / >). A '?' before '&' catches the > case. + if (index + 1 >= text.Length) + return true; + char next = text [index + 1]; + return next == ']' || next == ',' || next == '>' || next == '&' || next == ' '; + } } class ApiChanges : Dictionary> { @@ -61,12 +132,66 @@ public void Add (XElement source, XElement target, ApiChange change) if (!change.AnyChange) return; + // Detect if this change is nullability-only + if (DiffersOnlyByNullability (source, target)) + change.IsNullabilityChange = true; + if (!TryGetValue (change.Header, out List? list)) { list = new List (); base.Add (change.Header, list); } list.Add (change); } + + static bool DiffersOnlyByNullability (XElement source, XElement target) + { + // Compare all attributes, stripping nullability from type-related attributes + var typeAttributes = new HashSet { "returntype", "fieldtype", "ptype", "eventtype", "type" }; + + if (source.Name != target.Name) + return false; + + // Check that all non-type-related attributes are the same + var srcAttrs = source.Attributes ().ToDictionary (a => a.Name.LocalName, a => a.Value); + var tgtAttrs = target.Attributes ().ToDictionary (a => a.Name.LocalName, a => a.Value); + + if (srcAttrs.Count != tgtAttrs.Count) + return false; + + bool hasNullabilityDiff = false; + foreach (var kvp in srcAttrs) { + if (!tgtAttrs.TryGetValue (kvp.Key, out var tgtValue)) + return false; + + if (kvp.Value == tgtValue) + continue; + + if (typeAttributes.Contains (kvp.Key)) { + if (Helper.DiffersOnlyByNullability (kvp.Value, tgtValue)) + hasNullabilityDiff = true; + else + return false; + } else { + return false; + } + } + + // Always check child elements — a non-nullability child diff means this + // is not a nullability-only change, even if the parent attributes differ only by nullability. + var srcChildren = source.Elements ().ToList (); + var tgtChildren = target.Elements ().ToList (); + if (srcChildren.Count != tgtChildren.Count) + return false; + + for (int i = 0; i < srcChildren.Count; i++) { + if (XNode.DeepEquals (srcChildren [i], tgtChildren [i])) + continue; + if (!DiffersOnlyByNullability (srcChildren [i], tgtChildren [i])) + return false; + hasNullabilityDiff = true; + } + + return hasNullabilityDiff; + } } } - diff --git a/tools/api-tools/mono-api-html/ConstructorComparer.cs b/tools/api-tools/mono-api-html/ConstructorComparer.cs index e541861f2093..5ae44bf7ee6d 100644 --- a/tools/api-tools/mono-api-html/ConstructorComparer.cs +++ b/tools/api-tools/mono-api-html/ConstructorComparer.cs @@ -59,7 +59,7 @@ void RenderReturnType (XElement source, XElement target, ApiChange change) var tgtType = target.GetTypeName ("returntype", State); if (srcType != tgtType) { - change.AppendModified (srcType ?? "", tgtType ?? ""); + change.AppendTypeModified (srcType ?? "", tgtType ?? ""); change.Append (" "); } else if (srcType is not null) { // ctor don't have a return type diff --git a/tools/api-tools/mono-api-html/EventComparer.cs b/tools/api-tools/mono-api-html/EventComparer.cs index 302d483a5008..123cfe850897 100644 --- a/tools/api-tools/mono-api-html/EventComparer.cs +++ b/tools/api-tools/mono-api-html/EventComparer.cs @@ -59,7 +59,7 @@ public override bool Equals (XElement source, XElement target, ApiChanges change var tgtEventType = target.GetTypeName ("eventtype", State) ?? ""; if (srcEventType != tgtEventType) { - change.AppendModified (srcEventType, tgtEventType); + change.AppendTypeModified (srcEventType, tgtEventType); } else { change.Append (srcEventType); } diff --git a/tools/api-tools/mono-api-html/FieldComparer.cs b/tools/api-tools/mono-api-html/FieldComparer.cs index ba600ba548c2..d0b6169cbf73 100644 --- a/tools/api-tools/mono-api-html/FieldComparer.cs +++ b/tools/api-tools/mono-api-html/FieldComparer.cs @@ -146,7 +146,7 @@ public override bool Equals (XElement source, XElement target, ApiChanges change var tgtType = target.GetTypeName ("fieldtype", State) ?? ""; if (srcType != tgtType) { - change.AppendModified (srcType, tgtType); + change.AppendTypeModified (srcType, tgtType); } else { change.Append (srcType); } diff --git a/tools/api-tools/mono-api-html/Formatter.cs b/tools/api-tools/mono-api-html/Formatter.cs index a7be2f256c0b..0fce910fc46c 100644 --- a/tools/api-tools/mono-api-html/Formatter.cs +++ b/tools/api-tools/mono-api-html/Formatter.cs @@ -211,6 +211,13 @@ public void Append (string value) cachedOutput.Append (value); } + public void Append (char value) + { + foreach (var kvp in stringbuilders) + kvp.StringBuilder.Append (value); + cachedOutput.Append (value); + } + public override string ToString () { throw new InvalidOperationException (); diff --git a/tools/api-tools/mono-api-html/Helpers.cs b/tools/api-tools/mono-api-html/Helpers.cs index 975eefacd838..9a98857a0826 100644 --- a/tools/api-tools/mono-api-html/Helpers.cs +++ b/tools/api-tools/mono-api-html/Helpers.cs @@ -127,6 +127,7 @@ static bool TryGetAttributeProperty (this XElement? self, string attributeName, StringBuilder sb = null!; bool is_nullable = false; + bool is_nullable_ref = false; if (type.StartsWith ("System.Nullable`1[", StringComparison.Ordinal)) { is_nullable = true; sb = new StringBuilder (type, 18, type.Length - 19, 1024); @@ -134,6 +135,12 @@ static bool TryGetAttributeProperty (this XElement? self, string attributeName, sb = new StringBuilder (type); } + // Handle nullable reference type annotation (trailing '?' added by mono-api-info) + if (!is_nullable && sb.Length > 0 && sb [sb.Length - 1] == '?') { + is_nullable_ref = true; + sb.Remove (sb.Length - 1, 1); + } + bool is_ref = (sb [sb.Length - 1] == '&'); if (is_ref) sb.Remove (sb.Length - 1, 1); @@ -159,6 +166,8 @@ static bool TryGetAttributeProperty (this XElement? self, string attributeName, sb.Append ("[]"); if (is_nullable) sb.Append ('?'); + if (is_nullable_ref) + sb.Append ('?'); if (is_pointer) sb.Append ('*'); return sb.ToString (); @@ -245,5 +254,32 @@ public static FieldAttributes GetFieldAttributes (this XElement element) var srcAttribs = element.Attribute ("attrib"); return (FieldAttributes) (srcAttribs is not null ? Int32.Parse (srcAttribs.Value) : 0); } + + // Strips trailing '?' nullability annotations from a type name for comparison purposes. + // Handles both top-level (System.String?) and nested generics (List`1[System.String?]). + public static string? StripNullability (string? type) + { + if (type is null) + return null; + // Remove all '?' that appear before ']', at end of string, before ',', + // before '>' or '&' (HTML entities like >), or before ' ' (before param name) + var sb = new StringBuilder (type.Length); + for (int i = 0; i < type.Length; i++) { + if (type [i] == '?') { + if (i + 1 >= type.Length || type [i + 1] == ']' || type [i + 1] == ',' || type [i + 1] == '>' || type [i + 1] == '&' || type [i + 1] == ' ') + continue; + } + sb.Append (type [i]); + } + return sb.ToString (); + } + + // Returns true if two type names differ only in nullability annotations. + public static bool DiffersOnlyByNullability (string? source, string? target) + { + if (source == target) + return false; + return StripNullability (source) == StripNullability (target); + } } } diff --git a/tools/api-tools/mono-api-html/MemberComparer.cs b/tools/api-tools/mono-api-html/MemberComparer.cs index d5a0ebfa216c..f0f5915c384b 100644 --- a/tools/api-tools/mono-api-html/MemberComparer.cs +++ b/tools/api-tools/mono-api-html/MemberComparer.cs @@ -134,11 +134,24 @@ void Add (IEnumerable elements) void Modify (ApiChanges modified) { foreach (var changes in modified) { - Formatter.BeginMemberModification (changes.Key); - foreach (var element in changes.Value) { - Formatter.Diff (element); + var nonNullability = changes.Value.Where (c => !c.IsNullabilityChange).ToList (); + var nullabilityOnly = changes.Value.Where (c => c.IsNullabilityChange).ToList (); + + if (nonNullability.Count > 0) { + Formatter.BeginMemberModification (changes.Key); + foreach (var element in nonNullability) { + Formatter.Diff (element); + } + Formatter.EndMemberModification (); + } + + if (nullabilityOnly.Count > 0) { + Formatter.BeginMemberModification (changes.Key + " (nullability)"); + foreach (var element in nullabilityOnly) { + Formatter.Diff (element); + } + Formatter.EndMemberModification (); } - Formatter.EndMemberModification (); } } @@ -315,7 +328,7 @@ protected void RenderParameters (XElement source, XElement target, ApiChange cha } if (paramSourceType != paramTargetType) { - change.AppendModified (paramSourceType ?? "", paramTargetType ?? ""); + change.AppendTypeModified (paramSourceType ?? "", paramTargetType ?? ""); } else { change.Append (paramSourceType ?? ""); } diff --git a/tools/api-tools/mono-api-html/MethodComparer.cs b/tools/api-tools/mono-api-html/MethodComparer.cs index 677a5136bdf1..6846c1cf6b4f 100644 --- a/tools/api-tools/mono-api-html/MethodComparer.cs +++ b/tools/api-tools/mono-api-html/MethodComparer.cs @@ -52,7 +52,7 @@ public override bool Find (XElement e) if (e.GetAttribute ("name") != Source.GetAttribute ("name")) return false; - if (e.GetAttribute ("returntype") != Source.GetAttribute ("returntype")) + if (Helper.StripNullability (e.GetAttribute ("returntype")) != Helper.StripNullability (Source.GetAttribute ("returntype"))) return false; var eGP = e.Element ("generic-parameters"); diff --git a/tools/api-tools/mono-api-html/PropertyComparer.cs b/tools/api-tools/mono-api-html/PropertyComparer.cs index 205600a6a199..a2783f0782d0 100644 --- a/tools/api-tools/mono-api-html/PropertyComparer.cs +++ b/tools/api-tools/mono-api-html/PropertyComparer.cs @@ -103,7 +103,7 @@ void RenderPropertyType (XElement source, XElement target, ApiChange change) if (srcType == tgtType) { change.Append (tgtType); } else { - change.AppendModified (srcType, tgtType); + change.AppendTypeModified (srcType, tgtType); } change.Append (" "); } @@ -150,7 +150,7 @@ void RenderIndexers (List srcIndexers, List tgtIndexers, Api if (srcType == tgtType) { change.Append (tgtType); } else { - change.AppendModified (srcType, tgtType); + change.AppendTypeModified (srcType, tgtType); } change.Append (" "); diff --git a/tools/api-tools/mono-api-info/mono-api-info.cs b/tools/api-tools/mono-api-info/mono-api-info.cs index b0c8a4d9d542..1b22201ef597 100644 --- a/tools/api-tools/mono-api-info/mono-api-info.cs +++ b/tools/api-tools/mono-api-info/mono-api-info.cs @@ -1014,7 +1014,9 @@ protected override void AddExtraAttributes (MemberReference memberDefinition) base.AddExtraAttributes (memberDefinition); FieldDefinition field = (FieldDefinition) memberDefinition; - AddAttribute ("fieldtype", Utils.CleanupTypeName (field.FieldType)); + var fieldTypeName = Utils.CleanupTypeName (field.FieldType); + fieldTypeName = NullabilityHelper.AppendNullabilityToTypeName (fieldTypeName, field.FieldType, field, field.DeclaringType); + AddAttribute ("fieldtype", fieldTypeName); if (field.IsLiteral) { object value = field.Constant;//object value = field.GetValue (null); @@ -1083,7 +1085,9 @@ protected override void AddExtraAttributes (MemberReference memberDefinition) base.AddExtraAttributes (memberDefinition); PropertyDefinition prop = (PropertyDefinition) memberDefinition; - AddAttribute ("ptype", Utils.CleanupTypeName (prop.PropertyType)); + var ptypeName = Utils.CleanupTypeName (prop.PropertyType); + ptypeName = NullabilityHelper.AppendNullabilityToTypeName (ptypeName, prop.PropertyType, prop, prop.DeclaringType); + AddAttribute ("ptype", ptypeName); bool haveParameters; MethodDefinition []? methods = GetMethods ((PropertyDefinition) memberDefinition, out haveParameters); @@ -1149,7 +1153,9 @@ protected override void AddExtraAttributes (MemberReference memberDefinition) base.AddExtraAttributes (memberDefinition); EventDefinition evt = (EventDefinition) memberDefinition; - AddAttribute ("eventtype", Utils.CleanupTypeName (evt.EventType)); + var evtTypeName = Utils.CleanupTypeName (evt.EventType); + evtTypeName = NullabilityHelper.AppendNullabilityToTypeName (evtTypeName, evt.EventType, evt, evt.DeclaringType); + AddAttribute ("eventtype", evtTypeName); } public override string ParentTag { @@ -1217,8 +1223,10 @@ protected override void AddExtraAttributes (MemberReference memberDefinition) AddAttribute ("is-override", "true"); } string rettype = Utils.CleanupTypeName (mbase.MethodReturnType.ReturnType); - if (rettype != "System.Void" || !mbase.IsConstructor) + if (rettype != "System.Void" || !mbase.IsConstructor) { + rettype = NullabilityHelper.AppendNullabilityToTypeName (rettype, mbase.MethodReturnType.ReturnType, mbase.MethodReturnType, mbase); AddAttribute ("returntype", (rettype)); + } // // if (mbase.MethodReturnType.HasCustomAttributes) // AttributeData.OutputAttributes (writer, mbase.MethodReturnType); @@ -1302,7 +1310,7 @@ public override void DoOutput () pt = brt.ElementType; } - AddAttribute ("type", Utils.CleanupTypeName (pt)); + AddAttribute ("type", NullabilityHelper.AppendNullabilityToTypeName (Utils.CleanupTypeName (pt), pt, parameter, parameter.Method as ICustomAttributeProvider)); if (parameter.IsOptional) { AddAttribute ("optional", "true"); @@ -1456,6 +1464,130 @@ public static string GetSignature (IList infos) } + static class NullabilityHelper { + const string NullableAttributeName = "System.Runtime.CompilerServices.NullableAttribute"; + const string NullableContextAttributeName = "System.Runtime.CompilerServices.NullableContextAttribute"; + + // Returns the nullability flag for the top-level type: + // 0 = oblivious, 1 = not-null, 2 = nullable + public static byte GetTopLevelNullability (ICustomAttributeProvider provider, ICustomAttributeProvider? context) + { + // Check for NullableAttribute directly on the member/parameter/return type + var flag = GetNullableFlagFromProvider (provider); + if (flag.HasValue) + return flag.Value; + + // Fall back to NullableContextAttribute on the containing method/type + if (context is not null) { + var contextFlag = GetNullableContextFlag (context); + if (contextFlag.HasValue) + return contextFlag.Value; + } + + return 0; // oblivious + } + + // Gets the NullableContextAttribute flag from a method or type + public static byte? GetNullableContextFlag (ICustomAttributeProvider provider) + { + if (provider is null) + return null; + + if (!provider.HasCustomAttributes) + return GetNullableContextFromParent (provider); + + foreach (var attr in provider.CustomAttributes) { + if (attr.AttributeType.FullName != NullableContextAttributeName) + continue; + if (attr.ConstructorArguments.Count == 1 && attr.ConstructorArguments [0].Value is byte b) + return b; + } + + return GetNullableContextFromParent (provider); + } + + static byte? GetNullableContextFromParent (ICustomAttributeProvider? provider) + { + if (provider is MethodDefinition method) + return GetNullableContextFlag (method.DeclaringType); + if (provider is PropertyDefinition prop) + return GetNullableContextFlag (prop.DeclaringType); + if (provider is FieldDefinition field) + return GetNullableContextFlag (field.DeclaringType); + if (provider is EventDefinition evt) + return GetNullableContextFlag (evt.DeclaringType); + if (provider is TypeDefinition type) { + if (type.DeclaringType is not null) + return GetNullableContextFlag (type.DeclaringType); + // Fall back to module-level NullableContextAttribute + return GetNullableContextFlagFromAttributes (type.Module); + } + return null; + } + + static byte? GetNullableContextFlagFromAttributes (ICustomAttributeProvider? provider) + { + if (provider is null || !provider.HasCustomAttributes) + return null; + + foreach (var attr in provider.CustomAttributes) { + if (attr.AttributeType.FullName != NullableContextAttributeName) + continue; + if (attr.ConstructorArguments.Count == 1 && attr.ConstructorArguments [0].Value is byte b) + return b; + } + return null; + } + + static byte? GetNullableFlagFromProvider (ICustomAttributeProvider provider) + { + if (!provider.HasCustomAttributes) + return null; + + foreach (var attr in provider.CustomAttributes) { + if (attr.AttributeType.FullName != NullableAttributeName) + continue; + if (attr.ConstructorArguments.Count != 1) + continue; + + var arg = attr.ConstructorArguments [0]; + if (arg.Value is byte b) + return b; + if (arg.Value is CustomAttributeArgument [] arr && arr.Length > 0 && arr [0].Value is byte b2) + return b2; + } + + return null; + } + + public static bool IsNullableReferenceType (TypeReference type, ICustomAttributeProvider provider, ICustomAttributeProvider? context) + { + if (type is null) + return false; + + // Value types use Nullable for nullability, not annotations + if (type.IsValueType) + return false; + + // ByReference types (ref/out parameters): check the element type + if (type.IsByReference) { + var elementType = ((ByReferenceType) type).ElementType; + if (elementType.IsValueType) + return false; + } + + var flag = GetTopLevelNullability (provider, context); + return flag == 2; + } + + public static string AppendNullabilityToTypeName (string typeName, TypeReference type, ICustomAttributeProvider provider, ICustomAttributeProvider? context) + { + if (IsNullableReferenceType (type, provider, context)) + return typeName + "?"; + return typeName; + } + } + class TypeReferenceComparer : IComparer { public static TypeReferenceComparer Default = new TypeReferenceComparer (); From b53046a688dfc299407b9bcc607fc55188de0842 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2026 06:43:18 +0000 Subject: [PATCH 65/79] [.github] Bump .github/workflows/inter-branch-merge-base.yml from b36a3594d11b8d56bf7d3dbce919e0688715d5ec to b6ed8ef7f3251d0b67ac2428c06253d0ba328e97 (#25639) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/inter-branch-merge-flow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/inter-branch-merge-flow.yml b/.github/workflows/inter-branch-merge-flow.yml index 7c95ddd17a32..b456685eebb4 100644 --- a/.github/workflows/inter-branch-merge-flow.yml +++ b/.github/workflows/inter-branch-merge-flow.yml @@ -30,4 +30,4 @@ permissions: jobs: Merge: - uses: dotnet/arcade/.github/workflows/inter-branch-merge-base.yml@b36a3594d11b8d56bf7d3dbce919e0688715d5ec # main + uses: dotnet/arcade/.github/workflows/inter-branch-merge-base.yml@b6ed8ef7f3251d0b67ac2428c06253d0ba328e97 # main From 587323252d7ad75bafbeccecf340898621dc797a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2026 08:46:55 +0200 Subject: [PATCH 66/79] [github] Bump actions/setup-dotnet from 5.2.0 to 5.3.0 (#25638) Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 5.2.0 to 5.3.0. --- .github/workflows/autoformat-v2.yml | 4 ++-- .github/workflows/maestro-changelog.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/autoformat-v2.yml b/.github/workflows/autoformat-v2.yml index 4fb4d4a234df..e1e32292ad03 100644 --- a/.github/workflows/autoformat-v2.yml +++ b/.github/workflows/autoformat-v2.yml @@ -21,7 +21,7 @@ jobs: persist-credentials: false - name: 'Install .NET' - uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5 + uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5 with: global-json-file: ./global.json @@ -67,7 +67,7 @@ jobs: fetch-depth: 1 - name: 'Install .NET' - uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5 + uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5 with: global-json-file: ./global.json diff --git a/.github/workflows/maestro-changelog.yml b/.github/workflows/maestro-changelog.yml index e390cd2d872c..ab0ebf924389 100644 --- a/.github/workflows/maestro-changelog.yml +++ b/.github/workflows/maestro-changelog.yml @@ -17,7 +17,7 @@ jobs: if: github.event.pull_request.user.login == 'dotnet-maestro[bot]' steps: - - uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5 + - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5 with: dotnet-version: '9' From 90011e1495fb12a9b960fc1d22b8bf258ac0174a Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 4 Jun 2026 08:55:21 +0200 Subject: [PATCH 67/79] [tools] Remove most static state from the tools. (#25619) The remaining static state is mostly caching of environment variables (which won't change during a process' lifetime). This is necessary, because soon we'll include some of this code in MSBuild tasks, which shouldn't have static state. --- .../ErrorHelper.msbuild.cs | 9 +- src/bgen/BindingTouch.cs | 1 + tests/common/ErrorHelper.tests.cs | 41 ++- tools/common/Application.cs | 61 +++- tools/common/Driver.cs | 332 +----------------- tools/common/ErrorHelper.tools.cs | 93 ++--- tools/common/FileCopier.cs | 10 - tools/common/Frameworks.cs | 2 +- tools/common/IToolLog.cs | 5 + tools/common/Optimizations.cs | 4 +- tools/common/StaticRegistrar.cs | 2 +- tools/common/StringUtils.cs | 6 +- tools/common/cache.cs | 16 +- tools/common/error.cs | 23 +- .../dotnet-linker/BackingFieldDelayHandler.cs | 8 +- tools/dotnet-linker/LinkerConfiguration.cs | 36 +- tools/dotnet-linker/SetupStep.cs | 1 - .../Steps/ConfigurationAwareStep.cs | 2 +- .../Steps/TrimmableRegistrarStep.cs | 2 +- tools/linker/CoreTypeMapStep.cs | 2 +- tools/mtouch/AssemblyResolver.cs | 2 +- 21 files changed, 196 insertions(+), 462 deletions(-) diff --git a/msbuild/Xamarin.MacDev.Tasks/ErrorHelper.msbuild.cs b/msbuild/Xamarin.MacDev.Tasks/ErrorHelper.msbuild.cs index 9827fc45e2bd..8bda5c48e690 100644 --- a/msbuild/Xamarin.MacDev.Tasks/ErrorHelper.msbuild.cs +++ b/msbuild/Xamarin.MacDev.Tasks/ErrorHelper.msbuild.cs @@ -10,10 +10,9 @@ namespace Xamarin.Bundler { public static partial class ErrorHelper { public static ApplePlatform Platform; - internal static string Prefix { - get { - return Xamarin.MacDev.Tasks.LoggingExtensions.ErrorPrefix; - } + internal static string GetPrefix (IToolLog? log) + { + return Xamarin.MacDev.Tasks.LoggingExtensions.ErrorPrefix; } public enum WarningLevel { @@ -24,7 +23,7 @@ public enum WarningLevel { static Dictionary? warning_levels; - public static WarningLevel GetWarningLevel (int code) + public static WarningLevel GetWarningLevel (IToolLog log, int code) { WarningLevel level; diff --git a/src/bgen/BindingTouch.cs b/src/bgen/BindingTouch.cs index 72aa883e0985..7b9e2d32dadb 100644 --- a/src/bgen/BindingTouch.cs +++ b/src/bgen/BindingTouch.cs @@ -46,6 +46,7 @@ public class BindingTouch : IDisposable, IToolLog { public static ApplePlatform [] AllPlatforms = new ApplePlatform [] { ApplePlatform.iOS, ApplePlatform.MacOSX, ApplePlatform.TVOS, ApplePlatform.MacCatalyst }; public static PlatformName [] AllPlatformNames = new PlatformName [] { PlatformName.iOS, PlatformName.MacOSX, PlatformName.TvOS, PlatformName.MacCatalyst }; public PlatformName CurrentPlatform; + public ApplePlatform Platform { get => CurrentPlatform.AsApplePlatform (); } public bool BindThirdPartyLibrary = true; public string? outfile; diff --git a/tests/common/ErrorHelper.tests.cs b/tests/common/ErrorHelper.tests.cs index eb2c7d952b10..495ed26053ff 100644 --- a/tests/common/ErrorHelper.tests.cs +++ b/tests/common/ErrorHelper.tests.cs @@ -3,6 +3,7 @@ #nullable enable using System.Linq; +using System.Runtime.CompilerServices; using Mono.Cecil; using Mono.Cecil.Cil; @@ -13,10 +14,9 @@ namespace Xamarin.Bundler { public static partial class ErrorHelper { public static ApplePlatform Platform; - internal static string Prefix { - get { - return "TESTS"; - } + internal static string GetPrefix (IToolLog? log) + { + return "TESTS"; } public enum WarningLevel { @@ -25,33 +25,32 @@ public enum WarningLevel { Disable = 1, } - static Dictionary? warning_levels; + static ConditionalWeakTable> warning_levels = new (); - public static WarningLevel GetWarningLevel (int code) + public static WarningLevel GetWarningLevel (IToolLog log, int code) { - WarningLevel level; - - if (warning_levels is null) - return WarningLevel.Warning; + if (warning_levels.TryGetValue (log, out var log_warning_levels)) { + // code -1: all codes + if (log_warning_levels.TryGetValue (-1, out var level)) + return level; - // code -1: all codes - if (warning_levels.TryGetValue (-1, out level)) - return level; - - if (warning_levels.TryGetValue (code, out level)) - return level; + if (log_warning_levels.TryGetValue (code, out level)) + return level; + } return WarningLevel.Warning; } - public static void SetWarningLevel (WarningLevel level, int? code = null /* if null, apply to all warnings */) + public static void SetWarningLevel (IToolLog log, WarningLevel level, int? code = null /* if null, apply to all warnings */) { - if (warning_levels is null) - warning_levels = new Dictionary (); + if (!warning_levels.TryGetValue (log, out var log_warning_levels)) { + log_warning_levels = new Dictionary (); + warning_levels.Add (log, log_warning_levels); + } if (code.HasValue) { - warning_levels [code.Value] = level; + log_warning_levels [code.Value] = level; } else { - warning_levels [-1] = level; // code -1: all codes. + log_warning_levels [-1] = level; // code -1: all codes. } } } diff --git a/tools/common/Application.cs b/tools/common/Application.cs index 6aad8900c70c..5405b8eb0244 100644 --- a/tools/common/Application.cs +++ b/tools/common/Application.cs @@ -102,7 +102,8 @@ public partial class Application : IToolLog { public HashSet WeakFrameworks = new HashSet (); public bool IsExtension; - public ApplePlatform Platform { get { return Driver.TargetFramework.Platform; } } + public TargetFramework TargetFramework { get; set; } + public ApplePlatform Platform { get { return TargetFramework.Platform; } } public List MonoLibraries = new List (); public List InterpretedAssemblies = new List (); @@ -266,7 +267,7 @@ public bool PackageManagedDebugSymbols { public Version GetMacCatalystiOSVersion (Version macOSVersion) { #if LEGACY_TOOLS - if (macOSVersion.Major >= 26 && Driver.SdkRoot is null) { + if (macOSVersion.Major >= 26 && SdkRoot is null) { // this shouldn't happen for normal builds, nor for customers, so just show an internal 99 warning. ErrorHelper.Warning (this, 99, Errors.MX0099, $"No Xcode configured, assuming the macOS version {macOSVersion} is identical to the Mac Catalyst/iOS version."); return macOSVersion; @@ -287,6 +288,7 @@ public Application (LinkerConfiguration configuration) #endif this.StaticRegistrar = new StaticRegistrar (this); this.Resolver = new PlatformResolver (this); + SetDefaultHiddenWarnings (); } #if !LEGACY_TOOLS @@ -1032,7 +1034,7 @@ public void GetAotArguments (string filename, Abi abi, string outputDir, string if (!string.IsNullOrEmpty (llvm_path)) { aotArguments.Add ($"llvm-path={llvm_path}"); } else { - aotArguments.Add ($"llvm-path={Driver.GetFrameworkCurrentDirectory (app)}/LLVM/bin/"); + aotArguments.Add ($"llvm-path={app.FrameworkCurrentDirectory}/LLVM/bin/"); } } @@ -1198,17 +1200,12 @@ public bool VerifyDynamicFramework (string framework_path) } #endif // !LEGACY_TOOLS - static Application () - { - SetDefaultHiddenWarnings (); - } - - public static void SetDefaultHiddenWarnings () + public void SetDefaultHiddenWarnings () { // People don't like these warnings (#20670), and they also complicate our tests, so ignore them. - ErrorHelper.ParseWarningLevel (ErrorHelper.WarningLevel.Disable, "4178"); // The class '{0}' will not be registered because the {1} framework has been removed from the {2} SDK. - ErrorHelper.ParseWarningLevel (ErrorHelper.WarningLevel.Disable, "4189"); // The class '{0}' will not be registered because it has been removed from the {1} SDK. - ErrorHelper.ParseWarningLevel (ErrorHelper.WarningLevel.Disable, "4190"); // The class '{0}' will not be registered because the {1} framework has been deprecated from the {2} SDK. + ErrorHelper.ParseWarningLevel (this, ErrorHelper.WarningLevel.Disable, "4178"); // The class '{0}' will not be registered because the {1} framework has been removed from the {2} SDK. + ErrorHelper.ParseWarningLevel (this, ErrorHelper.WarningLevel.Disable, "4189"); // The class '{0}' will not be registered because it has been removed from the {1} SDK. + ErrorHelper.ParseWarningLevel (this, ErrorHelper.WarningLevel.Disable, "4190"); // The class '{0}' will not be registered because the {1} framework has been deprecated from the {2} SDK. } public void Log (string message) @@ -1236,5 +1233,45 @@ public int Verbosity { get => verbosity; set => verbosity = value; } + + public string? SdkRoot { get; set; } + public string? DeveloperDirectory { get; set; } + + string? framework_dir; + public string FrameworkCurrentDirectory { + get { + if (framework_dir is null) + throw new InvalidOperationException ($"Teh current framework directory hasn't been set."); + return framework_dir; + } + set { + framework_dir = value; + } + } + + /// + /// This returns the /Applications/Xcode*.app/Contents/Developer/Platforms directory + /// + public string PlatformsDirectory { + get { + if (DeveloperDirectory is null) + throw new InvalidOperationException ("DeveloperDirectory is not set"); + return Path.Combine (DeveloperDirectory, "Platforms"); + } + } + + Version? xcode_version; + public Version XcodeVersion { + get { + if (xcode_version is null) + throw ErrorHelper.CreateError (99, Errors.MX0099, "The Xcode version has not been configured. Pass --xcode-version or configure an Xcode installation."); + return xcode_version; + } + set { + xcode_version = value; + } + } + + public string? XcodeProductVersion { get; set; } } } diff --git a/tools/common/Driver.cs b/tools/common/Driver.cs index c04f81cd0221..fe912e00e4a8 100644 --- a/tools/common/Driver.cs +++ b/tools/common/Driver.cs @@ -7,9 +7,8 @@ */ using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; using System.IO; +using System.Globalization; using System.Linq; using System.Text; @@ -20,8 +19,6 @@ namespace Xamarin.Bundler { public partial class Driver { - public static bool Force { get; set; } - #if LEGACY_TOOLS public static int Main (string [] args) { @@ -31,8 +28,6 @@ public static int Main (string [] args) return Main2 (args); } catch (Exception e) { ErrorHelper.Show (ConsoleLog.Instance, e); - } finally { - Watch (ConsoleLog.Instance, "Total time", 0); } return 0; } @@ -43,7 +38,7 @@ static void ParseOptions (Application app, Mono.Options.OptionSet options, strin options.Add ("v|verbose", "Specify how verbose the output should be. This can be passed multiple times to increase the verbosity.", v => app.Verbosity++); options.Add ("q|quiet", "Specify how quiet the output should be. This can be passed multiple times to increase the silence.", v => app.Verbosity--); options.Add ("reference=", "Add an assembly to be processed.", v => app.References.Add (v)); - options.Add ("sdkroot=", "Specify the location of Apple SDKs, default to 'xcode-select' value.", v => sdk_root = v); + options.Add ("sdkroot=", "Specify the location of Apple SDKs, default to 'xcode-select' value.", v => app.SdkRoot = v); options.Add ("sdk=", "Specifies the SDK version to compile against (version, for example \"10.9\"). For Mac Catalyst, this is the macOS version of the SDK.", v => { try { app.SdkVersion = StringUtils.ParseVersion (v); @@ -53,7 +48,7 @@ static void ParseOptions (Application app, Mono.Options.OptionSet options, strin } }); options.Add ("target-framework=", "Specify target framework to use. Currently supported: '" + string.Join ("', '", TargetFramework.ValidFrameworks.Select ((v) => v.ToString ())) + "'.", v => { - targetFramework = TargetFramework.Parse (v); + app.TargetFramework = TargetFramework.Parse (v); }); options.Add ("abi=", "Comma-separated list of ABIs to target.", v => app.ParseAbi (v)); options.Add ("runregistrar:", "Runs the registrar on the input assembly and outputs a corresponding native library.", @@ -72,7 +67,7 @@ static void ParseOptions (Application app, Mono.Options.OptionSet options, strin options.Add ("xcode-version=", "The Xcode version we're building with", v => { if (!Version.TryParse (v, out var xcodeVersion)) throw ErrorHelper.CreateError (26, Errors.MX0026, $"xcode-version:{v}", "Expected a valid version string."); - Driver.XcodeVersion = xcodeVersion; + app.XcodeVersion = xcodeVersion; }); try { @@ -97,13 +92,6 @@ public static int GetDefaultVerbosity () return v; } - static TargetFramework targetFramework; - - public static TargetFramework TargetFramework { - get { return targetFramework; } - set { targetFramework = value; } - } - static void FileMove (string source, string target) { File.Delete (target); @@ -186,25 +174,7 @@ internal static string GetFullPath () return System.Reflection.Assembly.GetExecutingAssembly ().Location; } - static string? xcode_product_version; - public static string? XcodeProductVersion { - get { - return xcode_product_version; - } - } - - static Version? xcode_version; - public static Version XcodeVersion { - get { - if (xcode_version is null) - throw ErrorHelper.CreateError (99, Errors.MX0099, "The Xcode version has not been configured. Pass --xcode-version or configure an Xcode installation."); - return xcode_version; - } - set { - xcode_version = value; - } - } - +#if LEGACY_TOOLS static void SetCurrentLanguage (IToolLog log) { // There's no way to change the current culture from the command-line @@ -236,6 +206,7 @@ static void SetCurrentLanguage (IToolLog log) ErrorHelper.Warning (log, 124, e, Errors.MT0124, lang, lang_variable, e.Message); } } +#endif public static void Touch (IToolLog log, IEnumerable filenames, DateTime? timestamp = null) { @@ -261,28 +232,7 @@ public static void Touch (IToolLog log, params string [] filenames) Touch (log, (IEnumerable) filenames); } - static int watch_level; - static Stopwatch? watch; - - public static int WatchLevel { - get { return watch_level; } - set { - watch_level = value; - if ((watch_level > 0) && (watch is null)) { - watch = new Stopwatch (); - watch.Start (); - } - } - } - - public static void Watch (IToolLog log, string msg, int level) - { - if ((watch is null) || (level > WatchLevel)) - return; - log.Log ($"{new string ('!', level)}Timestamp {msg}: {watch.ElapsedMilliseconds} ms"); - } - - internal static PDictionary? FromPList (string name) + internal static PDictionary FromPList (string name) { if (!File.Exists (name)) throw ErrorHelper.CreateError (24, Errors.MT0024, name); @@ -301,48 +251,6 @@ public static void Watch (IToolLog log, string msg, int level) return output.ToString ().Trim (); } - static string? sdk_root; - static string? developer_directory = null; - - public static string? SdkRoot { - get => sdk_root; - set => sdk_root = value; - } - - public static string? DeveloperDirectory { - get { - return developer_directory; - } - } - - // This returns the /Applications/Xcode*.app/Contents/Developer/Platforms directory - public static string PlatformsDirectory { - get { - if (DeveloperDirectory is null) - throw new InvalidOperationException ("DeveloperDirectory is not set"); - return Path.Combine (DeveloperDirectory, "Platforms"); - } - } - - // This returns the /Applications/Xcode*.app/Contents/Developer/Platforms/*.platform directory - public static string GetPlatformDirectory (Application app) - { - return Path.Combine (PlatformsDirectory, GetPlatform (app) + ".platform"); - } - - static string? framework_dir; - public static string GetFrameworkCurrentDirectory (Application app) - { - if (framework_dir is null) - throw new InvalidOperationException ($"Teh current framework directory hasn't been set."); - return framework_dir; - } - - public static void SetFrameworkCurrentDirectory (string value) - { - framework_dir = value; - } - // This returns the platform to use in /Applications/Xcode*.app/Contents/Developer/Platforms/*.platform public static string GetPlatform (Application app) { @@ -364,7 +272,7 @@ public static string GetFrameworkDirectory (Application app) { var platform = GetPlatform (app); var sdkVersion = app.NativeSdkVersion?.ToString () ?? ""; - return Path.Combine (PlatformsDirectory, platform + ".platform", "Developer", "SDKs", platform + sdkVersion + ".sdk"); + return Path.Combine (app.PlatformsDirectory, platform + ".platform", "Developer", "SDKs", platform + sdkVersion + ".sdk"); } public static string GetProductAssembly (Application app) @@ -385,6 +293,8 @@ public static string GetProductAssembly (Application app) public static void ValidateXcode (Application app, bool accept_any_xcode_version, bool warn_if_not_found) { + var sdk_root = app.SdkRoot; + if (sdk_root is null) { sdk_root = FindSystemXcode (app); if (sdk_root is null) { @@ -419,28 +329,28 @@ public static void ValidateXcode (Application app, bool accept_any_xcode_version // Check what kind of path we got if (File.Exists (Path.Combine (sdk_root, "Contents", "MacOS", "Xcode"))) { // path to the Xcode.app - developer_directory = Path.Combine (sdk_root, "Contents", "Developer"); + app.DeveloperDirectory = Path.Combine (sdk_root, "Contents", "Developer"); } else if (File.Exists (Path.Combine (sdk_root, "..", "MacOS", "Xcode"))) { // path to Contents/Developer - developer_directory = Path.GetFullPath (Path.Combine (sdk_root, "..", "..", "Contents", "Developer")); + app.DeveloperDirectory = Path.GetFullPath (Path.Combine (sdk_root, "..", "..", "Contents", "Developer")); } else { throw ErrorHelper.CreateError (57, Errors.MT0057, sdk_root); } - var plist_path = Path.Combine (Path.GetDirectoryName (DeveloperDirectory)!, "version.plist"); + var plist_path = Path.Combine (Path.GetDirectoryName (app.DeveloperDirectory)!, "version.plist"); if (File.Exists (plist_path)) { var plist = FromPList (plist_path); - var version = plist?.GetString ("CFBundleShortVersionString"); + var version = plist.GetString ("CFBundleShortVersionString"); if (version is null) - throw ErrorHelper.CreateError (58, Errors.MT0058, Path.GetDirectoryName (Path.GetDirectoryName (DeveloperDirectory)), plist_path); - xcode_version = new Version (version); - xcode_product_version = plist!.GetString ("ProductBuildVersion"); + throw ErrorHelper.CreateError (58, Errors.MT0058, Path.GetDirectoryName (Path.GetDirectoryName (app.DeveloperDirectory)), plist_path); + app.XcodeVersion = new Version (version); + app.XcodeProductVersion = plist.GetString ("ProductBuildVersion"); } else { - throw ErrorHelper.CreateError (58, Errors.MT0058, Path.GetDirectoryName (Path.GetDirectoryName (DeveloperDirectory)), plist_path); + throw ErrorHelper.CreateError (58, Errors.MT0058, Path.GetDirectoryName (Path.GetDirectoryName (app.DeveloperDirectory)), plist_path); } - app.Log (1, "Using Xcode {0} ({2}) found in {1}", XcodeVersion, sdk_root, XcodeProductVersion); + app.Log (1, "Using Xcode {0} ({2}) found in {1}", app.XcodeVersion, sdk_root, app.XcodeProductVersion); } internal static bool TryParseBool (string value, out bool result) @@ -476,210 +386,6 @@ internal static bool ParseBool (string value, string name, bool show_error = tru return result; } -#if !LEGACY_TOOLS - static readonly Dictionary tools = new Dictionary (); - static string FindTool (Application app, string tool) - { - lock (tools) { - if (tools.TryGetValue (tool, out var path) && path is not null) - return path; - } - - var foundPath = LocateTool (app, tool); - static string? LocateTool (Application app, string tool) - { - if (XcrunFind (app, tool, out var path)) - return path; - - if (DeveloperDirectory is null) - return null; - - // either /Developer (Xcode 4.2 and earlier), /Applications/Xcode.app/Contents/Developer (Xcode 4.3) or user override - path = Path.Combine (DeveloperDirectory, "usr", "bin", tool); - if (File.Exists (path)) - return path; - - // Xcode 4.3 (without command-line tools) also has a copy of 'strip' - path = Path.Combine (DeveloperDirectory, "Toolchains", "XcodeDefault.xctoolchain", "usr", "bin", tool); - if (File.Exists (path)) - return path; - - // Xcode "Command-Line Tools" install a copy in /usr/bin (and it can be there afterward) - path = Path.Combine ("/usr", "bin", tool); - if (File.Exists (path)) - return path; - - return null; - } - - // We can end up finding the same tool multiple times. - // That's not a problem. - lock (tools) - tools [tool] = foundPath; - - if (foundPath is null) - throw ErrorHelper.CreateError (5307, Errors.MX5307 /* Missing '{0}' tool. Please install Xcode 'Command-Line Tools' component */, tool); - - return foundPath; - } - - static bool XcrunFind (Application app, string tool, [NotNullWhen (true)] out string? path) - { - return XcrunFind (app, ApplePlatform.None, false, tool, out path); - } - - static bool XcrunFind (Application app, ApplePlatform platform, bool is_simulator, string tool, [NotNullWhen (true)] out string? path) - { - var env = new Dictionary (); - // Unset XCODE_DEVELOPER_DIR_PATH. See https://github.com/dotnet/macios/issues/3931. - env.Add ("XCODE_DEVELOPER_DIR_PATH", null); - // Set DEVELOPER_DIR if we have it - if (!string.IsNullOrEmpty (DeveloperDirectory)) - env.Add ("DEVELOPER_DIR", DeveloperDirectory); - - path = null; - - var args = new List (); - if (platform != ApplePlatform.None) { - args.Add ("-sdk"); - switch (platform) { - case ApplePlatform.iOS: - args.Add (is_simulator ? "iphonesimulator" : "iphoneos"); - break; - case ApplePlatform.MacOSX: - args.Add ("macosx"); - break; - case ApplePlatform.TVOS: - args.Add (is_simulator ? "appletvsimulator" : "appletvos"); - break; - default: - throw ErrorHelper.CreateError (71, Errors.MX0071 /* Unknown platform: {0}. This usually indicates a bug in {1}; please file a bug report at https://github.com/dotnet/macios/issues/new with a test case. */, platform.ToString (), app.ProductName); - } - } - args.Add ("-f"); - args.Add (tool); - - var stdout = new StringBuilder (); - var stderr = new StringBuilder (); - var both = new StringBuilder (); - // xcrun can write unrelated stuff to stderr even if it succeeds, so we need to separate stdout and stderr. - // We also want to print out what happened if something went wrong, and in that case we don't want stdout - // and stderr captured separately, because related lines could end up printed far from eachother in time, - // and that's confusing. So capture stdout and stderr by themselves, and also capture both together. - int ret = RunCommand (app, "xcrun", args, env, - (v) => { - lock (both) { - both.AppendLine (v); - stdout.AppendLine (v); - } - }, - (v) => { - lock (both) { - both.AppendLine (v); - stderr.AppendLine (v); - } - }); - - if (ret == 0) { - path = stdout.ToString ().Trim (); - if (!File.Exists (path)) { - ErrorHelper.Warning (app, 5315, Errors.MX5315 /* The tool xcrun failed to return a valid result (the file {0} does not exist). Check build log for details. */, tool, path); - return false; - } - } else { - app.Log (1, "Failed to locate the developer tool '{0}', 'xcrun {1}' returned with the exit code {2}:\n{3}", tool, string.Join (" ", args), ret, both.ToString ()); - } - - return ret == 0; - } - - public static void RunXcodeTool (Application app, string tool, params string [] arguments) - { - RunXcodeTool (app, tool, (IList) arguments); - } - - public static void RunXcodeTool (Application app, string tool, IList arguments) - { - var executable = FindTool (app, tool); - var rv = RunCommand (app, executable, arguments); - if (rv != 0) - throw ErrorHelper.CreateError (5309, Errors.MX5309 /* Failed to execute the tool '{0}', it failed with an error code '{1}'. Please check the build log for details. */, tool, rv); - } - - public static void RunClang (Application app, IList arguments) - { - RunXcodeTool (app, "clang", arguments); - } - - public static void RunInstallNameTool (Application app, IList arguments) - { - RunXcodeTool (app, "install_name_tool", arguments); - } - - public static void RunBitcodeStrip (Application app, IList arguments) - { - RunXcodeTool (app, "bitcode_strip", arguments); - } - - public static void RunLipo (Application app, string output, IEnumerable inputs) - { - var sb = new List (); - sb.AddRange (inputs); - sb.Add ("-create"); - sb.Add ("-output"); - sb.Add (output); - RunLipo (app, sb); - } - - public static void RunLipoAndCreateDsym (Application app, string output, IEnumerable inputs) - { - RunLipo (app, output, inputs); - - var dsymFolders = inputs.Select (input => input + ".dSYM").Where (Directory.Exists).ToArray (); - if (dsymFolders.Length > 1) { - // Lipo the dSYMs into one big happy dSYM - var dsymLibsDir = dsymFolders.Select (dsym => Path.Combine (dsym, "Contents", "Resources", "DWARF")).ToArray (); - var allLibs = dsymLibsDir.Where (Directory.Exists).SelectMany (dir => Directory.EnumerateFiles (dir)).Select (dir => Path.GetFileName (dir)).Distinct ().ToArray (); - - foreach (var lib in allLibs) { - var outputLib = Path.Combine (dsymLibsDir [0], lib); - var allDsymInputs = dsymLibsDir.Select (libDir => Path.Combine (libDir, lib)).Where (File.Exists).ToArray (); - Driver.RunLipo (app, outputLib, allDsymInputs); - } - } - - // Move the dSYM next to its executable - if (dsymFolders.Length > 0) { - var outputDsymDir = output + ".dSYM"; - if (Directory.Exists (outputDsymDir)) - Directory.Delete (outputDsymDir, true); - Directory.Move (dsymFolders [0], outputDsymDir); - RunCommand (app, "/usr/bin/mdimport", outputDsymDir); - } - } - - public static void RunLipo (Application app, IList options) - { - RunXcodeTool (app, "lipo", options); - } - - public static void CreateDsym (Application app, string output_dir, string appname, string dsym_dir) - { - RunDsymUtil (app, Path.Combine (output_dir, appname), "-num-threads", "4", "-z", "-o", dsym_dir); - RunCommand (app, "/usr/bin/mdimport", dsym_dir); - } - - public static void RunDsymUtil (Application app, params string [] options) - { - RunXcodeTool (app, "dsymutil", options); - } - - public static void RunStrip (Application app, IList options) - { - RunXcodeTool (app, "strip", options); - } -#endif // !LEGACY_TOOLS - public static string CorlibName { get { return "System.Private.CoreLib"; diff --git a/tools/common/ErrorHelper.tools.cs b/tools/common/ErrorHelper.tools.cs index 275f0d04a894..e930f95731f6 100644 --- a/tools/common/ErrorHelper.tools.cs +++ b/tools/common/ErrorHelper.tools.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; - +using System.Runtime.CompilerServices; using Mono.Cecil; using Mono.Cecil.Cil; @@ -13,22 +13,20 @@ namespace Xamarin.Bundler { public static partial class ErrorHelper { - public static ApplePlatform Platform; - - internal static string Prefix { - get { - switch (Platform) { - case ApplePlatform.iOS: - case ApplePlatform.TVOS: - case ApplePlatform.MacCatalyst: - case ApplePlatform.None: // Return "MT" by default instead of throwing an exception, because any exception here will most likely hide whatever other error we're trying to show. - return "MT"; - case ApplePlatform.MacOSX: - return "MM"; - default: - // Do not use the ErrorHandler machinery, because it will probably end up recursing and eventually throwing a StackOverflowException. - throw new InvalidOperationException ($"Unknown platform: {Platform}"); - } + internal static string GetPrefix (IToolLog? log) + { + switch (log?.Platform) { + case ApplePlatform.iOS: + case ApplePlatform.TVOS: + case ApplePlatform.MacCatalyst: + case ApplePlatform.None: // Return "MT" by default instead of throwing an exception, because any exception here will most likely hide whatever other error we're trying to show. + case null: + return "MT"; + case ApplePlatform.MacOSX: + return "MM"; + default: + // Do not use the ErrorHandler machinery, because it will probably end up recursing and eventually throwing a StackOverflowException. + throw new InvalidOperationException ($"Unknown platform: {log.Platform}"); } } @@ -38,48 +36,42 @@ public enum WarningLevel { Disable = 1, } - static Dictionary? warning_levels; - -#pragma warning disable 649 - public static Func? IsExpectedException; - public static Action? ExitCallback; -#pragma warning restore 649 + static ConditionalWeakTable> warning_levels = new (); - public static WarningLevel GetWarningLevel (int code) + public static WarningLevel GetWarningLevel (IToolLog log, int code) { - WarningLevel level; + if (warning_levels.TryGetValue (log, out var log_warning_levels)) { + // code -1: all codes + if (log_warning_levels.TryGetValue (-1, out var level)) + return level; - if (warning_levels is null) - return WarningLevel.Warning; - - // code -1: all codes - if (warning_levels.TryGetValue (-1, out level)) - return level; - - if (warning_levels.TryGetValue (code, out level)) - return level; + if (log_warning_levels.TryGetValue (code, out level)) + return level; + } return WarningLevel.Warning; } - public static void SetWarningLevel (WarningLevel level, int? code = null /* if null, apply to all warnings */) + public static void SetWarningLevel (IToolLog log, WarningLevel level, int? code = null /* if null, apply to all warnings */) { - if (warning_levels is null) - warning_levels = new Dictionary (); + if (!warning_levels.TryGetValue (log, out var log_warning_levels)) { + log_warning_levels = new Dictionary (); + warning_levels.Add (log, log_warning_levels); + } if (code.HasValue) { - warning_levels [code.Value] = level; + log_warning_levels [code.Value] = level; } else { - warning_levels [-1] = level; // code -1: all codes. + log_warning_levels [-1] = level; // code -1: all codes. } } - public static void ParseWarningLevel (WarningLevel level, string value) + public static void ParseWarningLevel (IToolLog log, WarningLevel level, string value) { if (string.IsNullOrEmpty (value)) { - SetWarningLevel (level); + SetWarningLevel (log, level); } else { foreach (var code in value.Split (new char [] { ',' }, StringSplitOptions.RemoveEmptyEntries)) - SetWarningLevel (level, int.Parse (code)); + SetWarningLevel (log, level, int.Parse (code)); } } @@ -254,7 +246,7 @@ public static ProductException Create (Application app, int code, bool error, Ex public static void Warning (IToolLog log, int code, string message, params object [] args) { - Show (log, new ProductException (code, false, message, args)); + Show (log, new ProductException (code, false, null, message, args)); } public static void Warning (IToolLog log, int code, Exception innerException, string message, params object [] args) @@ -269,7 +261,7 @@ public static void ThrowIfErrors (IToolLog log, IList exceptions) return; // Separate warnings from errors - var grouped = exceptions.GroupBy ((v) => (v as ProductException)?.Error == false); + var grouped = exceptions.GroupBy ((v) => (v as ProductException)?.IsError (log) == false); var warnings = grouped.SingleOrDefault ((v) => v.Key); if (warnings?.Any () == true) @@ -299,8 +291,6 @@ public static void Show (IToolLog log, Exception e) static void Exit (int exitCode) { - if (ExitCallback is not null) - ExitCallback (exitCode); Environment.Exit (exitCode); } @@ -310,9 +300,9 @@ static bool ShowInternal (IToolLog log, Exception e) bool error = true; if (mte is not null) { - error = mte.Error; + error = mte.IsError (log); - if (!error && GetWarningLevel (mte.Code) == WarningLevel.Disable) + if (!error && GetWarningLevel (log, mte.Code) == WarningLevel.Disable) return false; // This is an ignored warning. log.LogError (mte.ToString ()); @@ -321,14 +311,9 @@ static bool ShowInternal (IToolLog log, Exception e) if (log.Verbosity > 2 && !string.IsNullOrEmpty (e.StackTrace)) log.LogError (e.StackTrace); - } else if (IsExpectedException is null || !IsExpectedException (e)) { - log.LogError ("error " + Prefix + "0000: Unexpected error - Please file a bug report at https://github.com/dotnet/macios/issues/new"); - log.LogError (e.ToString ()); } else { + log.LogError ("error " + GetPrefix (log) + "0000: Unexpected error - Please file a bug report at https://github.com/dotnet/macios/issues/new"); log.LogError (e.ToString ()); - ShowInner (log, e); - if (log.Verbosity > 2 && !string.IsNullOrEmpty (e.StackTrace)) - log.LogError (e.StackTrace); } return error; diff --git a/tools/common/FileCopier.cs b/tools/common/FileCopier.cs index d10e93b59827..d26254c1ba17 100644 --- a/tools/common/FileCopier.cs +++ b/tools/common/FileCopier.cs @@ -241,11 +241,6 @@ static CopyFileResult CopyFileCallback (CopyFileWhat what, CopyFileStep stage, I // if it's later than the timestamp of the "target" file itself. public static bool IsUptodate (IToolLog log, string source, string target, bool check_contents = false, bool check_stamp = true) { -#if LEGACY_TOOLS || BUNDLER // msbuild does not have force - if (Driver.Force) - return false; -#endif - var tfi = new FileInfo (target); if (!tfi.Exists) { @@ -288,11 +283,6 @@ public static bool IsUptodate (IToolLog log, string source, string target, bool // if it's later than the timestamp of the "target" file itself. public static bool IsUptodate (IToolLog log, IEnumerable sources, IEnumerable targets, bool check_stamp = true) { -#if LEGACY_TOOLS || BUNDLER // msbuild does not have force - if (Driver.Force) - return false; -#endif - DateTime max_source = DateTime.MinValue; string? max_s = null; diff --git a/tools/common/Frameworks.cs b/tools/common/Frameworks.cs index 3970120f17fa..8e40602e764a 100644 --- a/tools/common/Frameworks.cs +++ b/tools/common/Frameworks.cs @@ -134,7 +134,7 @@ public void Add (string @namespace, string framework, Version version, Version? return null; } - static Version NotAvailableInSimulator = new Version (int.MaxValue, int.MaxValue); + static readonly Version NotAvailableInSimulator = new Version (int.MaxValue, int.MaxValue); static Frameworks? mac_frameworks; public static Frameworks MacFrameworks { diff --git a/tools/common/IToolLog.cs b/tools/common/IToolLog.cs index 1bc5a9c41d31..758cb136eaf4 100644 --- a/tools/common/IToolLog.cs +++ b/tools/common/IToolLog.cs @@ -1,7 +1,10 @@ +using Xamarin.Utils; + namespace Xamarin.Bundler; public interface IToolLog { int Verbosity { get; } + ApplePlatform Platform { get; } void Log (string message); void LogError (string message); // Log an error we raise ourselves (through an exception) @@ -45,6 +48,8 @@ public class ConsoleLog : IToolLog { public int Verbosity { get => verbosity; } + public ApplePlatform Platform => ApplePlatform.None; + public void Log (string message) { Console.WriteLine (message); diff --git a/tools/common/Optimizations.cs b/tools/common/Optimizations.cs index 992d36ee118a..d243a9b39415 100644 --- a/tools/common/Optimizations.cs +++ b/tools/common/Optimizations.cs @@ -9,7 +9,7 @@ namespace Xamarin.Bundler { public class Optimizations { - static string [] opt_names = + static readonly string [] opt_names = { "remove-uithread-checks", "dead-code-elimination", @@ -31,7 +31,7 @@ public class Optimizations { "redirect-class-handles", }; - static ApplePlatform [] [] valid_platforms = new ApplePlatform [] [] { + static readonly ApplePlatform [] [] valid_platforms = new ApplePlatform [] [] { /* Opt.RemoveUIThreadChecks */ new ApplePlatform [] { ApplePlatform.iOS, ApplePlatform.MacOSX, ApplePlatform.TVOS, ApplePlatform.MacCatalyst }, /* Opt.DeadCodeElimination */ new ApplePlatform [] { ApplePlatform.iOS, ApplePlatform.MacOSX, ApplePlatform.TVOS, ApplePlatform.MacCatalyst }, /* Opt.InlineIsDirectBinding */ new ApplePlatform [] { ApplePlatform.iOS, ApplePlatform.MacOSX, ApplePlatform.TVOS, ApplePlatform.MacCatalyst }, diff --git a/tools/common/StaticRegistrar.cs b/tools/common/StaticRegistrar.cs index be257833d956..f6b55ec0a015 100644 --- a/tools/common/StaticRegistrar.cs +++ b/tools/common/StaticRegistrar.cs @@ -2686,7 +2686,7 @@ List GetAllTypes (List exceptions) if (!string.IsNullOrEmpty (single_assembly) && single_assembly != @class.Type.Module.Assembly.Name.Name) continue; - if (Driver.XcodeVersion.Major >= 15) { + if (App.XcodeVersion.Major >= 15) { if (@class.Type.Is ("PassKit", "PKDisbursementAuthorizationControllerDelegate") || @class.Type.Is ("PassKit", "IPKDisbursementAuthorizationControllerDelegate")) { exceptions.Add (ErrorHelper.CreateWarning (4189, $"The class '{@class.Type.FullName}' will not be registered it has been removed from the {App.Platform} SDK.")); continue; diff --git a/tools/common/StringUtils.cs b/tools/common/StringUtils.cs index 01dadf374af7..d2470f08fdae 100644 --- a/tools/common/StringUtils.cs +++ b/tools/common/StringUtils.cs @@ -17,9 +17,9 @@ static StringUtils () shellQuoteChar = '\''; // !Windows } - static char shellQuoteChar; - static char [] mustQuoteCharacters = new char [] { ' ', '\'', ',', '$', '\\' }; - static char [] mustQuoteCharactersProcess = { ' ', '\\', '"', '\'' }; + static readonly char shellQuoteChar; + static readonly char [] mustQuoteCharacters = new char [] { ' ', '\'', ',', '$', '\\' }; + static readonly char [] mustQuoteCharactersProcess = { ' ', '\\', '"', '\'' }; [return: NotNullIfNotNull (nameof (array))] public static string []? Quote (params string [] array) diff --git a/tools/common/cache.cs b/tools/common/cache.cs index 1dcba0073ef9..1a7e607f1630 100644 --- a/tools/common/cache.cs +++ b/tools/common/cache.cs @@ -74,13 +74,8 @@ public void Clean (IToolLog log) Directory.CreateDirectory (location); } - public static bool CompareFiles (IToolLog log, string a, string b, bool ignore_cache = false) + public static bool CompareFiles (IToolLog log, string a, string b) { - if (Driver.Force && !ignore_cache) { - log.Log (6, "Files {0} and {1} are considered different because -f was passed to " + NAME + ".", a, b); - return false; - } - if (!File.Exists (b)) { log.Log (6, "Files {0} and {1} are considered different because the latter doesn't exist.", a, b); return false; @@ -90,20 +85,15 @@ public static bool CompareFiles (IToolLog log, string a, string b, bool ignore_c using (var bstream = new FileStream (b, FileMode.Open, FileAccess.Read, FileShare.Read)) { bool rv; log.Log (6, "Comparing files {0} and {1}...", a, b); - rv = CompareStreams (log, astream, bstream, ignore_cache); + rv = CompareStreams (log, astream, bstream); log.Log (6, " > {0}", rv ? "Identical" : "Different"); return rv; } } } - public unsafe static bool CompareStreams (IToolLog log, Stream astream, Stream bstream, bool ignore_cache = false) + public unsafe static bool CompareStreams (IToolLog log, Stream astream, Stream bstream) { - if (Driver.Force && !ignore_cache) { - log.Log (6, " > streams are considered different because -f was passed to " + NAME + "."); - return false; - } - if (astream.Length != bstream.Length) { log.Log (6, " > streams are considered different because their lengths do not match."); return false; diff --git a/tools/common/error.cs b/tools/common/error.cs index d61c1f80f6eb..626a112453e5 100644 --- a/tools/common/error.cs +++ b/tools/common/error.cs @@ -9,8 +9,6 @@ namespace Xamarin.Bundler { public class ProductException : Exception { - public static string Prefix => ErrorHelper.Prefix; - public ProductException (int code, string message) : this (code, false, message) { @@ -49,22 +47,37 @@ public ProductException (int code, bool error, Exception? innerException, string public int Code { get; private set; } - public bool Error { get; private set; } + bool Warning { get; set; } + + public bool IsError (IToolLog? log) + { + if (!Warning) + return true; + if (log is null) + return false; + return ErrorHelper.GetWarningLevel (log, Code) == ErrorHelper.WarningLevel.Error; + } void SetValues (int code, bool error) { Code = code; - Error = error || ErrorHelper.GetWarningLevel (code) == ErrorHelper.WarningLevel.Error; + Warning = !error; } // http://blogs.msdn.com/b/msbuild/archive/2006/11/03/msbuild-visual-studio-aware-error-messages-and-message-formats.aspx public override string ToString () + { + return ToString (null); + } + + // http://blogs.msdn.com/b/msbuild/archive/2006/11/03/msbuild-visual-studio-aware-error-messages-and-message-formats.aspx + public string ToString (IToolLog? log) { var sb = new StringBuilder (); if (!String.IsNullOrEmpty (FileName)) sb.Append (FileName).Append ('(').Append (LineNumber).Append ("): "); - sb.Append (Error ? "error" : "warning").Append (' ').Append (Prefix).Append (Code.ToString ("0000: ")).Append (Message); + sb.Append (IsError (log) ? "error" : "warning").Append (' ').Append (ErrorHelper.GetPrefix (log)).Append (Code.ToString ("0000: ")).Append (Message); return sb.ToString (); } diff --git a/tools/dotnet-linker/BackingFieldDelayHandler.cs b/tools/dotnet-linker/BackingFieldDelayHandler.cs index c77ad969e721..9a61786f7c0d 100644 --- a/tools/dotnet-linker/BackingFieldDelayHandler.cs +++ b/tools/dotnet-linker/BackingFieldDelayHandler.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Linker; @@ -38,7 +39,7 @@ public override void Initialize (LinkContext context, MarkContext markContext) } // cache `Dispose` body of optimization NSObject subclasses - static Dictionary dispose = new (); + static ConditionalWeakTable> disposes = new (); protected override void Process (MethodDefinition method) { @@ -54,7 +55,7 @@ protected override void Process (MethodDefinition method) return; // keep original for later (if needed) - dispose.Add (method, method.Body); + disposes.GetOrCreateValue (LinkContext).Add (method, method.Body); // setting body to null will only cause it to be reloaded again // same if we don't get a new IL processor @@ -68,6 +69,9 @@ protected override void Process (MethodDefinition method) public static void ReapplyDisposedFields (DerivedLinkContext context, string operation) { // note: all methods in the dictionary are marked (since they were added from an IMarkHandler) + if (!disposes.TryGetValue (context, out var dispose)) + return; + var app = context.App; foreach ((var method, var body) in dispose) { foreach (var ins in body.Instructions) { diff --git a/tools/dotnet-linker/LinkerConfiguration.cs b/tools/dotnet-linker/LinkerConfiguration.cs index a7d9909785ed..e9375514f12c 100644 --- a/tools/dotnet-linker/LinkerConfiguration.cs +++ b/tools/dotnet-linker/LinkerConfiguration.cs @@ -276,7 +276,7 @@ public static LinkerConfiguration GetInstance (LinkContext context) break; case "NoWarn": try { - ErrorHelper.ParseWarningLevel (ErrorHelper.WarningLevel.Disable, value); + ErrorHelper.ParseWarningLevel (Application, ErrorHelper.WarningLevel.Disable, value); } catch (Exception ex) { throw new InvalidOperationException ($"Invalid WarnAsError '{value}' in {linker_file}", ex); } @@ -357,11 +357,11 @@ public static LinkerConfiguration GetInstance (LinkContext context) Application.RuntimeConfigurationFile = value; break; case "SdkDevPath": - Driver.SdkRoot = value; + Application.SdkRoot = value; break; case "SdkRootDirectory": SdkRootDirectory = value; - Driver.SetFrameworkCurrentDirectory (value); + Application.FrameworkCurrentDirectory = value; break; case "SdkVersion": if (!Version.TryParse (value, out var sdk_version)) @@ -380,7 +380,7 @@ public static LinkerConfiguration GetInstance (LinkContext context) case "TargetFramework": if (!TargetFramework.TryParse (value, out var tf)) throw new InvalidOperationException ($"Invalid TargetFramework '{value}' in {linker_file}"); - Driver.TargetFramework = TargetFramework.Parse (value); + Application.TargetFramework = TargetFramework.Parse (value); break; case "TypeMapAssemblyName": Application.TypeMapAssemblyName = value; @@ -401,14 +401,14 @@ public static LinkerConfiguration GetInstance (LinkContext context) break; case "Warn": try { - ErrorHelper.ParseWarningLevel (ErrorHelper.WarningLevel.Warning, value); + ErrorHelper.ParseWarningLevel (Application, ErrorHelper.WarningLevel.Warning, value); } catch (Exception ex) { throw new InvalidOperationException ($"Invalid Warn '{value}' in {linker_file}", ex); } break; case "WarnAsError": try { - ErrorHelper.ParseWarningLevel (ErrorHelper.WarningLevel.Error, value); + ErrorHelper.ParseWarningLevel (Application, ErrorHelper.WarningLevel.Error, value); } catch (Exception ex) { throw new InvalidOperationException ($"Invalid WarnAsError '{value}' in {linker_file}", ex); } @@ -432,8 +432,6 @@ public static LinkerConfiguration GetInstance (LinkContext context) } } - ErrorHelper.Platform = Platform; - // Optimizations.Parse can only be called after setting ErrorHelper.Platform if (!StringUtils.IsNullOrEmpty (user_optimize_flags)) { var messages = new List (); @@ -468,8 +466,8 @@ public static LinkerConfiguration GetInstance (LinkContext context) break; } - if (Driver.TargetFramework.Platform != Platform) - throw ErrorHelper.CreateError (99, "Inconsistent platforms. TargetFramework={0}, Platform={1}", Driver.TargetFramework.Platform, Platform); + if (Application.TargetFramework.Platform != Platform) + throw ErrorHelper.CreateError (99, "Inconsistent platforms. TargetFramework={0}, Platform={1}", Application.TargetFramework.Platform, Platform); if (Application.XamarinRuntime != XamarinRuntime.MonoVM && Application.UseInterpreter) { Application.Log (4, "The interpreter is enabled, but the current runtime isn't MonoVM. The interpreter settings will be ignored."); @@ -574,7 +572,7 @@ public void Write () Application.Log ($" Registrar: {Application.Registrar} (Options: {Application.RegistrarOptions})"); Application.Log ($" RuntimeConfigurationFile: {Application.RuntimeConfigurationFile}"); Application.Log ($" RequirePInvokeWrappers: {Application.RequiresPInvokeWrappers}"); - Application.Log ($" SdkDevPath: {Driver.SdkRoot}"); + Application.Log ($" SdkDevPath: {Application.SdkRoot}"); Application.Log ($" SdkRootDirectory: {SdkRootDirectory}"); Application.Log ($" SdkVersion: {SdkVersion}"); Application.Log ($" TypeMapAssemblyName: {Application.TypeMapAssemblyName}"); @@ -636,15 +634,23 @@ public static void Report (LinkContext context, IList exceptions) // Since we print using a standard message format, msbuild will parse those error messages and show // them as msbuild errors. var list = ErrorHelper.CollectExceptions (exceptions); - var allWarnings = list.All (v => v is ProductException pe && !pe.Error); + if (!TryGetInstance (context, out var instance)) { + // Something went very wrong. Just dump out everything. + context.LogMessage (MessageContainer.CreateCustomErrorMessage ("No linker configuration available.", 7000)); + foreach (var exception in exceptions) { + context.LogMessage (MessageContainer.CreateCustomErrorMessage (exception.ToString (), 7000)); + } + return; + } + + var allWarnings = list.All (v => v is ProductException pe && !pe.IsError (instance.Application)); if (!allWarnings) { - TryGetInstance (context, out var instance); - var platform = (instance?.Platform)?.ToString () ?? "unknown"; + var platform = instance.Platform.ToString (); var msg = MessageContainer.CreateCustomErrorMessage (Errors.MX7000 /* An error occurred while executing the custom linker steps. Please review the build log for more information. */, 7000, platform); context.LogMessage (msg); } // ErrorHelper.Show will print our errors and warnings to stderr. - ErrorHelper.Show (ConsoleLog.Instance, list); + ErrorHelper.Show (instance.Application, list); } public IEnumerable GetNonDeletedAssemblies (BaseStep step) diff --git a/tools/dotnet-linker/SetupStep.cs b/tools/dotnet-linker/SetupStep.cs index 3d1ecf296cf3..a6fb4461aef7 100644 --- a/tools/dotnet-linker/SetupStep.cs +++ b/tools/dotnet-linker/SetupStep.cs @@ -22,7 +22,6 @@ public class SetupStep : ConfigurationAwareStep { protected override void TryProcess () { Configuration.Write (); - ErrorHelper.Platform = Configuration.Platform; Directory.CreateDirectory (Configuration.ItemsDirectory); Directory.CreateDirectory (Configuration.CacheDirectory); } diff --git a/tools/dotnet-linker/Steps/ConfigurationAwareStep.cs b/tools/dotnet-linker/Steps/ConfigurationAwareStep.cs index 56423d905bff..7c5eb5ca3775 100644 --- a/tools/dotnet-linker/Steps/ConfigurationAwareStep.cs +++ b/tools/dotnet-linker/Steps/ConfigurationAwareStep.cs @@ -136,7 +136,7 @@ Exception [] CollectExceptions (Exception e, Func createExcept // If we're only reporting warnings, then don't add the step-specific exception at all. if (CollectProductExceptions (e, out var productExceptions)) { // don't add inner exception - if (productExceptions.Any (v => v.Error)) { + if (productExceptions.Any (v => v.IsError (App))) { var ex = createException (); // instead return an aggregate exception with the original exception and all the ProductExceptions we're reporting. productExceptions.Add (ex); diff --git a/tools/dotnet-linker/Steps/TrimmableRegistrarStep.cs b/tools/dotnet-linker/Steps/TrimmableRegistrarStep.cs index cf3f544bc5b6..e494cec5a972 100644 --- a/tools/dotnet-linker/Steps/TrimmableRegistrarStep.cs +++ b/tools/dotnet-linker/Steps/TrimmableRegistrarStep.cs @@ -47,7 +47,7 @@ AssemblyDefinition CreateTypeMapRootAssembly (ModuleParameters moduleParameters, AssemblyDefinition rootTypeMapAssembly; // .NET 10 doesn't support a separate root type map assembly, so we have to add these attributes to the entry assembly instead. - var useEntryAssemblyAsRootTypeMapAssembly = Driver.TargetFramework.Version.Major <= 10; + var useEntryAssemblyAsRootTypeMapAssembly = App.TargetFramework.Version.Major <= 10; if (useEntryAssemblyAsRootTypeMapAssembly) { rootTypeMapAssembly = Configuration.EntryAssembly; diff --git a/tools/linker/CoreTypeMapStep.cs b/tools/linker/CoreTypeMapStep.cs index 76fd37c49770..c180cf3b43fc 100644 --- a/tools/linker/CoreTypeMapStep.cs +++ b/tools/linker/CoreTypeMapStep.cs @@ -167,7 +167,7 @@ bool IsWrapperType (TypeDefinition type) // Cache the results of the IsCIFilter check in a dictionary. It makes this method slightly faster // (total time spent in IsCIFilter when linking monotouch-test went from 11 ms to 3ms). - static Dictionary ci_filter_types = new Dictionary (); + Dictionary ci_filter_types = new Dictionary (); bool IsCIFilter (TypeReference type) { if (type is null) diff --git a/tools/mtouch/AssemblyResolver.cs b/tools/mtouch/AssemblyResolver.cs index e5386afd5df5..3b2f94d2d865 100644 --- a/tools/mtouch/AssemblyResolver.cs +++ b/tools/mtouch/AssemblyResolver.cs @@ -25,7 +25,7 @@ namespace MonoTouch.Tuner { // recent cecil removed some overloads - https://github.com/mono/cecil/commit/42db79cc16f1cbe8dbab558904e188352dba2b41 public static class AssemblyResolverRocks { - static ReaderParameters defaults = new ReaderParameters (); + static readonly ReaderParameters defaults = new ReaderParameters (); public static AssemblyDefinition Resolve (this IAssemblyResolver self, string fullName) { From c102699a9c27f68d8c0fde7ad8c219958e62d86b Mon Sep 17 00:00:00 2001 From: "CSIGS@microsoft.com" Date: Thu, 4 Jun 2026 02:08:29 -0700 Subject: [PATCH 68/79] LEGO: Pull request from lego/hb_5df43909-4a19-4f55-bc3f-9ea8fccf3c82_20260603055155830 to main (#25626) LEGO: Pull request from lego/hb_5df43909-4a19-4f55-bc3f-9ea8fccf3c82_20260603055155830 to main with localized lcls --- .../MSBStrings.resx.lcl | 27 +++++++++++++++++++ .../MSBStrings.resx.lcl | 27 +++++++++++++++++++ .../MSBStrings.resx.lcl | 27 +++++++++++++++++++ .../MSBStrings.resx.lcl | 27 +++++++++++++++++++ .../MSBStrings.resx.lcl | 27 +++++++++++++++++++ .../MSBStrings.resx.lcl | 27 +++++++++++++++++++ 6 files changed, 162 insertions(+) diff --git a/macios/Localize/loc/cs/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/cs/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index 44c49d101321..e7c0532070d5 100644 --- a/macios/Localize/loc/cs/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/cs/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -2734,6 +2734,24 @@ + + + Components).]]> + + Komponenty).]]> + + + + + + + Components).]]> + + Komponenty).]]> + + + + @@ -3661,6 +3679,15 @@ + + + + + + + + + diff --git a/macios/Localize/loc/de/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/de/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index 564e87794819..10b33895439f 100644 --- a/macios/Localize/loc/de/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/de/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -2734,6 +2734,24 @@ + + + Components).]]> + + Komponenten) ausführen.]]> + + + + + + + Components).]]> + + Komponenten) ausführen.]]> + + + + @@ -3661,6 +3679,15 @@ + + + + + + + + + diff --git a/macios/Localize/loc/es/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/es/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index b692e401e9c4..32a33885ce1a 100644 --- a/macios/Localize/loc/es/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/es/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -2734,6 +2734,24 @@ + + + Components).]]> + + Componentes).]]> + + + + + + + Components).]]> + + Componentes).]]> + + + + @@ -3661,6 +3679,15 @@ + + + + + + + + + diff --git a/macios/Localize/loc/ko/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/ko/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index 8f6a9309a649..68a284939316 100644 --- a/macios/Localize/loc/ko/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/ko/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -2734,6 +2734,24 @@ + + + Components).]]> + + 구성 요소)에서 'xcodebuild -downloadPlatform {0}'을 실행하여 설치합니다.]]> + + + + + + + Components).]]> + + 구성 요소)에서 'xcodebuild -downloadPlatform {0}'을 실행하여 시뮬레이터 런타임을 업데이트합니다.]]> + + + + @@ -3661,6 +3679,15 @@ + + + + + + + + + diff --git a/macios/Localize/loc/pt-BR/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/pt-BR/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index b36703f288b6..503593408653 100644 --- a/macios/Localize/loc/pt-BR/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/pt-BR/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -2734,6 +2734,24 @@ + + + Components).]]> + + Componentes).]]> + + + + + + + Components).]]> + + Componentes).]]> + + + + @@ -3661,6 +3679,15 @@ + + + + + + + + + diff --git a/macios/Localize/loc/zh-Hant/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl b/macios/Localize/loc/zh-Hant/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl index 0c5e6cd287eb..0f9913fdbb09 100644 --- a/macios/Localize/loc/zh-Hant/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl +++ b/macios/Localize/loc/zh-Hant/macios/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx.lcl @@ -2734,6 +2734,24 @@ + + + Components).]]> + + 元件]來安裝。]]> + + + + + + + Components).]]> + + 元件]來更新模擬器執行階段。]]> + + + + @@ -3661,6 +3679,15 @@ + + + + + + + + + From e7e179532c76ce40ce1c8bcf12b1d9117cc4ab6e Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 4 Jun 2026 14:54:45 +0200 Subject: [PATCH 69/79] [tests] Ignore any tests using CFNetworkHandler. Fixes #25634. (#25635) There are known issues (deadlocks) with CFNetworkHandler: https://github.com/dotnet/macios/issues/25634 CFNetworkHandler is obsolete, so we won't fix any such issues, so to avoid deadlocks, just avoid testing CFNetworkHandler. Fixes https://github.com/dotnet/macios/issues/25634. --- tests/monotouch-test/HttpClient/HttpClientTest.cs | 4 +++- tests/monotouch-test/System.Net.Http/MessageHandlers.cs | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/monotouch-test/HttpClient/HttpClientTest.cs b/tests/monotouch-test/HttpClient/HttpClientTest.cs index ba17c5427721..77efe890d8fb 100644 --- a/tests/monotouch-test/HttpClient/HttpClientTest.cs +++ b/tests/monotouch-test/HttpClient/HttpClientTest.cs @@ -80,7 +80,9 @@ public static IHandlerWrapper GetWrapper (Type handlerType) } [TestCase (typeof (HttpClientHandler), 8)] - [TestCase (typeof (CFNetworkHandler), 8)] + // There are known issues (deadlocks) with CFNetworkHandler: https://github.com/dotnet/macios/issues/25634 + // CFNetworkHandler is obsolete, so we won't fix any such issues, so to avoid deadlocks, just avoid testing CFNetworkHandler. + [TestCase (typeof (CFNetworkHandler), 8, Ignore = "There are known issues (deadlocks) with CFNetworkHandler: https://github.com/dotnet/macios/issues/25634")] [TestCase (typeof (NSUrlSessionHandler), 9)] public void EnsureModifiabilityPostSend (Type handlerType, int macOSMinVersion) { diff --git a/tests/monotouch-test/System.Net.Http/MessageHandlers.cs b/tests/monotouch-test/System.Net.Http/MessageHandlers.cs index b159fab8c8c5..6bf154dc0934 100644 --- a/tests/monotouch-test/System.Net.Http/MessageHandlers.cs +++ b/tests/monotouch-test/System.Net.Http/MessageHandlers.cs @@ -52,7 +52,9 @@ HttpMessageHandler GetHandler (Type handler_type) [Test] [TestCase (typeof (HttpClientHandler))] - [TestCase (typeof (CFNetworkHandler))] + // There are known issues (deadlocks) with CFNetworkHandler: https://github.com/dotnet/macios/issues/25634 + // CFNetworkHandler is obsolete, so we won't fix any such issues, so to avoid deadlocks, just avoid testing CFNetworkHandler. + [TestCase (typeof (CFNetworkHandler), Ignore = "There are known issues (deadlocks) with CFNetworkHandler: https://github.com/dotnet/macios/issues/25634")] [TestCase (typeof (SocketsHttpHandler))] [TestCase (typeof (NSUrlSessionHandler))] public void DnsFailure (Type handlerType) @@ -438,7 +440,9 @@ public void TestNSUrlSessionTimeoutExceptionWhileStreamingContent () // ensure that if we have a redirect, we do not have the auth headers in the following requests [TestCase (typeof (HttpClientHandler))] - [TestCase (typeof (CFNetworkHandler))] + // There are known issues (deadlocks) with CFNetworkHandler: https://github.com/dotnet/macios/issues/25634 + // CFNetworkHandler is obsolete, so we won't fix any such issues, so to avoid deadlocks, just avoid testing CFNetworkHandler. + [TestCase (typeof (CFNetworkHandler), Ignore = "There are known issues (deadlocks) with CFNetworkHandler: https://github.com/dotnet/macios/issues/25634")] [TestCase (typeof (NSUrlSessionHandler))] public void RedirectionWithAuthorizationHeaders (Type handlerType) { From 063c4ad515ec26820053d67b25ef63d26500a384 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 4 Jun 2026 14:55:23 +0200 Subject: [PATCH 70/79] [tests] Don't strip symbols on any platform. (#25643) `MtouchNoSymbolStrip` doesn't do anything on macOS, so use `NoSymbolStrip` instead. --- tests/monotouch-test/dotnet/shared.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/monotouch-test/dotnet/shared.csproj b/tests/monotouch-test/dotnet/shared.csproj index 989a89db0bef..ecb61eeb4914 100644 --- a/tests/monotouch-test/dotnet/shared.csproj +++ b/tests/monotouch-test/dotnet/shared.csproj @@ -13,7 +13,7 @@ $(RootTestsDirectory)\monotouch-test - true + true $(DefineConstants);DEBUG From ac159468375779e75d07ca0a41eeb414b838304c Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Jun 2026 12:05:37 -0400 Subject: [PATCH 71/79] [github] Increase Code Radiator safe-output patch size limit (#25642) Code Radiator failed when large merge PRs exceeded the default 1024 KB safe-output patch limit, causing PR creation to fall back to an issue. This updates the workflow to allow substantially larger generated patches. - **Workflow source** - Raise the top-level `safe-outputs.max-patch-size` in `code-radiator.md` from the default to `10240` KB. - **Compiled workflow** - Update the generated safe-output config in `code-radiator.lock.yml` so both PR creation and PR branch updates use the new `10240` KB limit. - **Result** - Large inter-branch merge patches can proceed through `create_pull_request` instead of being rejected by the safe-output size guard. ```yaml safe-outputs: max-patch-files: 1000 max-patch-size: 10240 ``` --------- Co-authored-by: rolfbjarne <249268+rolfbjarne@users.noreply.github.com> --- .github/workflows/code-radiator.lock.yml | 34 ++++++++++++------------ .github/workflows/code-radiator.md | 1 + 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.github/workflows/code-radiator.lock.yml b/.github/workflows/code-radiator.lock.yml index 76736b1d9f98..2f6344841183 100644 --- a/.github/workflows/code-radiator.lock.yml +++ b/.github/workflows/code-radiator.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"1ba69a47e6cfbd91cfe78d637aac47fa364d969c56a89ac3523d5c936169f250","body_hash":"b966744ea05e67fd471e10231f2cce01d0525af33bc90ff317655344e889cc92","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.5"} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"3f1a9ee34482a7876690cddd0a715bb51cd8db1f78e741ef5d16c383db9b0508","body_hash":"b966744ea05e67fd471e10231f2cce01d0525af33bc90ff317655344e889cc92","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.5"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"3ea13c02d765410340d533515cb31a7eef2baaf0","version":"v0.77.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.58"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.22"},{"image":"ghcr.io/github/github-mcp-server:v1.1.0"},{"image":"node:lts-alpine","digest":"sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14","pinned_image":"node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14"}]} # ___ _ _ # / _ \ | | (_) @@ -192,24 +192,24 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_686c839083e52610_EOF' + cat << 'GH_AW_PROMPT_78cbc6c89dc649e4_EOF' - GH_AW_PROMPT_686c839083e52610_EOF + GH_AW_PROMPT_78cbc6c89dc649e4_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_686c839083e52610_EOF' + cat << 'GH_AW_PROMPT_78cbc6c89dc649e4_EOF' Tools: add_comment(max:10), create_pull_request(max:10), update_pull_request(max:10), add_labels(max:10), push_to_pull_request_branch(max:10), missing_tool, missing_data, noop - GH_AW_PROMPT_686c839083e52610_EOF + GH_AW_PROMPT_78cbc6c89dc649e4_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_push_to_pr_branch.md" - cat << 'GH_AW_PROMPT_686c839083e52610_EOF' + cat << 'GH_AW_PROMPT_78cbc6c89dc649e4_EOF' - GH_AW_PROMPT_686c839083e52610_EOF + GH_AW_PROMPT_78cbc6c89dc649e4_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_686c839083e52610_EOF' + cat << 'GH_AW_PROMPT_78cbc6c89dc649e4_EOF' The following GitHub context information is available for this workflow: {{#if github.actor}} @@ -241,12 +241,12 @@ jobs: - **Note**: If a branch you need is not in the list above and is not listed as an additional fetched ref, it has NOT been checked out. For private repositories you cannot fetch it without proper authentication. If the branch is required and not available, exit with an error and ask the user to add it to the `fetch:` option of the `checkout:` configuration (e.g., `fetch: ["refs/pulls/open/*"]` for all open PR refs, or `fetch: ["main", "feature/my-branch"]` for specific branches). - GH_AW_PROMPT_686c839083e52610_EOF + GH_AW_PROMPT_78cbc6c89dc649e4_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_686c839083e52610_EOF' + cat << 'GH_AW_PROMPT_78cbc6c89dc649e4_EOF' {{#runtime-import .github/workflows/code-radiator.md}} - GH_AW_PROMPT_686c839083e52610_EOF + GH_AW_PROMPT_78cbc6c89dc649e4_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -461,9 +461,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_9a64ac31bc43ee2c_EOF' - {"add_comment":{"max":10,"target":"*"},"add_labels":{"max":10,"target":"*"},"create_pull_request":{"allowed_base_branches":["net*.0","xcode*","xcode*.*"],"max":10,"max_patch_files":1000,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"protected_files_policy":"request_review","signed_commits":false},"create_report_incomplete_issue":{},"merge_pull_request":{"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_to_pull_request_branch":{"if_no_changes":"warn","max":10,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"signed_commits":false,"target":"*","title_prefix":"🤖 Merge 'main' =\u003e '"},"report_incomplete":{},"update_pull_request":{"allow_body":true,"allow_title":true,"max":10,"update_branch":false}} - GH_AW_SAFE_OUTPUTS_CONFIG_9a64ac31bc43ee2c_EOF + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_1667342258ba4bbd_EOF' + {"add_comment":{"max":10,"target":"*"},"add_labels":{"max":10,"target":"*"},"create_pull_request":{"allowed_base_branches":["net*.0","xcode*","xcode*.*"],"max":10,"max_patch_files":1000,"max_patch_size":10240,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"protected_files_policy":"request_review","signed_commits":false},"create_report_incomplete_issue":{},"merge_pull_request":{"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_to_pull_request_branch":{"if_no_changes":"warn","max":10,"max_patch_size":10240,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"signed_commits":false,"target":"*","title_prefix":"🤖 Merge 'main' =\u003e '"},"report_incomplete":{},"update_pull_request":{"allow_body":true,"allow_title":true,"max":10,"update_branch":false}} + GH_AW_SAFE_OUTPUTS_CONFIG_1667342258ba4bbd_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -806,7 +806,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_ecdd849dd0c87e2d_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_b4c6a644a0b8a10f_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -850,7 +850,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_ecdd849dd0c87e2d_EOF + GH_AW_MCP_CONFIG_b4c6a644a0b8a10f_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1594,7 +1594,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10,\"target\":\"*\"},\"add_labels\":{\"max\":10,\"target\":\"*\"},\"create_pull_request\":{\"allowed_base_branches\":[\"net*.0\",\"xcode*\",\"xcode*.*\"],\"max\":10,\"max_patch_files\":1000,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"protected_files_policy\":\"request_review\",\"signed_commits\":false},\"create_report_incomplete_issue\":{},\"merge_pull_request\":{\"max\":10},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max\":10,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"signed_commits\":false,\"target\":\"*\",\"title_prefix\":\"🤖 Merge 'main' =\\u003e '\"},\"report_incomplete\":{},\"update_pull_request\":{\"allow_body\":true,\"allow_title\":true,\"max\":10,\"update_branch\":false}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10,\"target\":\"*\"},\"add_labels\":{\"max\":10,\"target\":\"*\"},\"create_pull_request\":{\"allowed_base_branches\":[\"net*.0\",\"xcode*\",\"xcode*.*\"],\"max\":10,\"max_patch_files\":1000,\"max_patch_size\":10240,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"protected_files_policy\":\"request_review\",\"signed_commits\":false},\"create_report_incomplete_issue\":{},\"merge_pull_request\":{\"max\":10},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max\":10,\"max_patch_size\":10240,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"signed_commits\":false,\"target\":\"*\",\"title_prefix\":\"🤖 Merge 'main' =\\u003e '\"},\"report_incomplete\":{},\"update_pull_request\":{\"allow_body\":true,\"allow_title\":true,\"max\":10,\"update_branch\":false}}" GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }} with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/code-radiator.md b/.github/workflows/code-radiator.md index e1fe86d0d97d..89fd81f9bdef 100644 --- a/.github/workflows/code-radiator.md +++ b/.github/workflows/code-radiator.md @@ -26,6 +26,7 @@ checkout: fetch-depth: 0 safe-outputs: max-patch-files: 1000 + max-patch-size: 10240 create-pull-request: max: 10 signed-commits: false From dc9651151bc2d439dc17261589ed8274e4d1c71c Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Jun 2026 12:06:18 -0400 Subject: [PATCH 72/79] [github] Disable weekly schedule trigger for CI Post-Mortem Analysis workflow (#25602) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CI Post-Mortem workflow was failing on its weekly schedule because it requires authenticated Azure DevOps access that isn't available in the scheduled runner environment. Disabling the schedule so the workflow can only be triggered manually (`workflow_dispatch`). ## Changes - **`.github/workflows/ci-postmortem.md`** — removed the `schedule` trigger from the frontmatter `on:` block - **`.github/workflows/ci-postmortem.lock.yml`** — recompiled from updated source --------- Co-authored-by: rolfbjarne <249268+rolfbjarne@users.noreply.github.com> --- .github/workflows/ci-postmortem.lock.yml | 32 ++++++++++-------------- .github/workflows/ci-postmortem.md | 2 -- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci-postmortem.lock.yml b/.github/workflows/ci-postmortem.lock.yml index 58d2f385a005..61755f20a0a7 100644 --- a/.github/workflows/ci-postmortem.lock.yml +++ b/.github/workflows/ci-postmortem.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"15354af11629eb0049ecb70f03b13ed2df90af330f1d6a40d7ce9a202538bb0b","body_hash":"ad13b1075fd08c989fe77b74abdce355d88ab2ebeea692809a67d8b68cd7559b","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.5"} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"e7f2b78dc23c58554e0403a91ececfda7a6869936e0f622236e8b581313a38a7","body_hash":"ad13b1075fd08c989fe77b74abdce355d88ab2ebeea692809a67d8b68cd7559b","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.5"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"3ea13c02d765410340d533515cb31a7eef2baaf0","version":"v0.77.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.58"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.22"},{"image":"ghcr.io/github/github-mcp-server:v1.1.0"},{"image":"node:lts-alpine","digest":"sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14","pinned_image":"node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14"}]} # ___ _ _ # / _ \ | | (_) @@ -47,9 +47,6 @@ name: "CI Post-Mortem Analysis" on: - schedule: - - cron: "11 2 * * 0" - # Friendly format: weekly on sunday (scattered) workflow_dispatch: inputs: aw_context: @@ -186,20 +183,20 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_956c986f1e45d6a0_EOF' + cat << 'GH_AW_PROMPT_452cd77d855884c0_EOF' - GH_AW_PROMPT_956c986f1e45d6a0_EOF + GH_AW_PROMPT_452cd77d855884c0_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_956c986f1e45d6a0_EOF' + cat << 'GH_AW_PROMPT_452cd77d855884c0_EOF' Tools: add_comment(max:20), create_issue(max:20), update_issue(max:20), missing_tool, missing_data, noop - GH_AW_PROMPT_956c986f1e45d6a0_EOF + GH_AW_PROMPT_452cd77d855884c0_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_956c986f1e45d6a0_EOF' + cat << 'GH_AW_PROMPT_452cd77d855884c0_EOF' The following GitHub context information is available for this workflow: {{#if github.actor}} @@ -228,12 +225,12 @@ jobs: {{/if}} - GH_AW_PROMPT_956c986f1e45d6a0_EOF + GH_AW_PROMPT_452cd77d855884c0_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_956c986f1e45d6a0_EOF' + cat << 'GH_AW_PROMPT_452cd77d855884c0_EOF' {{#runtime-import .github/workflows/ci-postmortem.md}} - GH_AW_PROMPT_956c986f1e45d6a0_EOF + GH_AW_PROMPT_452cd77d855884c0_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -316,9 +313,6 @@ jobs: permissions: contents: read issues: read - concurrency: - group: "gh-aw-copilot-${{ github.workflow }}" - queue: max env: DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} GH_AW_ASSETS_ALLOWED_EXTS: "" @@ -441,9 +435,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_c2d474e65378a915_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_418d6ffb54599ea1_EOF' {"add_comment":{"max":20},"create_issue":{"max":20},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{},"update_issue":{"allow_body":true,"max":20}} - GH_AW_SAFE_OUTPUTS_CONFIG_c2d474e65378a915_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_418d6ffb54599ea1_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -727,7 +721,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_7bd5cd6513b45b21_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_f57e5774da7cad26_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -771,7 +765,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_7bd5cd6513b45b21_EOF + GH_AW_MCP_CONFIG_f57e5774da7cad26_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true diff --git a/.github/workflows/ci-postmortem.md b/.github/workflows/ci-postmortem.md index 64065525ca6e..ae2eda3ac76c 100644 --- a/.github/workflows/ci-postmortem.md +++ b/.github/workflows/ci-postmortem.md @@ -1,7 +1,5 @@ --- on: - schedule: - - cron: "weekly on sunday" workflow_dispatch: permissions: contents: read From 2743052e0ec066d28884442dede68bfceef0076d Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 4 Jun 2026 18:10:18 +0200 Subject: [PATCH 73/79] [tools] Don't inline calls to Class.GetHandle for BrowserEngineKit/BrowserEngineCore classes. (#25629) --- tools/dotnet-linker/Steps/InlineClassGetHandleStep.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/dotnet-linker/Steps/InlineClassGetHandleStep.cs b/tools/dotnet-linker/Steps/InlineClassGetHandleStep.cs index 4cb66ed8fd27..11e8dd1996b8 100644 --- a/tools/dotnet-linker/Steps/InlineClassGetHandleStep.cs +++ b/tools/dotnet-linker/Steps/InlineClassGetHandleStep.cs @@ -215,6 +215,13 @@ bool isOurOwnCode () // UITitlebar is a weird special case, the class exists in the headers, and it's documented online, but it's not possible to link with it (not even in an Xcode project, it's not in any .tbd files). continue; } + + if (objCType.Type.Namespace == "BrowserEngineKit" || objCType.Type.Namespace == "BrowserEngineCore") { + // Most apps do not use BrowserEngineKit, and linking with it when an app is not supposed to will prevent it from getting approved in the App Store. + // So we treat these frameworks specially, where we don't link with these two frameworks by default, *even if they're detected as used*, + // which means we shouldn't inline Class.GetHandle calls for any classes in these frameworks, since native linking will fail. + continue; + } } ldstr.OpCode = OpCodes.Call; From c513ce7161f9582e050d4bc33875e8177c25b994 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 4 Jun 2026 18:11:41 +0200 Subject: [PATCH 74/79] [tests] Filter out any warnings about using deprecated locations for the Xcode location. (#25628) These warnings are just noise for us at this point. Eventually we'll just stop using the deprecated locations. --------- Co-authored-by: Rolf Bjarne Kvinge --- tests/dotnet/UnitTests/BundleStructureTest.cs | 14 +++---- tests/dotnet/UnitTests/Extensions.cs | 27 +++++++++++++ tests/dotnet/UnitTests/ExtensionsTest.cs | 2 +- tests/dotnet/UnitTests/ProjectTest.cs | 38 ++++++------------- tests/dotnet/UnitTests/TemplateTest.cs | 2 +- tests/dotnet/UnitTests/TrimmerWarningsTest.cs | 6 +-- tests/dotnet/UnitTests/WindowsTest.cs | 8 ++-- tests/dotnet/UnitTests/XcodeProjectTests.cs | 2 +- 8 files changed, 52 insertions(+), 47 deletions(-) diff --git a/tests/dotnet/UnitTests/BundleStructureTest.cs b/tests/dotnet/UnitTests/BundleStructureTest.cs index d751c42dbdd6..c2cd6eab5a41 100644 --- a/tests/dotnet/UnitTests/BundleStructureTest.cs +++ b/tests/dotnet/UnitTests/BundleStructureTest.cs @@ -646,7 +646,7 @@ public void Build (ApplePlatform platform, string runtimeIdentifiers, CodeSignat properties ["Configuration"] = configuration; var rv = DotNet.AssertBuild (project_path, properties); var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).ToArray (); - var warningMessages = FilterWarnings (warnings); + var warningMessages = FilterWarnings (warnings, platform); var isReleaseBuild = string.Equals (configuration, "Release", StringComparison.OrdinalIgnoreCase); var platformString = platform.AsString (); @@ -692,7 +692,7 @@ public void Build (ApplePlatform platform, string runtimeIdentifiers, CodeSignat rv = DotNet.AssertBuild (project_path, properties); warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).ToArray (); - warningMessages = FilterWarnings (warnings); + warningMessages = FilterWarnings (warnings, platform); CheckAppBundleContents (platform, appPath, rids, signature, isReleaseBuild); Assert.That (warningMessages, Is.EqualTo (expectedWarnings), "Warnings Rebuild 1"); @@ -704,7 +704,7 @@ public void Build (ApplePlatform platform, string runtimeIdentifiers, CodeSignat rv = DotNet.AssertBuild (project_path, properties); warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).ToArray (); - warningMessages = FilterWarnings (warnings); + warningMessages = FilterWarnings (warnings, platform); CheckAppBundleContents (platform, appPath, rids, signature, isReleaseBuild); Assert.That (warningMessages, Is.EqualTo (expectedWarnings), "Warnings Rebuild 2"); @@ -713,7 +713,7 @@ public void Build (ApplePlatform platform, string runtimeIdentifiers, CodeSignat // a simple rebuild should succeed rv = DotNet.AssertBuild (project_path, properties); warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).ToArray (); - warningMessages = FilterWarnings (warnings); + warningMessages = FilterWarnings (warnings, platform); CheckAppBundleContents (platform, appPath, rids, signature, isReleaseBuild); Assert.That (warningMessages, Is.EqualTo (expectedWarnings), "Warnings Rebuild 3"); @@ -740,9 +740,9 @@ static string GetXCFrameworkArchitectures (ApplePlatform platform, string runtim } } - public static string [] FilterWarnings (IEnumerable warnings, bool canonicalizePaths = false) + public static string [] FilterWarnings (IEnumerable warnings, ApplePlatform platform, bool canonicalizePaths = false) { - return warnings + return Extensions.FilterWarnings (warnings, platform) .Select (v => v?.Message!).Where (v => !string.IsNullOrWhiteSpace (v)) // Remove warnings of the form "This call site is reachable on: '...' and later. 'TheAPI' is only supported on: '...' and later." .Where (v => !v.StartsWith ("This call site is reachable on:")) @@ -752,8 +752,6 @@ public static string [] FilterWarnings (IEnumerable warnings, boo .Where (v => !v.Contains (" is obsolete: ")) // More obsolete warnings .Where (v => !v.Contains (" overrides obsolete member ")) - // Don't care about this - .Where (v => !v.Contains ("Supported iPhone orientations have not been set")) // Canonicalize if so requested .Select (v => canonicalizePaths ? v.Replace (Path.DirectorySeparatorChar, '/') : v) // Sort the messages so that comparison against the expected array is faster diff --git a/tests/dotnet/UnitTests/Extensions.cs b/tests/dotnet/UnitTests/Extensions.cs index ba97980be9a9..249cf21297cc 100644 --- a/tests/dotnet/UnitTests/Extensions.cs +++ b/tests/dotnet/UnitTests/Extensions.cs @@ -113,6 +113,33 @@ public static void AssertWarnings (this IEnumerable actualWarning Assert.Fail ($"Missing warning: {evt.File}: {evt.Message}"); }); } + + public static IEnumerable FilterWarnings (this IEnumerable actualWarnings, ApplePlatform platform, bool filterSupportediPhoneOrientations = true, bool filterXcodeLocation = true) + { + return actualWarnings.Where (v => !IsFilteredWarning (v, platform, filterSupportediPhoneOrientations, filterXcodeLocation)); + } + + public static bool IsFilteredWarning (BuildLogEvent evt, ApplePlatform platform, bool filterSupportediPhoneOrientations = true, bool filterXcodeLocation = true) + { + var v = evt.Message?.Trim (); + + if (string.IsNullOrEmpty (v)) + return false; + + if (filterSupportediPhoneOrientations && platform == ApplePlatform.iOS && v == "Supported iPhone orientations have not been set") + return true; + + if (filterXcodeLocation) { + if (v.Contains ("The environment variable 'MD_APPLE_SDK_ROOT' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use.")) + return true; + if (v.Contains ($"The settings file '{Environment.GetEnvironmentVariable ("HOME")}/Library/Preferences/maui/Settings.plist' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use.")) + return true; + if (v.Contains ($"The settings file '{Environment.GetEnvironmentVariable ("HOME")}/Library/Preferences/Xamarin/Settings.plist' is deprecated, and will be ignored. Please use the 'DEVELOPER_DIR' environment variable or the 'XcodeLocation' MSBuild property to choose which Xcode to use.")) + return true; + } + + return false; + } } public class ExpectedBuildMessage { diff --git a/tests/dotnet/UnitTests/ExtensionsTest.cs b/tests/dotnet/UnitTests/ExtensionsTest.cs index cf50e2098e90..cba31ce61c92 100644 --- a/tests/dotnet/UnitTests/ExtensionsTest.cs +++ b/tests/dotnet/UnitTests/ExtensionsTest.cs @@ -59,7 +59,7 @@ public void AdditionalAppExtensionTest (ApplePlatform platform, string runtimeId } else { var rv = DotNet.AssertBuild (project_path, properties); var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath) - .Where (v => v?.Message?.Contains ("Supported iPhone orientations have not been set") != true) + .FilterWarnings (platform) .ToArray (); var extensionPath = Path.Combine (appPath, GetPlugInsRelativePath (platform), $"{extensionProject}.appex"); AssertWarningMessages (warnings, [ diff --git a/tests/dotnet/UnitTests/ProjectTest.cs b/tests/dotnet/UnitTests/ProjectTest.cs index 4fe2a5d3f241..8cbe51e3d86d 100644 --- a/tests/dotnet/UnitTests/ProjectTest.cs +++ b/tests/dotnet/UnitTests/ProjectTest.cs @@ -506,7 +506,7 @@ public void IsOverrideRuntimeIdentifier (ApplePlatform platform, string runtimeI properties.Remove ("RuntimeIdentifiers"); properties ["cmdline:RuntimeIdentifier"] = "maccatalyst-x64"; var rv = DotNet.AssertBuild (project_path, properties); - var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).ToArray (); + var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).FilterWarnings (platform).ToArray (); Assert.That (warnings.Length, Is.EqualTo (1), "Warning Count"); Assert.That (warnings [0].Message, Is.EqualTo ("RuntimeIdentifier was set on the command line, and will override the value for RuntimeIdentifiers set in the project file."), "Warning message"); } @@ -649,7 +649,7 @@ public void FilesInAppBundle (ApplePlatform platform, string runtimeIdentifiers) // Build again - this time it'll fail var rv = DotNet.Build (project_path, properties); - var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).ToArray (); + var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).FilterWarnings (platform).ToArray (); Assert.That (rv.ExitCode, Is.Not.EqualTo (0), "Unexpected success"); Assert.That (warnings.Length, Is.EqualTo (1), "Warning Count"); Assert.That (warnings [0].Message, Is.EqualTo ($"Found files in the root directory of the app bundle. This will likely cause codesign to fail. Files:\nbin/Debug/{Configuration.DotNetTfm}-maccatalyst/maccatalyst-x64/MySimpleApp.app/otherfile.txt\nbin/Debug/{Configuration.DotNetTfm}-maccatalyst/maccatalyst-x64/MySimpleApp.app/otherdir\nbin/Debug/{Configuration.DotNetTfm}-maccatalyst/maccatalyst-x64/MySimpleApp.app/otherdir/otherfile.log"), "Warning"); @@ -658,7 +658,7 @@ public void FilesInAppBundle (ApplePlatform platform, string runtimeIdentifiers) var enableAutomaticCleanupProperties = new Dictionary (properties); enableAutomaticCleanupProperties ["EnableAutomaticAppBundleRootDirectoryCleanup"] = "true"; rv = DotNet.AssertBuild (project_path, enableAutomaticCleanupProperties); - warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).ToArray (); + warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).FilterWarnings (platform).ToArray (); Assert.That (warnings.Length, Is.EqualTo (0), "Warning Count"); // Verify that the files were in fact removed. @@ -766,7 +766,7 @@ public void BindingWithDefaultCompileInclude (ApplePlatform platform) Assert.That (myStruct, Is.Not.Null, "MyStruct type"); Assert.That (myStruct!.IsValueType, Is.True, "MyStruct"); - var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).Select (v => v.Message); + var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).FilterWarnings (platform).Select (v => v.Message); Assert.That (warnings, Is.Empty, $"Build warnings:\n\t{string.Join ("\n\t", warnings)}"); } @@ -1583,12 +1583,7 @@ public void AppWithDuplicatedResources (ApplePlatform platform, string runtimeId throw new NotImplementedException (scenario.ToString ()); } } - var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath) - .Where (evt => { - if (platform == ApplePlatform.iOS && evt.Message?.Trim () == "Supported iPhone orientations have not been set") - return false; - return true; - }); + var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).FilterWarnings (platform); warnings.AssertWarnings (expectedWarnings); if (bundleOriginalResources && expectedWarnings.Length > 0) { @@ -2530,12 +2525,10 @@ public void BuildMyNativeAotAppWithTrimAnalysisWarning (ApplePlatform platform, var rv = DotNet.AssertBuild (project_path, properties); // We expect to get a warning from the trim analzyer in Debug build - var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).ToArray (); + var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath) + .FilterWarnings (platform) + .ToArray (); - // Ignore warnings we haven't fixed yet - if (platform == ApplePlatform.iOS) { - warnings = warnings.Where (w => w.Message?.Trim () != "Supported iPhone orientations have not been set").ToArray (); - } Assert.That (warnings.Length, Is.EqualTo (1), "Warning count"); Assert.That (warnings [0].Code, Is.EqualTo ("IL2075"), "Warning code"); @@ -2577,12 +2570,7 @@ public void PublishAot (ApplePlatform platform, string runtimeIdentifiers, strin // Verify that we have no warnings, but unfortunately we still have some we haven't fixed yet. // Ignore those, and fail the test if we stop getting them (so that we can update the test to not ignore them anymore). - rv.AssertNoWarnings ((evt) => { - if (platform == ApplePlatform.iOS && evt.Message?.Trim () == "Supported iPhone orientations have not been set") - return false; - - return true; - }); + rv.AssertNoWarnings ((evt) => !Extensions.IsFilteredWarning (evt, platform)); } [Test] @@ -2606,11 +2594,7 @@ public void PublishAotMonoTouchTest_NoIL2009 (ApplePlatform platform, string run var config = "Debug"; var runtimeIdentifierInfix = $"/{runtimeIdentifiers}/"; var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath) - .Where (evt => { - if (platform == ApplePlatform.iOS && evt.Message?.Trim () == "Supported iPhone orientations have not been set") - return false; - return true; - }); + .FilterWarnings (platform); var expectedWarnings = new ExpectedBuildMessage [] { new ExpectedBuildMessage ($"ILLINK", $"It's not safe to remove the dynamic registrar, because monotouchtest references 'ObjCRuntime.Runtime.ConnectMethod (System.Reflection.MethodInfo, ObjCRuntime.Selector)'."), new ExpectedBuildMessage ($"ILLINK", $"It's not safe to remove the dynamic registrar, because monotouchtest references 'ObjCRuntime.Runtime.ConnectMethod (System.Type, System.Reflection.MethodInfo, Foundation.ExportAttribute)'."), @@ -2731,7 +2715,7 @@ public void UnsupportedTargetPlatformVersion (ApplePlatform platform) if (IsTargetPlatformVersionCompatEnabled) { var rv = DotNet.AssertBuild (project_path, properties); - var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).ToArray (); + var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).FilterWarnings (platform).ToArray (); AssertWarningMessages (warnings, $"{minSupportedOSVersion} is not a valid TargetPlatformVersion for {platform.AsString ()}. This warning will become an error in future versions of the {platform.AsString ()} workload. Valid versions include:\n{string.Join ('\n', supportedApiVersions)}"); } else { var rv = DotNet.AssertBuildFailure (project_path, properties); diff --git a/tests/dotnet/UnitTests/TemplateTest.cs b/tests/dotnet/UnitTests/TemplateTest.cs index 24c9e1d54dca..00f8e790b0fe 100644 --- a/tests/dotnet/UnitTests/TemplateTest.cs +++ b/tests/dotnet/UnitTests/TemplateTest.cs @@ -220,7 +220,7 @@ public void CreateAndBuildProjectTemplate (TemplateInfo info) var proj = Path.Combine (outputDir, $"{info.Template}.{language.AsFileExtension ()}"); var properties = GetDefaultProperties (); var rv = DotNet.AssertBuild (proj, properties); - var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).Select (v => v.Message); + var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).FilterWarnings (info.Platform).Select (v => v.Message); Assert.That (warnings, Is.Empty, $"Build warnings:\n\t{string.Join ("\n\t", warnings)}"); if (info.Execute) { diff --git a/tests/dotnet/UnitTests/TrimmerWarningsTest.cs b/tests/dotnet/UnitTests/TrimmerWarningsTest.cs index 719f8a8112ce..ab5b77f88d7e 100644 --- a/tests/dotnet/UnitTests/TrimmerWarningsTest.cs +++ b/tests/dotnet/UnitTests/TrimmerWarningsTest.cs @@ -128,11 +128,7 @@ void TrimmerWarnings (ApplePlatform platform, string runtimeIdentifiers, string var rv = DotNet.AssertBuild (project_path, properties); var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath) - .Where (evt => { - if (platform == ApplePlatform.iOS && evt.Message?.Trim () == "Supported iPhone orientations have not been set") - return false; - return true; - }); + .FilterWarnings (platform); warnings.AssertWarnings (expectedWarnings); } } diff --git a/tests/dotnet/UnitTests/WindowsTest.cs b/tests/dotnet/UnitTests/WindowsTest.cs index a3fe051dbd38..b7b9d9a2e25f 100644 --- a/tests/dotnet/UnitTests/WindowsTest.cs +++ b/tests/dotnet/UnitTests/WindowsTest.cs @@ -137,7 +137,7 @@ public void BundleStructureWithRemoteMac (ApplePlatform platform, string runtime var rv = DotNet.AssertBuild (project_path, properties); var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).ToArray (); - var warningMessages = BundleStructureTest.FilterWarnings (warnings, canonicalizePaths: true); + var warningMessages = BundleStructureTest.FilterWarnings (warnings, platform, canonicalizePaths: true); var isReleaseBuild = string.Equals (configuration, "Release", StringComparison.OrdinalIgnoreCase); var platformString = platform.AsString (); @@ -209,7 +209,7 @@ public void BundleStructureWithRemoteMac (ApplePlatform platform, string runtime rv = DotNet.AssertBuild (project_path, properties); var allTargets = BinLog.GetAllTargets (rv.BinLogPath); warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).ToArray (); - warningMessages = BundleStructureTest.FilterWarnings (warnings, canonicalizePaths: true); + warningMessages = BundleStructureTest.FilterWarnings (warnings, platform, canonicalizePaths: true); BundleStructureTest.CheckZippedAppBundleContents (platform, zippedAppBundlePath, rids, signature, isReleaseBuild); AssertWarningsEqual (expectedWarnings, warningMessages, "Warnings Rebuild 1"); @@ -229,7 +229,7 @@ public void BundleStructureWithRemoteMac (ApplePlatform platform, string runtime rv = DotNet.AssertBuild (project_path, properties); allTargets = BinLog.GetAllTargets (rv.BinLogPath); warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).ToArray (); - warningMessages = BundleStructureTest.FilterWarnings (warnings, canonicalizePaths: true); + warningMessages = BundleStructureTest.FilterWarnings (warnings, platform, canonicalizePaths: true); BundleStructureTest.CheckZippedAppBundleContents (platform, zippedAppBundlePath, rids, signature, isReleaseBuild); AssertWarningsEqual (expectedWarnings, warningMessages, "Warnings Rebuild 2"); @@ -246,7 +246,7 @@ public void BundleStructureWithRemoteMac (ApplePlatform platform, string runtime rv = DotNet.AssertBuild (project_path, properties); allTargets = BinLog.GetAllTargets (rv.BinLogPath); warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).ToArray (); - warningMessages = BundleStructureTest.FilterWarnings (warnings, canonicalizePaths: true); + warningMessages = BundleStructureTest.FilterWarnings (warnings, platform, canonicalizePaths: true); BundleStructureTest.CheckZippedAppBundleContents (platform, zippedAppBundlePath, rids, signature, isReleaseBuild); AssertWarningsEqual (expectedWarnings, warningMessages, "Warnings Rebuild 3"); diff --git a/tests/dotnet/UnitTests/XcodeProjectTests.cs b/tests/dotnet/UnitTests/XcodeProjectTests.cs index aab50fe70c93..dac323d491a4 100644 --- a/tests/dotnet/UnitTests/XcodeProjectTests.cs +++ b/tests/dotnet/UnitTests/XcodeProjectTests.cs @@ -149,7 +149,7 @@ public class Binding var properties = GetDefaultProperties (); var rv = DotNet.AssertBuild (proj, properties); - var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).Select (v => v.Message); + var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).FilterWarnings (platform).Select (v => v.Message); Assert.That (warnings, Is.Empty, $"Build warnings:\n\t{string.Join ("\n\t", warnings)}"); AssertXcFrameworkOutput (platform, testDir, xcodeProjName); } From 15b211351d4e1a61d904b8333928edccd6ce3831 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 4 Jun 2026 18:13:52 +0200 Subject: [PATCH 75/79] [src] Remove CIFilterGenerator from iOS and Mac Catalyst. (#25630) This is a macOS-only class. But keep obsolete APIs throwing PlatformNotSupportedException until XAMCORE_5_0. --------- Co-authored-by: Rolf Bjarne Kvinge --- src/CoreImage/CIFilterGenerator.cs | 145 +++++++++++++++++++++++++ src/coreimage.cs | 12 -- src/frameworks.sources | 1 + tests/introspection/ApiSelectorTest.cs | 9 -- 4 files changed, 146 insertions(+), 21 deletions(-) create mode 100644 src/CoreImage/CIFilterGenerator.cs diff --git a/src/CoreImage/CIFilterGenerator.cs b/src/CoreImage/CIFilterGenerator.cs new file mode 100644 index 000000000000..cdcd4d1d2646 --- /dev/null +++ b/src/CoreImage/CIFilterGenerator.cs @@ -0,0 +1,145 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#if !XAMCORE_5_0 && (__IOS__ || __MACCATALYST__) + +using System.ComponentModel; + +namespace CoreImage; + +[Register ("CIFilterGenerator", true)] +[UnsupportedOSPlatform ("ios")] +[UnsupportedOSPlatform ("maccatalyst")] +[UnsupportedOSPlatform ("tvos")] +[SupportedOSPlatform ("macos")] +[Obsolete (Constants.TypeUnavailable, false)] +[EditorBrowsable (EditorBrowsableState.Never)] +public class CIFilterGenerator : NSObject, ICIFilterConstructor, INSCoding, INSCopying, INSSecureCoding { + [EditorBrowsable (EditorBrowsableState.Never)] + public override NativeHandle ClassHandle { get => throw new PlatformNotSupportedException (Constants.TypeUnavailable); } + + [EditorBrowsable (EditorBrowsableState.Never)] + public CIFilterGenerator (NSCoder coder) : base (NSObjectFlag.Empty) + { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + + [EditorBrowsable (EditorBrowsableState.Never)] + protected CIFilterGenerator (NSObjectFlag t) : base (t) + { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + + [EditorBrowsable (EditorBrowsableState.Never)] + protected internal CIFilterGenerator (NativeHandle handle) : base (handle) + { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + + [EditorBrowsable (EditorBrowsableState.Never)] + public CIFilterGenerator (NSUrl aURL) + : base (NSObjectFlag.Empty) + { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + + [EditorBrowsable (EditorBrowsableState.Never)] + public virtual void ConnectObject (NSObject sourceObject, string? withSourceKey, NSObject targetObject, string targetKey) + { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + + [EditorBrowsable (EditorBrowsableState.Never)] + public virtual NSObject Copy (NSZone? zone) + { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + + [EditorBrowsable (EditorBrowsableState.Never)] + public static CIFilterGenerator Create () + { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + + [EditorBrowsable (EditorBrowsableState.Never)] + public virtual CIFilter CreateFilter () + { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + + [EditorBrowsable (EditorBrowsableState.Never)] + public virtual void DisconnectObject (NSObject sourceObject, string sourceKey, NSObject targetObject, string targetKey) + { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + + [EditorBrowsable (EditorBrowsableState.Never)] + public virtual void EncodeTo (NSCoder encoder) + { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + + [EditorBrowsable (EditorBrowsableState.Never)] + public virtual void ExportKey (string key, NSObject targetObject, string? exportedKeyName) + { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + + [EditorBrowsable (EditorBrowsableState.Never)] + public virtual CIFilter? FilterWithName (string name) + { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + + [EditorBrowsable (EditorBrowsableState.Never)] + public static CIFilterGenerator? FromUrl (NSUrl aURL) + { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + + [EditorBrowsable (EditorBrowsableState.Never)] + public virtual void RegisterFilterName (string name) + { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + + [EditorBrowsable (EditorBrowsableState.Never)] + public virtual void RemoveExportedKey (string exportedKeyName) + { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + + [EditorBrowsable (EditorBrowsableState.Never)] + public virtual bool Save (NSUrl toUrl, bool atomically) + { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + + [EditorBrowsable (EditorBrowsableState.Never)] + public virtual void SetAttributesforExportedKey (NSDictionary attributes, NSString exportedKey) + { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + + [EditorBrowsable (EditorBrowsableState.Never)] + public virtual NSDictionary ClassAttributes { + [Export ("classAttributes")] + get { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + [Export ("setClassAttributes:")] + set { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + } + + [EditorBrowsable (EditorBrowsableState.Never)] + public virtual NSDictionary ExportedKeys { + [Export ("exportedKeys")] + get { + throw new PlatformNotSupportedException (Constants.TypeUnavailable); + } + } +} /* class CIFilterGenerator */ + +#endif // !XAMCORE_5_0 && (__IOS__ || __MACCATALYST__) diff --git a/src/coreimage.cs b/src/coreimage.cs index 96a7abb07525..4dc8b5ccde2c 100644 --- a/src/coreimage.cs +++ b/src/coreimage.cs @@ -2633,17 +2633,8 @@ interface CIFilterApply { NSString OptionColorSpace { get; } } -#if XAMCORE_5_0 [NoiOS] [NoMacCatalyst] -#else -#if __IOS__ || __MACCATALYST__ - [EditorBrowsable (EditorBrowsableState.Never)] - [Obsolete ("Do not use; this type does not exist on this platform.")] -#endif - [iOS (17, 0)] - [MacCatalyst (17, 0)] -#endif [NoTV] [BaseType (typeof (NSObject))] [DisableDefaultCtor] @@ -2744,21 +2735,18 @@ interface CIFilterGenerator : CIFilterConstructor, NSSecureCoding, NSCopying { /// To be added. /// To be added. /// To be added. - [NoiOS, NoMacCatalyst] [Field ("kCIFilterGeneratorExportedKey", "+CoreImage")] NSString ExportedKey { get; } /// To be added. /// To be added. /// To be added. - [NoiOS, NoMacCatalyst] [Field ("kCIFilterGeneratorExportedKeyTargetObject", "+CoreImage")] NSString ExportedKeyTargetObject { get; } /// To be added. /// To be added. /// To be added. - [NoiOS, NoMacCatalyst] [Field ("kCIFilterGeneratorExportedKeyName", "+CoreImage")] NSString ExportedKeyName { get; } } diff --git a/src/frameworks.sources b/src/frameworks.sources index c715fddb973f..0b7237d278a2 100644 --- a/src/frameworks.sources +++ b/src/frameworks.sources @@ -574,6 +574,7 @@ COREIMAGE_SOURCES = \ CoreImage/CIDetector.cs \ CoreImage/CIDetectorOptions.cs \ CoreImage/CIFilter.cs \ + CoreImage/CIFilterGenerator.cs \ CoreImage/CIImage.cs \ CoreImage/CISampler.cs \ CoreImage/CISystemToneMap.cs \ diff --git a/tests/introspection/ApiSelectorTest.cs b/tests/introspection/ApiSelectorTest.cs index d22c7b8829d2..98aa1316d090 100644 --- a/tests/introspection/ApiSelectorTest.cs +++ b/tests/introspection/ApiSelectorTest.cs @@ -242,15 +242,6 @@ protected virtual bool Skip (Type type, string selectorName) return true; } break; - case "CIFilterGenerator": - switch (selectorName) { - case "filterGenerator": - case "filterGeneratorWithContentsOfURL:": - if (TestRuntime.IsSimulatorOrDesktop) - return true; - break; - } - break; } // This ctors needs to be manually bound switch (type.Name) { From 72081d04b8e5bc539170731d61ea48247e3fe6a8 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 4 Jun 2026 18:14:03 +0200 Subject: [PATCH 76/79] [msbuild] Fix getting an x64 simulator if asked to build for x64. (#25614) --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Tasks/GetAvailableDevices.cs | 7 +- .../TaskTests/GetAvailableDevicesTest.cs | 86 +++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/GetAvailableDevices.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/GetAvailableDevices.cs index 8046bf247001..3a21eb4081d6 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/GetAvailableDevices.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/GetAvailableDevices.cs @@ -100,8 +100,13 @@ public override bool Execute () continue; } // if we have multiple runtime identifiers, we're running in the simulator, and one is x64 and the other is arm64. + // if 'RuntimeIdentifier' is set on the task, set that value, otherwise // if we can run on arm64, then pick the arm64 simulator, otherwise pick the x64 simulator - d.Item.SetMetadata ("RuntimeIdentifier", d.RuntimeIdentifiers.Single (v => v.Contains ("arm64") == CanRunArm64)); + if (!string.IsNullOrEmpty (RuntimeIdentifier)) { + d.Item.SetMetadata ("RuntimeIdentifier", RuntimeIdentifier); + } else { + d.Item.SetMetadata ("RuntimeIdentifier", d.RuntimeIdentifiers.Single (v => v.Contains ("arm64") == CanRunArm64)); + } } DiscardedDevices = devices.Where (d => d.Discarded).Select (v => { diff --git a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GetAvailableDevicesTest.cs b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GetAvailableDevicesTest.cs index 1d874fcb732c..c202f5e615aa 100644 --- a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GetAvailableDevicesTest.cs +++ b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GetAvailableDevicesTest.cs @@ -679,6 +679,30 @@ public void Ctl1_AppleTV () }); } + [Test] + [TestCase ("iossimulator-x64", "iossimulator-x64")] + [TestCase ("iossimulator-arm64", "iossimulator-arm64")] + [TestCase ("", null)] // null means it depends on CanRunArm64 + public void SimCtl_MultiArch_RuntimeIdentifier (string runtimeIdentifier, string? expectedRid) + { + var platform = ApplePlatform.iOS; + var task = CreateTask (platform, SIMCTL_JSON_MULTIARCH, ""); + task.RuntimeIdentifier = runtimeIdentifier; + Assert.That (task.Execute (), Is.True, "Task should have succeeded."); + Assert.Multiple (() => { + Assert.That (task.Devices.Count, Is.EqualTo (1), "Devices count mismatch."); + + Assert.That (task.Devices [0].ItemSpec, Is.EqualTo ("AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE"), "Device 1 mismatch."); + Assert.That (task.Devices [0].GetMetadata ("Description"), Is.EqualTo ("iPhone 11 - iOS 26.1"), "Device 1 Name mismatch."); + Assert.That (task.Devices [0].GetMetadata ("OSVersion"), Is.EqualTo ("26.1"), "Device 1 OSVersion mismatch."); + Assert.That (task.Devices [0].GetMetadata ("UDID"), Is.EqualTo ("AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE"), "Device 1 UDID mismatch."); + if (expectedRid is null) + expectedRid = GetAvailableDevices.CanRunArm64 ? "iossimulator-arm64" : "iossimulator-x64"; + Assert.That (task.Devices [0].GetMetadata ("RuntimeIdentifier"), Is.EqualTo (expectedRid), "Device 1 RuntimeIdentifier mismatch."); + Assert.That (task.Devices [0].GetMetadata ("DiscardedReason"), Is.Empty, "Device 1 discarded reason mismatch."); + }); + } + [Test] public void DeviceCtl2_Mac () { @@ -1108,5 +1132,67 @@ public void DeviceCtl2_Mac () } } """; + + const string SIMCTL_JSON_MULTIARCH = + """ + { + "devicetypes" : [ + { + "productFamily" : "iPhone", + "bundlePath" : "\/Library\/Developer\/CoreSimulator\/Profiles\/DeviceTypes\/iPhone 11.simdevicetype", + "maxRuntimeVersion" : 4294967295, + "maxRuntimeVersionString" : "65535.255.255", + "identifier" : "com.apple.CoreSimulator.SimDeviceType.iPhone-11", + "modelIdentifier" : "iPhone12,1", + "minRuntimeVersionString" : "13.0.0", + "minRuntimeVersion" : 851968, + "name" : "iPhone 11" + } + ], + "runtimes" : [ + { + "isAvailable" : true, + "version" : "26.1", + "isInternal" : false, + "buildversion" : "23B80", + "supportedArchitectures" : [ + "arm64", + "x86_64" + ], + "supportedDeviceTypes" : [ + { + "bundlePath" : "\/Library\/Developer\/CoreSimulator\/Profiles\/DeviceTypes\/iPhone 11.simdevicetype", + "name" : "iPhone 11", + "identifier" : "com.apple.CoreSimulator.SimDeviceType.iPhone-11", + "productFamily" : "iPhone" + } + ], + "identifier" : "com.apple.CoreSimulator.SimRuntime.iOS-26-1", + "platform" : "iOS", + "bundlePath" : "\/Library\/Developer\/CoreSimulator\/Volumes\/iOS_23B80\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS 26.1.simruntime", + "runtimeRoot" : "\/Library\/Developer\/CoreSimulator\/Volumes\/iOS_23B80\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS 26.1.simruntime\/Contents\/Resources\/RuntimeRoot", + "name" : "iOS 26.1" + } + ], + "devices" : { + "com.apple.CoreSimulator.SimRuntime.iOS-26-1" : [ + { + "dataPath" : "\/Users\/rolf\/Library\/Developer\/CoreSimulator\/Devices\/AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE\/data", + "dataPathSize" : 2274861056, + "logPath" : "\/Users\/rolf\/Library\/Logs\/CoreSimulator\/AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE", + "udid" : "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE", + "isAvailable" : true, + "logPathSize" : 253952, + "deviceTypeIdentifier" : "com.apple.CoreSimulator.SimDeviceType.iPhone-11", + "state" : "Shutdown", + "name" : "iPhone 11 - iOS 26.1" + } + ] + }, + "pairs" : { + + } + } + """; } } From 4d39e1b367409654f65ad7d47e26fa31840c980e Mon Sep 17 00:00:00 2001 From: Alex Soto Date: Thu, 4 Jun 2026 17:45:49 -0400 Subject: [PATCH 77/79] [CI] Use ACES in CI builds (#25572) Move the CI build/test/API-diff flows to the ACES shared macOS pool while keeping PR validation on the existing PR pool for now. ## Scope This PR enables ACES for the CI entry points only: - `build-pipeline.yml` - `run-ci-api-diff.yml` - `run-post-ci-build-tests.yml` The PR entry points remain opt-out for now and can be flipped later once CI is stable: - `build-pull-request.yml` - `run-pr-api-diff.yml` - `run-post-pr-build-tests.yml` ## What changed - Added/used the `useACES` template parameter to route CI build, API diff, and simulator-test jobs to the ACES shared pool/image. - Kept the existing non-ACES pool and demand behavior when `useACES` is false. - Marked ACES simulator-test jobs with `VM_VENDOR=ACES` so tests that should not run on virtualized machines can opt out correctly. - Avoid deleting simulator runtimes on ACES, since that operation is not supported/reliable there. - Wait longer for non-x64 simulator cleanup to complete before continuing. - Skip x64 simulator test runs on ACES machines. - Use the Xcode selected by `xcode-select` for build configuration and pre-configure provisioning, so ACES agents with `/Applications/Xcode_26.5.app` do not require symlinks or the traditional `/Applications/Xcode_26.5.0.app` bundle name. - Harden the `System.Net.Http` monotouch tests against transient CI/httpbin network timeouts so network stalls do not hang the entire monotouch app until the harness timeout. ## Related Xcode path support This branch includes the configure support from #25622. The CI templates use the selected Xcode developer root so both classic bots and ACES images can configure/build with the Xcode that is actually installed on the agent. ## Validation notes The original green build links in this PR description became stale as the branch evolved. Recent follow-up failures were investigated and resulted in the Xcode selection and network-timeout fixes included here. The intended validation matrix is: - CI Build on ACES - CI API diff on ACES - Post-CI simulator tests on ACES - PR pipelines still using the existing PR pool --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Rolf Bjarne Kvinge --- system-dependencies.sh | 31 ++++++++++++-- tests/common/TestRuntime.cs | 23 ++++++++++- .../linker/link sdk/LinkSdkRegressionTest.cs | 19 +++++++-- .../AudioToolbox/AudioQueueTest.cs | 2 + tests/monotouch-test/Metal/MTLDeviceTests.cs | 2 + .../System.Net.Http/MessageHandlers.cs | 32 ++++++++++----- .../System.Net.Http/NetworkResources.cs | 1 + .../VideoToolbox/VTCompressionSessionTests.cs | 3 ++ .../VTMotionEstimationSessionTest.cs | 6 +++ tests/xharness/AppRunner.cs | 7 ++++ .../xharness/Jenkins/TestVariationsFactory.cs | 9 +++-- tools/devops/automation/build-pipeline.yml | 1 + .../devops/automation/build-pull-request.yml | 1 + tools/devops/automation/run-ci-api-diff.yml | 1 + .../automation/run-post-ci-build-tests.yml | 1 + .../automation/run-post-pr-build-tests.yml | 1 + tools/devops/automation/run-pr-api-diff.yml | 1 + .../automation/templates/api-diff-stage.yml | 4 ++ .../templates/build/api-diff-stage.yml | 24 +++++++---- .../templates/build/build-mac-tests-stage.yml | 27 +++++++++---- .../templates/build/build-stage.yml | 27 +++++++++---- .../automation/templates/build/build.yml | 35 +++++++++++++++- .../automation/templates/main-stage.yml | 5 +++ .../templates/pipelines/api-diff-pipeline.yml | 5 +++ .../pipelines/run-tests-pipeline.yml | 6 +++ .../automation/templates/tests-stage.yml | 6 +++ .../automation/templates/tests/build.yml | 8 +++- .../automation/templates/tests/stage.yml | 38 +++++++++++++----- .../automation/templates/variables/common.yml | 40 +++++++++++++++++++ 29 files changed, 306 insertions(+), 60 deletions(-) diff --git a/system-dependencies.sh b/system-dependencies.sh index 912f671b6c90..9393d788eec7 100755 --- a/system-dependencies.sh +++ b/system-dependencies.sh @@ -365,6 +365,20 @@ function get_non_universal_simulator_runtimes () rm -f "$TMPFILE" } +function print_non_universal_simulator_runtimes () +{ + local TMPFILE + TMPFILE=$(mktemp) + + xcrun simctl runtime list -j --json-output="$TMPFILE" + + # this json query filters the json to simulator runtimes where iOS/tvOS >= 26.0 and where x64 is *not* supported (which we need to run x64 apps in the simulator on arm64) + JQ_QUERY='map({platformIdentifier: .platformIdentifier, identifier: .identifier, version: .version, state: .state, supportedArchitectures: .supportedArchitectures | join("|"), majorVersion: .version | split(".")[0] | tonumber }) | map(select(.majorVersion>=26) ) | map(select(.supportedArchitectures | contains("x86_64") | not))' + jq "$JQ_QUERY" -r "$TMPFILE" + + rm -f "$TMPFILE" +} + function xcodebuild_download_selected_platforms () { local XCODE_DEVELOPER_ROOT @@ -422,8 +436,10 @@ function xcodebuild_download_selected_platforms () log "Looking for iOS/tvOS 26+ simulator runtimes that don't support x64..." get_non_universal_simulator_runtimes - if [[ "$SIMULATORS_WITHOUT_X64_COUNT" -gt 0 ]]; then - log "Found ${SIMULATORS_WITHOUT_X64_COUNT} simulator runtimes that don't support x64, which will now be deleted: ${SIMULATORS_WITHOUT_X64[@]}" + if [[ "$SIMULATORS_WITHOUT_X64_COUNT" -gt 0 && "$ACES" == "1" ]]; then + log "Found ${SIMULATORS_WITHOUT_X64_COUNT} simulator runtimes that don't support x64, but we're running on ACES, so we can't do anything about that." + elif [[ "$SIMULATORS_WITHOUT_X64_COUNT" -gt 0 ]]; then + log "Found ${SIMULATORS_WITHOUT_X64_COUNT} simulator runtimes that don't support x64, which will now be deleted: ${SIMULATORS_WITHOUT_X64[*]}" for sim in "${SIMULATORS_WITHOUT_X64[@]}"; do log "Executing 'xcrun simctl runtime delete $sim'" xcrun simctl runtime delete "$sim" @@ -431,17 +447,24 @@ function xcodebuild_download_selected_platforms () # sadly simulator deletion is done asynchronously, so we have to wait until they're all gone log "Waiting for the simulators to be deleted..." printf " " - for i in $(seq 1 60); do + for i in $(seq 1 300); do sleep 1 get_non_universal_simulator_runtimes if [[ "$SIMULATORS_WITHOUT_X64_COUNT" == "0" ]]; then break fi + # every 60 seconds print the simulators left to delete + if [[ $(( i % 60)) == 0 ]]; then + printf "\n" + printf " Simulators left to delete:\n" + print_non_universal_simulator_runtimes | sed 's/^/ /' + printf " " + fi printf "$SIMULATORS_WITHOUT_X64_COUNT" done printf "\n" if [[ "$SIMULATORS_WITHOUT_X64_COUNT" != "0" ]]; then - warn "Waited for 60 seconds, but there are still $SIMULATORS_WITHOUT_X64_COUNT simulators waiting to deleted." + warn "Waited for 5 minutes, but there are still $SIMULATORS_WITHOUT_X64_COUNT simulators waiting to deleted." fi else log "All installed iOS/tvOS 26+ simulators support x64" diff --git a/tests/common/TestRuntime.cs b/tests/common/TestRuntime.cs index 296c348ac2cf..f7a8394faf62 100644 --- a/tests/common/TestRuntime.cs +++ b/tests/common/TestRuntime.cs @@ -299,12 +299,10 @@ public static void AssertSimulatorOrDesktop (string message = "This test only wo public static void AssertNotVirtualMachine () { -#if MONOMAC || __MACCATALYST__ // enviroment variable set by the CI when running on a VM var vmVendor = Environment.GetEnvironmentVariable ("VM_VENDOR"); if (!string.IsNullOrEmpty (vmVendor)) NUnit.Framework.Assert.Ignore ($"This test only runs on device. Found vm vendor: {vmVendor}"); -#endif } public static bool IsVSTS => @@ -1625,6 +1623,7 @@ public static void IgnoreInCIIfBadNetwork (Exception? ex) IgnoreInCIIfDnsResolutionFailed (ex); IgnoreInCIIfSshConnectionError (ex); IgnoreInCIIfTimedOut (ex); + IgnoreInCIIfHttpClientTimedOut (ex); } public static void IgnoreInCIIfBadNetwork (NSError? error) @@ -1666,6 +1665,26 @@ public static void IgnoreInCIIfTimedOut (NSError error) IgnoreNetworkError (error, CFNetworkErrors.TimedOut); } + public static void IgnoreInCIIfHttpClientTimedOut () + { + IgnoreInCI ("Ignored due to HTTP client timeout."); + } + + public static void IgnoreInCIIfHttpClientTimedOut (Exception? ex) + { + if (ex is null) + return; + + var tce = FindInner (ex); + if (tce is null) + return; + + if (FindInner (tce) is not null || + tce.Message.Contains ("HttpClient.Timeout", StringComparison.Ordinal)) { + IgnoreInCI ($"Ignored due to HTTP client timeout: {tce.Message}"); + } + } + public static void IgnoreInCIIfTimedOut (Exception ex) { if (ex is WebException wex) { diff --git a/tests/linker/link sdk/LinkSdkRegressionTest.cs b/tests/linker/link sdk/LinkSdkRegressionTest.cs index b75d223ed24a..0f147186f702 100644 --- a/tests/linker/link sdk/LinkSdkRegressionTest.cs +++ b/tests/linker/link sdk/LinkSdkRegressionTest.cs @@ -789,11 +789,11 @@ void SpecialFolderImpl () #else var myExists = false; #endif - path = TestFolder (Environment.SpecialFolder.MyMusic, exists: myExists); + path = TestFolderIfAvailableInCI (Environment.SpecialFolder.MyMusic, myExists); - path = TestFolder (Environment.SpecialFolder.MyVideos, exists: myExists); + path = TestFolderIfAvailableInCI (Environment.SpecialFolder.MyVideos, myExists); - path = TestFolder (Environment.SpecialFolder.DesktopDirectory, exists: myExists); + path = TestFolderIfAvailableInCI (Environment.SpecialFolder.DesktopDirectory, myExists); #if __TVOS__ path = TestFolder (Environment.SpecialFolder.Fonts, exists: null, supported: true); @@ -811,7 +811,7 @@ void SpecialFolderImpl () path = TestFolder (Environment.SpecialFolder.Templates, exists: false); #endif - path = TestFolder (Environment.SpecialFolder.MyPictures, exists: myExists); + path = TestFolderIfAvailableInCI (Environment.SpecialFolder.MyPictures, myExists); #if __MACOS__ path = TestFolder (Environment.SpecialFolder.CommonTemplates, supported: false); @@ -897,6 +897,17 @@ void SpecialFolderImpl () path = TestFolder (Environment.SpecialFolder.Resources, readOnly: tvos && device); Assert.That (path.EndsWith ("/Library", StringComparison.Ordinal), Is.True, "Resources"); #endif + // Some CI VM images don't initialize all standard user directories, so keep this + // tolerance limited to CI VMs and preserve the stricter check for other runs. + string TestFolderIfAvailableInCI (Environment.SpecialFolder folder, bool exists) + { +#if __MACOS__ + var path = Environment.GetFolderPath (folder); + if (string.IsNullOrEmpty (path) && TestRuntime.IsInCI && TestRuntime.IsVM) + return path; +#endif + return TestFolder (folder, exists: exists); + } } #if !__MACOS__ diff --git a/tests/monotouch-test/AudioToolbox/AudioQueueTest.cs b/tests/monotouch-test/AudioToolbox/AudioQueueTest.cs index a5249b42ea58..abe7b02f3266 100644 --- a/tests/monotouch-test/AudioToolbox/AudioQueueTest.cs +++ b/tests/monotouch-test/AudioToolbox/AudioQueueTest.cs @@ -30,6 +30,8 @@ public void Properties () [Test] public void ChannelAssignments () { + TestRuntime.AssertNotVirtualMachine (); + var aq = new OutputAudioQueue (AudioStreamBasicDescription.CreateLinearPCM ()); var route = global::AVFoundation.AVAudioSession.SharedInstance ().CurrentRoute; diff --git a/tests/monotouch-test/Metal/MTLDeviceTests.cs b/tests/monotouch-test/Metal/MTLDeviceTests.cs index 2a6a38b51134..948737e2749b 100644 --- a/tests/monotouch-test/Metal/MTLDeviceTests.cs +++ b/tests/monotouch-test/Metal/MTLDeviceTests.cs @@ -86,6 +86,8 @@ public void ReturnReleaseTest () if (device is null) Assert.Inconclusive ("Metal is not supported"); + TestRuntime.AssertNotVirtualMachine (); + // Apple claims that "Indirect command buffers" are available with MTLGPUFamilyCommon2, but it crashes on at least one machine. // Log what the current device supports, just to have it in the log. foreach (MTLFeatureSet fs in Enum.GetValues ()) { diff --git a/tests/monotouch-test/System.Net.Http/MessageHandlers.cs b/tests/monotouch-test/System.Net.Http/MessageHandlers.cs index 6bf154dc0934..2811dd23ee2c 100644 --- a/tests/monotouch-test/System.Net.Http/MessageHandlers.cs +++ b/tests/monotouch-test/System.Net.Http/MessageHandlers.cs @@ -454,12 +454,15 @@ public void RedirectionWithAuthorizationHeaders (Type handlerType) bool containsHeaders = false; string json = ""; + using var handler = GetHandler (handlerType); var done = TestRuntime.TryRunAsync (TimeSpan.FromSeconds (30), async () => { - HttpClient client = new HttpClient (GetHandler (handlerType)); + using var client = new HttpClient (handler, disposeHandler: false) { + Timeout = TimeSpan.FromSeconds (20), + }; client.BaseAddress = NetworkResources.Httpbin.Uri; var byteArray = new UTF8Encoding ().GetBytes ("username:password"); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue ("Basic", Convert.ToBase64String (byteArray)); - var result = await client.GetAsync (NetworkResources.Httpbin.GetRedirectUrl (3)); + using var result = await client.GetAsync (NetworkResources.Httpbin.GetRedirectUrl (3)); // get the data returned from httpbin which contains the details of the requested performed. json = await result.Content.ReadAsStringAsync (); containsAuthorizarion = json.Contains ("Authorization"); @@ -467,12 +470,15 @@ public void RedirectionWithAuthorizationHeaders (Type handlerType) }, out var ex); if (!done) { // timeouts happen in the bots due to dns issues, connection issues etc.. we do not want to fail + TestRuntime.IgnoreInCIIfHttpClientTimedOut (); Assert.Inconclusive ("Request timedout."); + } else if (ex is not null) { + TestRuntime.IgnoreInCIIfBadNetwork (ex); + Assert.That (ex, Is.Null, $"Exception {ex} for {json}"); } else if (!containsHeaders) { Assert.Inconclusive ("Response from httpbin does not contain headers, therefore we cannot ensure that if the authoriation is present."); } else { Assert.That (containsAuthorizarion, Is.False, $"Authorization header did reach the final destination. {json}"); - Assert.That (ex, Is.Null, $"Exception {ex} for {json}"); } } @@ -1357,18 +1363,22 @@ public void UpdateRequestUriAfterRedirect (Type handlerType) { // https://github.com/dotnet/macios/issues/20629 + using var handler = GetHandler (handlerType); var done = TestRuntime.TryRunAsync (TimeSpan.FromSeconds (30), async () => { - var client = new HttpClient (GetHandler (handlerType)); + using var client = new HttpClient (handler, disposeHandler: false) { + Timeout = TimeSpan.FromSeconds (20), + }; var postRequestUri = NetworkResources.Httpbin.Url + "/"; var initialRequestUri = NetworkResources.Httpbin.GetRedirectToUrl (postRequestUri); - var request = new HttpRequestMessage (HttpMethod.Get, initialRequestUri); + using var request = new HttpRequestMessage (HttpMethod.Get, initialRequestUri); Assert.That (request.RequestUri.ToString (), Is.EqualTo (initialRequestUri), "Initial RequestUri"); - var response = await client.SendAsync (request); + using var response = await client.SendAsync (request); TestRuntime.IgnoreInCIIfBadNetwork (response.StatusCode); Assert.That (request.RequestUri.ToString (), Is.EqualTo (postRequestUri), "Post RequestUri"); }, out var ex); if (!done) { // timeouts happen in the bots due to dns issues, connection issues etc. we do not want to fail + TestRuntime.IgnoreInCIIfHttpClientTimedOut (); Assert.Inconclusive ("Request timedout."); } else { TestRuntime.IgnoreInCIIfBadNetwork (ex); @@ -1382,17 +1392,21 @@ public void RequestUriNotUpdatedIfNotRedirect (Type handlerType) { // https://github.com/dotnet/macios/issues/20629 + using var handler = GetHandler (handlerType); var done = TestRuntime.TryRunAsync (TimeSpan.FromSeconds (30), async () => { - var client = new HttpClient (GetHandler (handlerType)); + using var client = new HttpClient (handler, disposeHandler: false) { + Timeout = TimeSpan.FromSeconds (20), + }; var requestUri = NetworkResources.Httpbin.Uri + "?stuffHere=[]{}"; - var request = new HttpRequestMessage (HttpMethod.Get, requestUri); + using var request = new HttpRequestMessage (HttpMethod.Get, requestUri); Assert.That (request.RequestUri.ToString (), Is.EqualTo (requestUri), "Initial RequestUri"); - var response = await client.SendAsync (request); + using var response = await client.SendAsync (request); TestRuntime.IgnoreInCIIfBadNetwork (response.StatusCode); Assert.That (request.RequestUri.ToString (), Is.EqualTo (requestUri), "Post RequestUri"); }, out var ex); if (!done) { // timeouts happen in the bots due to dns issues, connection issues etc. we do not want to fail + TestRuntime.IgnoreInCIIfHttpClientTimedOut (); Assert.Inconclusive ("Request timedout."); } else { TestRuntime.IgnoreInCIIfBadNetwork (ex); diff --git a/tests/monotouch-test/System.Net.Http/NetworkResources.cs b/tests/monotouch-test/System.Net.Http/NetworkResources.cs index 6fa21fe58507..2a887a52a3d6 100644 --- a/tests/monotouch-test/System.Net.Http/NetworkResources.cs +++ b/tests/monotouch-test/System.Net.Http/NetworkResources.cs @@ -10,6 +10,7 @@ public static class NetworkResources { public static string MicrosoftUrl => AssertNetworkConnection ("https://www.microsoft.com"); public static Uri MicrosoftUri => new Uri (MicrosoftUrl); public static string MicrosoftHttpUrl => AssertNetworkConnection ("http://www.microsoft.com"); + public static Uri MicrosoftHttpUri => new Uri (MicrosoftHttpUrl); public static string XamarinUrl => AssertNetworkConnection ("https://dotnet.microsoft.com/apps/xamarin"); public static string XamarinHttpUrl => AssertNetworkConnection ("http://dotnet.microsoft.com/apps/xamarin"); public static Uri XamarinUri => new Uri (XamarinUrl); diff --git a/tests/monotouch-test/VideoToolbox/VTCompressionSessionTests.cs b/tests/monotouch-test/VideoToolbox/VTCompressionSessionTests.cs index d72f372a878a..0c5a4f57af5f 100644 --- a/tests/monotouch-test/VideoToolbox/VTCompressionSessionTests.cs +++ b/tests/monotouch-test/VideoToolbox/VTCompressionSessionTests.cs @@ -236,6 +236,9 @@ public void TestCallbackBackground (bool stronglyTyped) public void TestMultiImage (bool stronglyTyped, bool customCallback) { TestRuntime.AssertXcodeVersion (26, 0); +#if __MACOS__ || __MACCATALYST__ + TestRuntime.AssertNotVirtualMachine (); +#endif if (!VTCompressionSession.IsStereoMvHevcEncodeSupported ()) Assert.Ignore ("Stereo MV-HEVC encoding is not supported on the current system."); diff --git a/tests/monotouch-test/VideoToolbox/VTMotionEstimationSessionTest.cs b/tests/monotouch-test/VideoToolbox/VTMotionEstimationSessionTest.cs index a8f49139e6ff..9e338d9d6ef9 100644 --- a/tests/monotouch-test/VideoToolbox/VTMotionEstimationSessionTest.cs +++ b/tests/monotouch-test/VideoToolbox/VTMotionEstimationSessionTest.cs @@ -24,6 +24,9 @@ public class VTMotionEstimationSessionTest { public void CreateTest () { TestRuntime.AssertXcodeVersion (26, 0); +#if __MACOS__ || __MACCATALYST__ + TestRuntime.AssertNotVirtualMachine (); +#endif // VTMotionEstimationSessionCreate just returns in the simulator (a single 'ret' instruction), // which means it returns with status=VTStatus.Ok, but no session actually created. So ignore // this test in the simulator. @@ -49,6 +52,9 @@ public void CreateTest () public void CreateStronglyTypedTest () { TestRuntime.AssertXcodeVersion (26, 0); +#if __MACOS__ || __MACCATALYST__ + TestRuntime.AssertNotVirtualMachine (); +#endif // VTMotionEstimationSessionCreate just returns in the simulator (a single 'ret' instruction), // which means it returns with status=VTStatus.Ok, but no session actually created. So ignore // this test in the simulator. diff --git a/tests/xharness/AppRunner.cs b/tests/xharness/AppRunner.cs index 4cc1a11b2690..8f79f259d1ae 100644 --- a/tests/xharness/AppRunner.cs +++ b/tests/xharness/AppRunner.cs @@ -225,6 +225,13 @@ public async Task RunAsync () if (harness.InCI) { // We use the 'BUILD_REVISION' variable to detect whether we're running CI or not. args.Add (new SetEnvVariableArgument ("BUILD_REVISION", Environment.GetEnvironmentVariable ("BUILD_REVISION"))); + + // Forward VM_VENDOR (set by the pipeline when running on a VM-backed + // pool such as ACES) so TestRuntime.AssertNotVirtualMachine works + // inside the iOS/tvOS simulator process. + var vmVendor = Environment.GetEnvironmentVariable ("VM_VENDOR"); + if (!string.IsNullOrEmpty (vmVendor)) + args.Add (new SetEnvVariableArgument ("VM_VENDOR", vmVendor)); } if (!harness.GetIncludeSystemPermissionTests (TestPlatform.iOS, !isSimulator)) diff --git a/tests/xharness/Jenkins/TestVariationsFactory.cs b/tests/xharness/Jenkins/TestVariationsFactory.cs index 00c05d4882c6..167d8f6247bb 100644 --- a/tests/xharness/Jenkins/TestVariationsFactory.cs +++ b/tests/xharness/Jenkins/TestVariationsFactory.cs @@ -33,6 +33,7 @@ IEnumerable GetTestData (RunTestTask test) var arm64_sim_runtime_identifier = string.Empty; var x64_sim_runtime_identifier = string.Empty; var supports_coreclr = test.Platform == TestPlatform.Mac || jenkins.Harness.DotNetVersion.Major >= 11; + var supports_x64 = string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("ACES")); // x64 is not supported on ACES machines switch (test.Platform) { case TestPlatform.Mac: @@ -111,8 +112,8 @@ IEnumerable GetTestData (RunTestTask test) yield return new TestData { Variation = "Release (managed static registrar, all optimizations)", TestVariation = "release|managed-static-registrar-all-optimizations-linkall", Ignored = ignore }; if (supports_coreclr) yield return new TestData { Variation = "Release (trimmable static registrar, all optimizations)", TestVariation = "trimmable-static-registrar-all-optimizations-linkall", Ignored = ignore }; - yield return new TestData { Variation = "Release (NativeAOT, x64)", TestVariation = "release|nativeaot", Ignored = ignore, RuntimeIdentifier = x64_sim_runtime_identifier }; - yield return new TestData { Variation = "Release (trimmable static registrar, NativeAOT, x64)", TestVariation = "trimmable-static-registrar|release|nativeaot", Ignored = ignore, RuntimeIdentifier = x64_sim_runtime_identifier }; + yield return new TestData { Variation = "Release (NativeAOT, x64)", TestVariation = "release|nativeaot", Ignored = !supports_x64 ? true : ignore, RuntimeIdentifier = x64_sim_runtime_identifier }; + yield return new TestData { Variation = "Release (trimmable static registrar, NativeAOT, x64)", TestVariation = "trimmable-static-registrar|release|nativeaot", Ignored = !supports_x64 ? true : ignore, RuntimeIdentifier = x64_sim_runtime_identifier }; if (supports_interpreter) { yield return new TestData { Variation = "Debug (interpreter)", TestVariation = "interpreter", Ignored = ignore }; yield return new TestData { Variation = "Release (interpreter)", TestVariation = "release|interpreter", Ignored = ignore }; @@ -157,12 +158,12 @@ IEnumerable GetTestData (RunTestTask test) yield return new TestData { Variation = "Release (trimmable static registrar, all optimizations)", TestVariation = "trimmable-static-registrar-all-optimizations-linkall", Ignored = ignore }; yield return new TestData { Variation = "Release (NativeAOT)", TestVariation = "release|nativeaot", Ignored = ignore }; yield return new TestData { Variation = "Release (NativeAOT, ARM64)", TestVariation = "release|nativeaot", Ignored = !mac_supports_arm64 ? true : ignore, RuntimeIdentifier = arm64_runtime_identifier }; - yield return new TestData { Variation = "Release (NativeAOT, x64)", TestVariation = "release|nativeaot", Ignored = ignore, RuntimeIdentifier = x64_runtime_identifier }; + yield return new TestData { Variation = "Release (NativeAOT, x64)", TestVariation = "release|nativeaot", Ignored = !supports_x64 ? true : ignore, RuntimeIdentifier = x64_runtime_identifier }; if (mac_supports_arm64) yield return new TestData { Variation = $"Release (NativeAOT, .NET 11 defaults)", TestVariation = "release|nativeaot-net11-defaults", Ignored = ignore }; yield return new TestData { Variation = "Release (trimmable static registrar, NativeAOT)", TestVariation = "trimmable-static-registrar|nativeaot|release", Ignored = ignore }; yield return new TestData { Variation = "Release (trimmable static registrar, NativeAOT, ARM64)", TestVariation = "trimmable-static-registrar|nativeaot|release", Ignored = !mac_supports_arm64 ? true : ignore, RuntimeIdentifier = arm64_runtime_identifier }; - yield return new TestData { Variation = "Release (trimmable static registrar, NativeAOT, x64)", TestVariation = "trimmable-static-registrar|nativeaot|release", Ignored = ignore, RuntimeIdentifier = x64_runtime_identifier }; + yield return new TestData { Variation = "Release (trimmable static registrar, NativeAOT, x64)", TestVariation = "trimmable-static-registrar|nativeaot|release", Ignored = !supports_x64 ? true : ignore, RuntimeIdentifier = x64_runtime_identifier }; yield return new TestData { Variation = "Release (static registrar)", TestVariation = "release|static-registrar", Ignored = ignore }; yield return new TestData { Variation = "Release (static registrar, all optimizations)", TestVariation = "release|static-registrar-all-optimizations-linkall", Ignored = ignore }; if (test.Platform == TestPlatform.MacCatalyst) { diff --git a/tools/devops/automation/build-pipeline.yml b/tools/devops/automation/build-pipeline.yml index 97824c8abe3b..09f588d9d0c7 100644 --- a/tools/devops/automation/build-pipeline.yml +++ b/tools/devops/automation/build-pipeline.yml @@ -132,6 +132,7 @@ extends: forceInsertion: ${{ parameters.forceInsertion }} pushNugets: ${{ parameters.pushNugets }} pushNugetsToMaestro: ${{ parameters.pushNugetsToMaestro }} + useACES: true # flip to false to opt CI out of ACES; see templates/variables/common.yml - template: localization/v1.yml@yaml-templates parameters: diff --git a/tools/devops/automation/build-pull-request.yml b/tools/devops/automation/build-pull-request.yml index 8d71f0ad7231..cc30c3acf9a0 100644 --- a/tools/devops/automation/build-pull-request.yml +++ b/tools/devops/automation/build-pull-request.yml @@ -113,3 +113,4 @@ extends: forceInsertion: ${{ parameters.forceInsertion }} pushNugets: false pushNugetsToMaestro: false + useACES: false # flip to true to opt PR into ACES; see templates/variables/common.yml diff --git a/tools/devops/automation/run-ci-api-diff.yml b/tools/devops/automation/run-ci-api-diff.yml index 8ccd511ab0c3..caf428a30870 100644 --- a/tools/devops/automation/run-ci-api-diff.yml +++ b/tools/devops/automation/run-ci-api-diff.yml @@ -17,3 +17,4 @@ extends: parameters: isPR: false pool: $(CIBuildPool) + useACES: true # flip to false to opt CI api-diff out of ACES; see templates/variables/common.yml diff --git a/tools/devops/automation/run-post-ci-build-tests.yml b/tools/devops/automation/run-post-ci-build-tests.yml index af8677d8b4e6..f09d70d3e7ba 100644 --- a/tools/devops/automation/run-post-ci-build-tests.yml +++ b/tools/devops/automation/run-post-ci-build-tests.yml @@ -17,3 +17,4 @@ extends: template: templates/pipelines/run-tests-pipeline.yml parameters: isPR: false + useACES: true # flip to false to opt CI simulator tests out of ACES; see templates/variables/common.yml diff --git a/tools/devops/automation/run-post-pr-build-tests.yml b/tools/devops/automation/run-post-pr-build-tests.yml index b83f2d8e95c3..81be17692d32 100644 --- a/tools/devops/automation/run-post-pr-build-tests.yml +++ b/tools/devops/automation/run-post-pr-build-tests.yml @@ -27,3 +27,4 @@ extends: parameters: isPR: true buildPackages: true + useACES: false # flip to true to opt PR simulator tests into ACES; see templates/variables/common.yml diff --git a/tools/devops/automation/run-pr-api-diff.yml b/tools/devops/automation/run-pr-api-diff.yml index 07401bdf077c..03f693b4b5ea 100644 --- a/tools/devops/automation/run-pr-api-diff.yml +++ b/tools/devops/automation/run-pr-api-diff.yml @@ -26,3 +26,4 @@ extends: parameters: isPR: true pool: $(PRBuildPool) + useACES: false # flip to true to opt PR api-diff into ACES; see templates/variables/common.yml diff --git a/tools/devops/automation/templates/api-diff-stage.yml b/tools/devops/automation/templates/api-diff-stage.yml index a81ac79c8bda..e13e9957a7c5 100644 --- a/tools/devops/automation/templates/api-diff-stage.yml +++ b/tools/devops/automation/templates/api-diff-stage.yml @@ -24,6 +24,9 @@ parameters: - name: macOSName type: string +- name: useACES + type: boolean + default: false stages: @@ -69,3 +72,4 @@ stages: gitHubToken: $(Github.Token) xqaCertPass: $(xqa--certificates--password) pool: ${{ parameters.pool }} + useACES: ${{ parameters.useACES }} diff --git a/tools/devops/automation/templates/build/api-diff-stage.yml b/tools/devops/automation/templates/build/api-diff-stage.yml index 03cc9d7f34d9..edac9d79ca91 100644 --- a/tools/devops/automation/templates/build/api-diff-stage.yml +++ b/tools/devops/automation/templates/build/api-diff-stage.yml @@ -34,6 +34,10 @@ parameters: - name: macOSName type: string +- name: useACES + type: boolean + default: false + jobs: # Detect changes - job: api_diff @@ -44,13 +48,19 @@ jobs: # set the branch variable name, this is required by jenkins and we have a lot of scripts that depend on it BRANCH_NAME: $[ replace(variables['Build.SourceBranch'], 'refs/heads/', '') ] XHARNESS_LABELS: $[ stageDependencies.configure_build.configure.outputs['labels.xharness_labels'] ] - pool: - name: ${{ parameters.pool }} - demands: - - Agent.OS -equals Darwin - - Agent.OSVersion -gtVersion $(minimumMacOSVersion) - - macOS.Name -equals ${{ parameters.macOSName }} - - XcodeChannel -equals ${{ parameters.xcodeChannel }} + ${{ if parameters.useACES }}: + pool: + name: $(CIBuildPoolACES) + demands: + - ImageOverride -equals $(CIBuildPoolACESImage) + ${{ else }}: + pool: + name: ${{ parameters.pool }} + demands: + - Agent.OS -equals Darwin + - Agent.OSVersion -gtVersion $(minimumMacOSVersion) + - macOS.Name -equals ${{ parameters.macOSName }} + - XcodeChannel -equals ${{ parameters.xcodeChannel }} workspace: clean: all diff --git a/tools/devops/automation/templates/build/build-mac-tests-stage.yml b/tools/devops/automation/templates/build/build-mac-tests-stage.yml index 0622c8741b5d..be7ea2efb3b1 100644 --- a/tools/devops/automation/templates/build/build-mac-tests-stage.yml +++ b/tools/devops/automation/templates/build/build-mac-tests-stage.yml @@ -34,6 +34,10 @@ parameters: - name: macOSName type: string +- name: useACES + type: boolean + default: false + jobs: # This job builds the macOS tests. @@ -49,14 +53,21 @@ jobs: BRANCH_NAME: $[ replace(variables['Build.SourceBranch'], 'refs/heads/', '') ] RUN_MAC_TESTS: $[ stageDependencies.configure_build.configure.outputs['decisions.RUN_MAC_TESTS'] ] condition: ne(stageDependencies.configure_build.configure.outputs['decisions.RUN_MAC_TESTS'],'') - pool: - os: macOS - name: ${{ parameters.pool }} - demands: - - Agent.OS -equals Darwin - - Agent.OSVersion -gtVersion $(minimumMacOSVersion) - - macOS.Name -equals ${{ parameters.macOSName }} - - XcodeChannel -equals ${{ parameters.xcodeChannel }} + ${{ if parameters.useACES }}: + pool: + os: macOS + name: $(CIBuildPoolACES) + demands: + - ImageOverride -equals $(CIBuildPoolACESImage) + ${{ else }}: + pool: + os: macOS + name: ${{ parameters.pool }} + demands: + - Agent.OS -equals Darwin + - Agent.OSVersion -gtVersion $(minimumMacOSVersion) + - macOS.Name -equals ${{ parameters.macOSName }} + - XcodeChannel -equals ${{ parameters.xcodeChannel }} steps: - template: build-mac-tests.yml diff --git a/tools/devops/automation/templates/build/build-stage.yml b/tools/devops/automation/templates/build/build-stage.yml index c03d3abe9a37..3bd8bc1f799b 100644 --- a/tools/devops/automation/templates/build/build-stage.yml +++ b/tools/devops/automation/templates/build/build-stage.yml @@ -38,6 +38,10 @@ parameters: - name: use1ES type: boolean + - name: useACES + type: boolean + default: false + jobs: # This job performs the build of the nuget pkgs and the framework pkgs. There are two interesting dependencies in this job: - job: build @@ -66,14 +70,21 @@ jobs: BRANCH_NAME: $[ replace(variables['Build.SourceBranch'], 'refs/heads/', '') ] XHARNESS_LABELS: $[ stageDependencies.configure_build.configure.outputs['labels.xharness_labels'] ] RUN_MAC_TESTS: $[ stageDependencies.configure_build.configure.outputs['decisions.RUN_MAC_TESTS'] ] - pool: - os: macOS - name: ${{ parameters.pool }} - demands: - - Agent.OS -equals Darwin - - Agent.OSVersion -gtVersion $(minimumMacOSVersion) - - macOS.Name -equals ${{ parameters.macOSName }} - - XcodeChannel -equals ${{ parameters.xcodeChannel }} + ${{ if parameters.useACES }}: + pool: + os: macOS + name: $(CIBuildPoolACES) + demands: + - ImageOverride -equals $(CIBuildPoolACESImage) + ${{ else }}: + pool: + os: macOS + name: ${{ parameters.pool }} + demands: + - Agent.OS -equals Darwin + - Agent.OSVersion -gtVersion $(minimumMacOSVersion) + - macOS.Name -equals ${{ parameters.macOSName }} + - XcodeChannel -equals ${{ parameters.xcodeChannel }} steps: - template: build-pkgs.yml diff --git a/tools/devops/automation/templates/build/build.yml b/tools/devops/automation/templates/build/build.yml index ae746a2d4776..1eff352e19b6 100644 --- a/tools/devops/automation/templates/build/build.yml +++ b/tools/devops/automation/templates/build/build.yml @@ -82,6 +82,17 @@ steps: HostedMacKeychainPassword: ${{ parameters.keyringPass }} - bash: | + set -eo pipefail + set -x + + XCODE_PATH=$(xcode-select -p 2>/dev/null || true) + if [[ "$XCODE_PATH" != */Contents/Developer || ! -d "$XCODE_PATH" ]]; then + echo "xcode-select does not point to an Xcode developer directory: '$XCODE_PATH'" + exit 1 + fi + export XCODE_DEVELOPER_ROOT="$XCODE_PATH" + export DEVELOPER_DIR="$XCODE_PATH" + make -C $(Build.SourcesDirectory)/$(BUILD_REPOSITORY_TITLE)/tools/devops provisioning displayName: 'Generate provisionator files.' @@ -93,8 +104,18 @@ steps: retryCount: ${{ parameters.retryCount }} # mono does give issues sometimes to download, we will retry - bash: | + set -eo pipefail set -x - set -e + + XCODE_PATH=$(xcode-select -p 2>/dev/null || true) + if [[ "$XCODE_PATH" != */Contents/Developer || ! -d "$XCODE_PATH" ]]; then + echo "xcode-select does not point to an Xcode developer directory: '$XCODE_PATH'" + exit 1 + fi + export XCODE_DEVELOPER_ROOT="$XCODE_PATH" + export DEVELOPER_DIR="$XCODE_PATH" + xcrun -k + ARGS=() ARGS+=(--ignore-all) ARGS+=(--provision-xcode-components) @@ -135,7 +156,17 @@ steps: displayName: "Configure install extra args" timeoutInMinutes: 5 - - bash: $(System.DefaultWorkingDirectory)/$(BUILD_REPOSITORY_TITLE)/configure + - bash: | + set -eo pipefail + set -x + + ARGS=() + XCODE_PATH=$(xcode-select -p 2>/dev/null || true) + if [[ -n "$XCODE_PATH" && -d "$XCODE_PATH" ]]; then + ARGS+=("--xcode=$XCODE_PATH") + fi + + ./configure "${ARGS[@]}" env: ${{ if eq(parameters.isPR, true) }}: IsPR: 'True' diff --git a/tools/devops/automation/templates/main-stage.yml b/tools/devops/automation/templates/main-stage.yml index c06cfb6e6828..671be873a5d1 100644 --- a/tools/devops/automation/templates/main-stage.yml +++ b/tools/devops/automation/templates/main-stage.yml @@ -56,6 +56,10 @@ parameters: type: string default: '' + - name: useACES + type: boolean + default: false + stages: - stage: configure_build @@ -103,6 +107,7 @@ stages: xqaCertPass: $(xqa--certificates--password) pool: ${{ parameters.pool }} use1ES: true + useACES: ${{ parameters.useACES }} # .NET Release Prep and VS Insertion Stages, only execute them when the build comes from an official branch and is not a schedule build from OneLoc # setting the stage at this level makes the graph of the UI look better, else the lines overlap and is not clear. diff --git a/tools/devops/automation/templates/pipelines/api-diff-pipeline.yml b/tools/devops/automation/templates/pipelines/api-diff-pipeline.yml index 26bb8c290581..f8fe235e2bb9 100644 --- a/tools/devops/automation/templates/pipelines/api-diff-pipeline.yml +++ b/tools/devops/automation/templates/pipelines/api-diff-pipeline.yml @@ -22,6 +22,10 @@ parameters: type: boolean default: false +- name: useACES + type: boolean + default: false + resources: repositories: - repository: self @@ -49,3 +53,4 @@ stages: isPR: ${{ parameters.isPR }} provisionatorChannel: ${{ parameters.provisionatorChannel }} pool: ${{ parameters.pool }} + useACES: ${{ parameters.useACES }} diff --git a/tools/devops/automation/templates/pipelines/run-tests-pipeline.yml b/tools/devops/automation/templates/pipelines/run-tests-pipeline.yml index 47e83ca8c65f..3da391226489 100644 --- a/tools/devops/automation/templates/pipelines/run-tests-pipeline.yml +++ b/tools/devops/automation/templates/pipelines/run-tests-pipeline.yml @@ -36,6 +36,11 @@ parameters: type: boolean default: false + - name: useACES + displayName: Use ACES shared pool for simulator tests + type: boolean + default: false + resources: repositories: - repository: self @@ -62,3 +67,4 @@ stages: runTests: ${{ parameters.runTests }} runWindowsIntegration: ${{ parameters.runWindowsIntegration }} buildPackages: ${{ parameters.buildPackages }} + useACES: ${{ parameters.useACES }} diff --git a/tools/devops/automation/templates/tests-stage.yml b/tools/devops/automation/templates/tests-stage.yml index 41f6fec21ddd..25bf39e80654 100644 --- a/tools/devops/automation/templates/tests-stage.yml +++ b/tools/devops/automation/templates/tests-stage.yml @@ -125,6 +125,10 @@ parameters: type: boolean default: false +- name: useACES + type: boolean + default: false + stages: - template: ./build/linux-build-verification.yml @@ -234,6 +238,7 @@ stages: xqaCertPass: $(xqa--certificates--password) condition: ${{ parameters.runTests }} postPipeline: ${{ not(parameters.buildPackages) }} + useACES: ${{ parameters.useACES }} - template: ./tests/publish-results.yml parameters: @@ -282,6 +287,7 @@ stages: gitHubToken: $(Github.Token) xqaCertPass: $(xqa--certificates--password) pool: $(PRBuildPool) + useACES: ${{ parameters.useACES }} - ${{ each config in parameters.macTestsConfigurations }}: - template: ./mac/stage.yml diff --git a/tools/devops/automation/templates/tests/build.yml b/tools/devops/automation/templates/tests/build.yml index 1eacbac94c70..4e071cd06d33 100644 --- a/tools/devops/automation/templates/tests/build.yml +++ b/tools/devops/automation/templates/tests/build.yml @@ -91,14 +91,19 @@ steps: # if we got to this point, it means that we do have at least 20 Gb to run the test, should # be more than enough, else the above script would have stopped the pipeline - bash: | + set -eo pipefail set -x - set -e cd "$BUILD_REPOSITORY_TITLE" + ARGS=() TEST_PLATFORM=$(echo "${{ parameters.testPlatform }}" | tr '[:upper:]' '[:lower:]') if test -n "$TEST_PLATFORM"; then ARGS+=("--disable-all-platforms") ARGS+=("--enable-$TEST_PLATFORM") fi + XCODE_PATH=$(xcode-select -p 2>/dev/null || true) + if [[ -n "$XCODE_PATH" && -d "$XCODE_PATH" ]]; then + ARGS+=("--xcode=$XCODE_PATH") + fi ./configure "${ARGS[@]}" displayName: 'Enable Xamarin and configure platforms' timeoutInMinutes: 1 @@ -208,6 +213,7 @@ steps: provisioning_script: $(System.DefaultWorkingDirectory)/$(BUILD_REPOSITORY_TITLE)/tools/devops/build-provisioning.csx displayName: 'Provisionator dependencies' provisionatorChannel: $(PROVISIONATOR_CHANNEL) + retryCount: 3 - bash: | set -x diff --git a/tools/devops/automation/templates/tests/stage.yml b/tools/devops/automation/templates/tests/stage.yml index 7301307f3f1f..cbe4c4c73881 100644 --- a/tools/devops/automation/templates/tests/stage.yml +++ b/tools/devops/automation/templates/tests/stage.yml @@ -66,6 +66,10 @@ parameters: type: boolean default: false +- name: useACES + type: boolean + default: false + stages: - stage: ${{ parameters.stageName }} displayName: ${{ parameters.displayName }} @@ -89,17 +93,29 @@ stages: BRANCH_NAME: $[ replace(variables['Build.SourceBranch'], 'refs/heads/', '') ] XHARNESS_LABELS: $[ stageDependencies.configure_build.configure.outputs['labels.xharness_labels'] ] DOTNET_PLATFORMS: $[ stageDependencies.configure_build.configure.outputs['configure_platforms.DOTNET_PLATFORMS'] ] - pool: - name: ${{ parameters.testPool }} - demands: - - Agent.OS -equals Darwin - - macOS.Name -equals ${{ parameters.macOSName }} - - macOS.Architecture -equals arm64 - - XcodeChannel -equals ${{ parameters.XcodeChannel }} - - ${{ each demand in parameters.extraBotDemands }}: - - demand - workspace: - clean: all + # The ACES pool is a virtual-machine pool; expose VM_VENDOR so tests + # gated by TestRuntime.AssertNotVirtualMachine get ignored there. + ${{ if parameters.useACES }}: + VM_VENDOR: ACES + ${{ if parameters.useACES }}: + pool: + name: $(CIBuildPoolACES) + demands: + - ImageOverride -equals $(CIBuildPoolACESImage) + workspace: + clean: all + ${{ else }}: + pool: + name: ${{ parameters.testPool }} + demands: + - Agent.OS -equals Darwin + - macOS.Name -equals ${{ parameters.macOSName }} + - macOS.Architecture -equals arm64 + - XcodeChannel -equals ${{ parameters.XcodeChannel }} + - ${{ each demand in parameters.extraBotDemands }}: + - demand + workspace: + clean: all strategy: matrix: $[ stageDependencies.configure_build.configure.outputs['test_matrix.SIMULATOR_TEST_MATRIX'] ] condition: ne(stageDependencies.configure_build.configure.outputs['labels.skip_all_tests'], 'True') diff --git a/tools/devops/automation/templates/variables/common.yml b/tools/devops/automation/templates/variables/common.yml index c596f2b28938..db4b945f053a 100644 --- a/tools/devops/automation/templates/variables/common.yml +++ b/tools/devops/automation/templates/variables/common.yml @@ -45,6 +45,46 @@ variables: - name: CIBuildPoolUrl value: 'https://devdiv.visualstudio.com/_settings/agentpools?poolId=367&view=agents' +# ACES shared pool wiring. +# +# The infrastructure for routing build / api-diff / simulator-test jobs onto +# the ACES shared pool ($(CIBuildPoolACES), image $(CIBuildPoolACESImage)) +# instead of the traditional Redmond Mac build pools ($(CIBuildPool) / +# $(PRBuildPool)) is plumbed through the `useACES` template parameter, which +# flows from each entry pipeline down through main-stage.yml / +# build-stage.yml, api-diff-stage.yml and tests/stage.yml. When `useACES` is +# true, those job-level templates emit: +# pool: +# name: $(CIBuildPoolACES) +# demands: ImageOverride -equals $(CIBuildPoolACESImage) +# Otherwise they emit the original pool + Darwin / macOS.Name / XcodeChannel +# demands. +# +# The switch MUST be a literal in the entry pipeline. Azure Pipelines only +# resolves `${{ }}` expressions at template-expansion time, and variables +# brought in via `- template:` (like this file) are not visible at that +# point — they are runtime-only. So there is no working "single global +# variable" we can read here; the literal lives in each entry pipeline: +# +# CI (uses CIBuildPool / ACES): +# - build-pipeline.yml -> useACES: true +# - run-ci-api-diff.yml -> useACES: true +# - run-post-ci-build-tests.yml -> useACES: true +# PR (uses PRBuildPool): +# - build-pull-request.yml -> useACES: false +# - run-pr-api-diff.yml -> useACES: false +# - run-post-pr-build-tests.yml -> useACES: false +# +# Flip the literal in the three files on the side you want to move (e.g. when +# the ACES pool is unavailable, or when you need an Xcode/macOS beta that is +# only present on the traditional pools) and queue a new run. +- name: CIBuildPoolACES + value: 'AcesShared' +- name: CIBuildPoolACESUrl + value: 'https://dev.azure.com/devdiv/DevDiv/_settings/agentqueues?queueId=8259&view=jobs' +- name: CIBuildPoolACESImage + value: 'ACES_VM_SharedPool_Tahoe' + # override the default build revision - name: BUILD_REVISION value: azure-devops-$(Build.SourceVersion) From cb474e4ca053c6f37d8301bf0d1d473ba2bf71c5 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 5 Jun 2026 09:51:36 +0200 Subject: [PATCH 78/79] [monotouch-test] Add timeouts to all Thread.Join calls (#25644) Replace all Thread.Join() calls (no timeout) with Thread.Join(TimeSpan) + Assert.That to fail the test if the thread doesn't complete within a reasonable time. Timeouts are chosen based on the work each thread does: - 5s for simple/fast operations (object creation, property checks) - 10s for I/O or RunLoop-based threads (timers, network listeners) - 30s for heavy workloads (10k iterations, GPU image processing) --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/monotouch-test/CoreAnimation/LayerTest.cs | 8 +++++--- tests/monotouch-test/CoreImage/CIKernelTests.cs | 6 ++++-- tests/monotouch-test/Foundation/NSStreamTest.cs | 6 ++++-- .../monotouch-test/Foundation/NotificationCenter.cs | 2 +- tests/monotouch-test/Foundation/ThreadTest.cs | 2 +- tests/monotouch-test/Foundation/TimerTest.cs | 6 +++--- tests/monotouch-test/ObjCRuntime/RegistrarTest.cs | 6 ++++-- tests/monotouch-test/ObjCRuntime/RuntimeTest.cs | 10 ++++++---- .../SystemConfiguration/NetworkReachabilityTest.cs | 12 ++++++++---- 9 files changed, 36 insertions(+), 22 deletions(-) diff --git a/tests/monotouch-test/CoreAnimation/LayerTest.cs b/tests/monotouch-test/CoreAnimation/LayerTest.cs index 5cf0be59a801..8c689285b031 100644 --- a/tests/monotouch-test/CoreAnimation/LayerTest.cs +++ b/tests/monotouch-test/CoreAnimation/LayerTest.cs @@ -131,9 +131,11 @@ public void TestBug26532 () } catch (Exception e) { ex = e; } - }); + }) { + IsBackground = true, + }; thread.Start (); - thread.Join (); + Assert.That (thread.Join (TimeSpan.FromSeconds (10)), Is.True, "Thread.Join timed out"); var watch = new Stopwatch (); watch.Start (); @@ -181,7 +183,7 @@ public void TestCALayerDelegateDispose () IsBackground = true, }; t.Start (); - t.Join (); + Assert.That (t.Join (TimeSpan.FromSeconds (5)), Is.True, "Thread.Join timed out"); GC.Collect (); NSRunLoop.Main.RunUntil (NSDate.Now.AddSeconds (0.1)); diff --git a/tests/monotouch-test/CoreImage/CIKernelTests.cs b/tests/monotouch-test/CoreImage/CIKernelTests.cs index 87cf3717ede0..3376f816d7e0 100644 --- a/tests/monotouch-test/CoreImage/CIKernelTests.cs +++ b/tests/monotouch-test/CoreImage/CIKernelTests.cs @@ -153,9 +153,11 @@ public void CIKernel_BasicTest () } catch (Exception ex2) { ex = ex2; } - }); + }) { + IsBackground = true, + }; t.Start (); - t.Join (); + Assert.That (t.Join (TimeSpan.FromSeconds (30)), Is.True, "Thread.Join timed out"); if (ex is not null) throw ex; } diff --git a/tests/monotouch-test/Foundation/NSStreamTest.cs b/tests/monotouch-test/Foundation/NSStreamTest.cs index d6c441e6403a..ce64ceed03cd 100644 --- a/tests/monotouch-test/Foundation/NSStreamTest.cs +++ b/tests/monotouch-test/Foundation/NSStreamTest.cs @@ -63,7 +63,9 @@ public void ConnectToHost () return; } - var listenThread = new Thread (new ParameterizedThreadStart (DebugListener)); + var listenThread = new Thread (new ParameterizedThreadStart (DebugListener)) { + IsBackground = true, + }; listenThread.Start (listener); NSStream.CreatePairWithSocketToHost (new IPEndPoint (IPAddress.Loopback, port), out read, out write); read.Open (); @@ -74,7 +76,7 @@ public void ConnectToHost () Assert.That (read.Read (result, 5), Is.EqualTo ((nint) 5)); for (int i = 0; i < 5; i++) Assert.That (result [i], Is.EqualTo (send [i] * 10)); - listenThread.Join (); + Assert.That (listenThread.Join (TimeSpan.FromSeconds (10)), Is.True, "listenThread.Join timed out"); listener.Stop (); read.Close (); write.Close (); diff --git a/tests/monotouch-test/Foundation/NotificationCenter.cs b/tests/monotouch-test/Foundation/NotificationCenter.cs index cab2406ee854..adea6d9968d4 100644 --- a/tests/monotouch-test/Foundation/NotificationCenter.cs +++ b/tests/monotouch-test/Foundation/NotificationCenter.cs @@ -140,7 +140,7 @@ public void ThreadSafe () // OK, we're done now, time to stop. stopSignal.Set (); for (var i = 0; i < threadCount; i++) { - threads [i].Join (); + Assert.That (threads [i].Join (TimeSpan.FromSeconds (5)), Is.True, $"Thread {i} failed to join within 5 seconds"); } Assert.That (ex, Is.Null, "Exception"); } diff --git a/tests/monotouch-test/Foundation/ThreadTest.cs b/tests/monotouch-test/Foundation/ThreadTest.cs index 120d545ebc14..93028e29763b 100644 --- a/tests/monotouch-test/Foundation/ThreadTest.cs +++ b/tests/monotouch-test/Foundation/ThreadTest.cs @@ -40,7 +40,7 @@ public void GetEntryAssemblyReturnsOk () IsBackground = true, }; t.Start (); - t.Join (); + Assert.That (t.Join (TimeSpan.FromSeconds (5)), Is.True, "Thread.Join timed out"); Assert.That (rv, Is.EqualTo (0)); } diff --git a/tests/monotouch-test/Foundation/TimerTest.cs b/tests/monotouch-test/Foundation/TimerTest.cs index 7e7ad52f4db4..be39f8e5be9b 100644 --- a/tests/monotouch-test/Foundation/TimerTest.cs +++ b/tests/monotouch-test/Foundation/TimerTest.cs @@ -42,7 +42,7 @@ public void Bug17793 () thread.Start (); Assert.That (evt.Wait (TimeSpan.FromSeconds (5)), Is.True, "Not signalled twice in 5s"); - thread.Join (); + Assert.That (thread.Join (TimeSpan.FromSeconds (10)), Is.True, "Thread.Join timed out"); } } @@ -70,7 +70,7 @@ public void Bug2443 () thread.Start (); Assert.That (evt.Wait (TimeSpan.FromSeconds (5)), Is.True, "Not signalled twice in 5s"); - thread.Join (); + Assert.That (thread.Join (TimeSpan.FromSeconds (10)), Is.True, "Thread.Join timed out"); } } @@ -101,7 +101,7 @@ public void CreateTimer_NewSignature () Assert.That (evt.WaitOne (TimeSpan.FromSeconds (5)), Is.True, "WaitOne"); Assert.That (result, Is.True, "result"); - thread.Join (); + Assert.That (thread.Join (TimeSpan.FromSeconds (10)), Is.True, "Thread.Join timed out"); } } } diff --git a/tests/monotouch-test/ObjCRuntime/RegistrarTest.cs b/tests/monotouch-test/ObjCRuntime/RegistrarTest.cs index b1f2ef35a054..c72e97e531d4 100644 --- a/tests/monotouch-test/ObjCRuntime/RegistrarTest.cs +++ b/tests/monotouch-test/ObjCRuntime/RegistrarTest.cs @@ -2122,9 +2122,11 @@ public void BlockCollection () } catch (Exception e) { ex = e; } - }); + }) { + IsBackground = true, + }; thread.Start (); - thread.Join (); + Assert.That (thread.Join (TimeSpan.FromSeconds (30)), Is.True, "Thread.Join timed out"); GC.Collect (); GC.WaitForPendingFinalizers (); TestRuntime.RunAsync (TimeSpan.FromSeconds (30), () => { }, () => ObjCBlockTester.FreedBlockCount > initialFreedCount); diff --git a/tests/monotouch-test/ObjCRuntime/RuntimeTest.cs b/tests/monotouch-test/ObjCRuntime/RuntimeTest.cs index 0fc4318e3168..10a84df16946 100644 --- a/tests/monotouch-test/ObjCRuntime/RuntimeTest.cs +++ b/tests/monotouch-test/ObjCRuntime/RuntimeTest.cs @@ -198,7 +198,7 @@ public bool UsableUntilDeadImpl () IsBackground = true, }; t.Start (); - t.Join (); + Assert.That (t.Join (TimeSpan.FromSeconds (5)), Is.True, "Thread.Join timed out"); // Now we have a handle to an object that may be garbage collected at any time. int counter = 0; @@ -297,7 +297,7 @@ public void FinalizationRaceCondition () IsBackground = true }; thread.Start (); - thread.Join (); + Assert.That (thread.Join (TimeSpan.FromSeconds (5)), Is.True, "Thread.Join timed out"); var getter1 = new Func ((key) => dict [key] as NSString); var getter2 = new Func ((key) => dict [key] as NSString); @@ -555,9 +555,11 @@ public void ResurrectedObjectsDisposedTest (Type type) var t1 = new Thread (() => { for (int i = 0; i < objects.Length; i++) Messaging.bool_objc_msgSend_IntPtr_int (invokerClassHandle, Selector.GetHandle ("invokeMe:wait:"), objects [i], 0); - }); + }) { + IsBackground = true, + }; t1.Start (); - t1.Join (); + Assert.That (t1.Join (TimeSpan.FromSeconds (5)), Is.True, "Thread.Join timed out"); // Collect all those managed wrappers, and make sure their finalizers are executed. GC.Collect (); diff --git a/tests/monotouch-test/SystemConfiguration/NetworkReachabilityTest.cs b/tests/monotouch-test/SystemConfiguration/NetworkReachabilityTest.cs index 0c57a89ef086..d1ceeeb70e39 100644 --- a/tests/monotouch-test/SystemConfiguration/NetworkReachabilityTest.cs +++ b/tests/monotouch-test/SystemConfiguration/NetworkReachabilityTest.cs @@ -220,10 +220,12 @@ public void SetNotification_GCHandleFreed () // Dispose to ensure GCHandle is freed reachability.Dispose (); } - }); + }) { + IsBackground = true, + }; thread.Start (); - thread.Join (); + Assert.That (thread.Join (TimeSpan.FromSeconds (5)), Is.True, "Thread.Join timed out"); // Force garbage collection GC.Collect (); @@ -263,10 +265,12 @@ public void SetNotification_GCHandleFreedWithNull () // Store weak reference to track if object is collected weakRefs [i] = new WeakReference (reachability); } - }); + }) { + IsBackground = true, + }; thread.Start (); - thread.Join (); + Assert.That (thread.Join (TimeSpan.FromSeconds (5)), Is.True, "Thread.Join timed out"); // Force garbage collection GC.Collect (); From 4c9e983ccc323b3581ea8e8f9434d02177b292d0 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 5 Jun 2026 12:38:08 +0200 Subject: [PATCH 79/79] [xtro] Don't report !unknown-type! for types with an [UnsupportedOSPlatform] attribute. (#25632) They're only present to keep binary compatibility, we already figured out they shouldn't be in the API (thus the `[UnsupportedOSPlatform]` attribute), so no need to report it. --------- Co-authored-by: Rolf Bjarne Kvinge --- .../MacCatalyst-CoreImage.ignore | 2 -- .../MacCatalyst-CoreML.ignore | 4 ---- .../iOS-CoreImage.ignore | 2 -- .../api-annotations-dotnet/iOS-CoreML.ignore | 4 ---- .../iOS-PhotosUI.ignore | 2 -- .../macOS-CoreML.ignore | 4 ---- .../xtro-sharpie/AttributeHelpers.cs | 22 +++++++++++++++++++ .../xtro-sharpie/ObjCInterfaceCheck.cs | 2 ++ 8 files changed, 24 insertions(+), 18 deletions(-) delete mode 100644 tests/xtro-sharpie/api-annotations-dotnet/MacCatalyst-CoreImage.ignore delete mode 100644 tests/xtro-sharpie/api-annotations-dotnet/MacCatalyst-CoreML.ignore delete mode 100644 tests/xtro-sharpie/api-annotations-dotnet/iOS-CoreML.ignore delete mode 100644 tests/xtro-sharpie/api-annotations-dotnet/iOS-PhotosUI.ignore delete mode 100644 tests/xtro-sharpie/api-annotations-dotnet/macOS-CoreML.ignore diff --git a/tests/xtro-sharpie/api-annotations-dotnet/MacCatalyst-CoreImage.ignore b/tests/xtro-sharpie/api-annotations-dotnet/MacCatalyst-CoreImage.ignore deleted file mode 100644 index c0280351bfe4..000000000000 --- a/tests/xtro-sharpie/api-annotations-dotnet/MacCatalyst-CoreImage.ignore +++ /dev/null @@ -1,2 +0,0 @@ -# removed in XAMCORE_5_0 -!unknown-type! CIFilterGenerator bound diff --git a/tests/xtro-sharpie/api-annotations-dotnet/MacCatalyst-CoreML.ignore b/tests/xtro-sharpie/api-annotations-dotnet/MacCatalyst-CoreML.ignore deleted file mode 100644 index 0d896ca7254b..000000000000 --- a/tests/xtro-sharpie/api-annotations-dotnet/MacCatalyst-CoreML.ignore +++ /dev/null @@ -1,4 +0,0 @@ -# These types are marked as unavailable in the headers if the min OS version is >= Xcode 16's OS versions, so xtro doesn't detect them as available. -# We're removing them in XAMCORE_5_0. -!unknown-type! MLModelCollection bound -!unknown-type! MLModelCollectionEntry bound diff --git a/tests/xtro-sharpie/api-annotations-dotnet/iOS-CoreImage.ignore b/tests/xtro-sharpie/api-annotations-dotnet/iOS-CoreImage.ignore index 03cc1b262fdd..704f52f5418b 100644 --- a/tests/xtro-sharpie/api-annotations-dotnet/iOS-CoreImage.ignore +++ b/tests/xtro-sharpie/api-annotations-dotnet/iOS-CoreImage.ignore @@ -1,5 +1,3 @@ ## OSX-only API, rdar #22524785 ## see https://trello.com/c/kpksFWto/6-22524785-coreimage-headers-discrepancies !missing-selector! CIKernel::setROISelector: not bound -# removed in XAMCORE_5_0 -!unknown-type! CIFilterGenerator bound diff --git a/tests/xtro-sharpie/api-annotations-dotnet/iOS-CoreML.ignore b/tests/xtro-sharpie/api-annotations-dotnet/iOS-CoreML.ignore deleted file mode 100644 index 0d896ca7254b..000000000000 --- a/tests/xtro-sharpie/api-annotations-dotnet/iOS-CoreML.ignore +++ /dev/null @@ -1,4 +0,0 @@ -# These types are marked as unavailable in the headers if the min OS version is >= Xcode 16's OS versions, so xtro doesn't detect them as available. -# We're removing them in XAMCORE_5_0. -!unknown-type! MLModelCollection bound -!unknown-type! MLModelCollectionEntry bound diff --git a/tests/xtro-sharpie/api-annotations-dotnet/iOS-PhotosUI.ignore b/tests/xtro-sharpie/api-annotations-dotnet/iOS-PhotosUI.ignore deleted file mode 100644 index 9fb677a7aaab..000000000000 --- a/tests/xtro-sharpie/api-annotations-dotnet/iOS-PhotosUI.ignore +++ /dev/null @@ -1,2 +0,0 @@ -# Removed in Xcode 14 from header -!unknown-type! PHEditingExtensionContext bound diff --git a/tests/xtro-sharpie/api-annotations-dotnet/macOS-CoreML.ignore b/tests/xtro-sharpie/api-annotations-dotnet/macOS-CoreML.ignore deleted file mode 100644 index 0d896ca7254b..000000000000 --- a/tests/xtro-sharpie/api-annotations-dotnet/macOS-CoreML.ignore +++ /dev/null @@ -1,4 +0,0 @@ -# These types are marked as unavailable in the headers if the min OS version is >= Xcode 16's OS versions, so xtro doesn't detect them as available. -# We're removing them in XAMCORE_5_0. -!unknown-type! MLModelCollection bound -!unknown-type! MLModelCollectionEntry bound diff --git a/tests/xtro-sharpie/xtro-sharpie/AttributeHelpers.cs b/tests/xtro-sharpie/xtro-sharpie/AttributeHelpers.cs index 1fe2e1d2773a..8904e46aa7a1 100644 --- a/tests/xtro-sharpie/xtro-sharpie/AttributeHelpers.cs +++ b/tests/xtro-sharpie/xtro-sharpie/AttributeHelpers.cs @@ -136,6 +136,28 @@ public static bool HasAnyDeprecationForCurrentPlatform (ICustomAttributeProvider return false; } + public static bool HasUnsupportedOSPlatform (ICustomAttributeProvider item) + { + if (Skip (item)) + return false; + + // Properties are a special case as it is generated on the property itself and not the individual get_ \ set_ methods + // Cecil does not have a link between the MethodDefinition we have and the hosting PropertyDefinition, so we have to dig to find the match + if (item is MethodDefinition method) { + var property = method.DeclaringType.Properties.FirstOrDefault (p => p.GetMethod == method || p.SetMethod == method); + if (property is not null && HasUnsupportedOSPlatform (property)) { + return true; + } + } + + foreach (var attribute in item.CustomAttributes) { + if (AttributeHelpers.HasUnsupportedOSPlatform (attribute, Helpers.Platform)) + return true; + } + + return false; + } + public static bool HasAnyObsoleted (ICustomAttributeProvider item) { if (Skip (item)) diff --git a/tests/xtro-sharpie/xtro-sharpie/ObjCInterfaceCheck.cs b/tests/xtro-sharpie/xtro-sharpie/ObjCInterfaceCheck.cs index 5d0fbaa6645f..a98cdd02f6fe 100644 --- a/tests/xtro-sharpie/xtro-sharpie/ObjCInterfaceCheck.cs +++ b/tests/xtro-sharpie/xtro-sharpie/ObjCInterfaceCheck.cs @@ -142,6 +142,8 @@ public override void EndVisit () // internal inner classes are not mapped to native ones if (type.IsNestedAssembly) continue; + if (AttributeHelpers.HasUnsupportedOSPlatform (type)) + continue; var framework = Helpers.MapFramework (type.Namespace); Log.On (framework).Add ($"!unknown-type! {extra} bound"); }