From 42cd13b5ba5d927caf772feb971bc34f1f8d5ff5 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 10 May 2024 21:00:56 -0500 Subject: [PATCH 001/133] Implement proper CallSite emit strategy for Mono --- .../Platforms/Runtimes/FxCoreBaseRuntime.cs | 2 +- .../DMDGenerators/DMDEmit.EmitCallSite.cs | 620 ++++++++++-------- src/MonoMod.Utils/Extensions.Unsafe.cs | 2 +- 3 files changed, 363 insertions(+), 261 deletions(-) diff --git a/src/MonoMod.Core/Platforms/Runtimes/FxCoreBaseRuntime.cs b/src/MonoMod.Core/Platforms/Runtimes/FxCoreBaseRuntime.cs index 17df3f1c..dc471a80 100644 --- a/src/MonoMod.Core/Platforms/Runtimes/FxCoreBaseRuntime.cs +++ b/src/MonoMod.Core/Platforms/Runtimes/FxCoreBaseRuntime.cs @@ -305,7 +305,7 @@ public virtual void Compile(MethodBase method) { // if the method is a virtual method on a value type, we want to do something to compile the real // method instead of just the unboxing stub. The RuntimeMethodHandle we got from the MethodBase - // points to the unboxing stub. We could use knowlege of the runtime to get the non-unboxing stub + // points to the unboxing stub. We could use knowledge of the runtime to get the non-unboxing stub // MethodDesc, then create a RuntimeMethodHandle for it, however that may change per-runtime. // If we want to implement that, we can implement TryGetCacnonicalMethodHandle. diff --git a/src/MonoMod.Utils/DMDGenerators/DMDEmit.EmitCallSite.cs b/src/MonoMod.Utils/DMDGenerators/DMDEmit.EmitCallSite.cs index 9e705963..a30ace63 100644 --- a/src/MonoMod.Utils/DMDGenerators/DMDEmit.EmitCallSite.cs +++ b/src/MonoMod.Utils/DMDGenerators/DMDEmit.EmitCallSite.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; -using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using CallSite = Mono.Cecil.CallSite; namespace MonoMod.Utils @@ -49,7 +49,7 @@ private static readonly MethodInfo? mDynamicMethod_AddRef .GetType("System.Reflection.Emit.DynamicScope")?.GetField("m_tokens", BindingFlags.NonPublic | BindingFlags.Instance); // Based on https://referencesource.microsoft.com/#mscorlib/system/reflection/mdimport.cs,74bfbae3c61889bc - private static readonly Type?[] CorElementTypes = new Type?[] { + private static readonly Type?[] CorElementTypes = [ null, // END typeof(void), // VOID typeof(bool), // BOOL @@ -80,7 +80,7 @@ private static readonly MethodInfo? mDynamicMethod_AddRef null, // FNPTR typeof(object), // OBJECT // all others don't have specific types associated - }; + ]; private abstract class TokenCreator { @@ -133,266 +133,274 @@ public override int GetTokenForSig(byte[] sig) // I assume, however, that we can't use SignatureHelper here because it is horribly broken on some (probably older) mono builds. } - internal static void _EmitCallSite(DynamicMethod dm, ILGenerator il, System.Reflection.Emit.OpCode opcode, CallSite csite) + private abstract class CallSiteEmitter { - /* The mess in this method is heavily based off of the code available at the following links: - * https://github.com/Microsoft/referencesource/blob/3b1eaf5203992df69de44c783a3eda37d3d4cd10/mscorlib/system/reflection/emit/dynamicmethod.cs#L791 - * https://github.com/Microsoft/referencesource/blob/3b1eaf5203992df69de44c783a3eda37d3d4cd10/mscorlib/system/reflection/emit/dynamicilgenerator.cs#L353 - * https://github.com/mono/mono/blob/82e573122a55482bf6592f36f819597238628385/mcs/class/corlib/System.Reflection.Emit/DynamicMethod.cs#L411 - * https://github.com/mono/mono/blob/82e573122a55482bf6592f36f819597238628385/mcs/class/corlib/System.Reflection.Emit/ILGenerator.cs#L800 - * https://github.com/dotnet/coreclr/blob/0fbd855e38bc3ec269479b5f6bf561dcfd67cbb6/src/System.Private.CoreLib/src/System/Reflection/Emit/SignatureHelper.cs#L57 - */ + public abstract void EmitCallSite(DynamicMethod dm, ILGenerator il, OpCode opcode, CallSite csite); + } - TokenCreator tokenCreator = DynamicMethod_AddRef is not null - ? new MonoTokenCreator(dm) : new NetTokenCreator(il); + private sealed class NetCallSiteEmitter : CallSiteEmitter + { + public override void EmitCallSite(DynamicMethod dm, ILGenerator il, OpCode opcode, CallSite csite) + { + /* The mess in this method is heavily based off of the code available at the following links: + * https://github.com/Microsoft/referencesource/blob/3b1eaf5203992df69de44c783a3eda37d3d4cd10/mscorlib/system/reflection/emit/dynamicmethod.cs#L791 + * https://github.com/Microsoft/referencesource/blob/3b1eaf5203992df69de44c783a3eda37d3d4cd10/mscorlib/system/reflection/emit/dynamicilgenerator.cs#L353 + * https://github.com/mono/mono/blob/82e573122a55482bf6592f36f819597238628385/mcs/class/corlib/System.Reflection.Emit/DynamicMethod.cs#L411 + * https://github.com/mono/mono/blob/82e573122a55482bf6592f36f819597238628385/mcs/class/corlib/System.Reflection.Emit/ILGenerator.cs#L800 + * https://github.com/dotnet/coreclr/blob/0fbd855e38bc3ec269479b5f6bf561dcfd67cbb6/src/System.Private.CoreLib/src/System/Reflection/Emit/SignatureHelper.cs#L57 + */ - var signature = new byte[32]; - var currSig = 0; - var sizeLoc = -1; + TokenCreator tokenCreator = DynamicMethod_AddRef is not null + ? new MonoTokenCreator(dm) : new NetTokenCreator(il); - // This expects a MdSigCallingConvention - AddData((byte)csite.CallingConvention); - sizeLoc = currSig++; + var signature = new byte[32]; + var currSig = 0; + var sizeLoc = -1; - var modReq = new List(); - var modOpt = new List(); + // We're emitting a StandAloneMethodSig - ResolveWithModifiers(csite.ReturnType, out var returnType, out var returnTypeModReq, out var returnTypeModOpt, modReq, modOpt); - AddArgument(returnType, returnTypeModReq, returnTypeModOpt); + AddData(((byte)csite.CallingConvention) | (csite.HasThis ? 0x20 : 0) | (csite.ExplicitThis ? 0x40 : 0)); + sizeLoc = currSig++; - foreach (var param in csite.Parameters) - { - if (param.ParameterType.IsSentinel) - AddElementType(0x41 /* CorElementType.Sentinel */); + var modReq = new List(); + var modOpt = new List(); - if (param.ParameterType.IsPinned) - { - AddElementType(0x45 /* CorElementType.Pinned */); - // AddArgument(param.ParameterType.ResolveReflection()); - // continue; - } + ResolveWithModifiers(csite.ReturnType, out var returnType, out var returnTypeModReq, out var returnTypeModOpt, modReq, modOpt); + AddArgument(returnType, returnTypeModReq, returnTypeModOpt); - ResolveWithModifiers(param.ParameterType, out var paramType, out var paramTypeModReq, out var paramTypeModOpt, modReq, modOpt); - AddArgument(paramType, paramTypeModReq, paramTypeModOpt); - } + foreach (var param in csite.Parameters) + { + if (param.ParameterType.IsSentinel) + AddElementType(0x41 /* CorElementType.Sentinel */); - AddElementType(0x00 /* CorElementType.End */); - - // For most signatures, this will set the number of elements in a byte which we have reserved for it. - // However, if we have a field signature, we don't set the length and return. - // If we have a signature with more than 128 arguments, we can't just set the number of elements, - // we actually have to allocate more space (e.g. shift everything in the array one or more spaces to the - // right. We do this by making a copy of the array and leaving the correct number of blanks. This new - // array is now set to be m_signature and we use the AddData method to set the number of elements properly. - // The forceCopy argument can be used to force SetNumberOfSignatureElements to make a copy of - // the array. This is useful for GetSignature which promises to trim the array to be the correct size anyway. - - byte[] temp; - int newSigSize; - var currSigHolder = currSig; - - // We need to have more bytes for the size. Figure out how many bytes here. - // Since we need to copy anyway, we're just going to take the cost of doing a - // new allocation. - if (csite.Parameters.Count < 0x80) - { - newSigSize = 1; - } - else if (csite.Parameters.Count < 0x4000) - { - newSigSize = 2; - } - else - { - newSigSize = 4; - } + if (param.ParameterType.IsPinned) + { + AddElementType(0x45 /* CorElementType.Pinned */); + // AddArgument(param.ParameterType.ResolveReflection()); + // continue; + } - // Allocate the new array. - temp = new byte[currSig + newSigSize - 1]; + ResolveWithModifiers(param.ParameterType, out var paramType, out var paramTypeModReq, out var paramTypeModOpt, modReq, modOpt); + AddArgument(paramType, paramTypeModReq, paramTypeModOpt); + } - // Copy the calling convention. The calling convention is always just one byte - // so we just copy that byte. Then copy the rest of the array, shifting everything - // to make room for the new number of elements. - temp[0] = signature[0]; - Buffer.BlockCopy(signature, sizeLoc + 1, temp, sizeLoc + newSigSize, currSigHolder - (sizeLoc + 1)); - signature = temp; + AddElementType(0x00 /* CorElementType.End */); + + // For most signatures, this will set the number of elements in a byte which we have reserved for it. + // However, if we have a field signature, we don't set the length and return. + // If we have a signature with more than 128 arguments, we can't just set the number of elements, + // we actually have to allocate more space (e.g. shift everything in the array one or more spaces to the + // right. We do this by making a copy of the array and leaving the correct number of blanks. This new + // array is now set to be m_signature and we use the AddData method to set the number of elements properly. + // The forceCopy argument can be used to force SetNumberOfSignatureElements to make a copy of + // the array. This is useful for GetSignature which promises to trim the array to be the correct size anyway. + + byte[] temp; + int newSigSize; + var currSigHolder = currSig; + + // We need to have more bytes for the size. Figure out how many bytes here. + // Since we need to copy anyway, we're just going to take the cost of doing a + // new allocation. + if (csite.Parameters.Count < 0x80) + { + newSigSize = 1; + } + else if (csite.Parameters.Count < 0x4000) + { + newSigSize = 2; + } + else + { + newSigSize = 4; + } - //Use the AddData method to add the number of elements appropriately compressed. - currSig = sizeLoc; - AddData(csite.Parameters.Count); - currSig = currSigHolder + (newSigSize - 1); + // Allocate the new array. + temp = new byte[currSig + newSigSize - 1]; - // This case will only happen if the user got the signature through - // InternalGetSignature first and then called GetSignature. - if (signature.Length > currSig) - { - temp = new byte[currSig]; - Array.Copy(signature, temp, currSig); + // Copy the calling convention. The calling convention is always just one byte + // so we just copy that byte. Then copy the rest of the array, shifting everything + // to make room for the new number of elements. + temp[0] = signature[0]; + Buffer.BlockCopy(signature, sizeLoc + 1, temp, sizeLoc + newSigSize, currSigHolder - (sizeLoc + 1)); signature = temp; - } - // Emit. + //Use the AddData method to add the number of elements appropriately compressed. + currSig = sizeLoc; + AddData(csite.Parameters.Count); + currSig = currSigHolder + (newSigSize - 1); - if (_ILGen_emit_int != null) - { - // Mono - _ILGen_make_room!.Invoke(il, new object[] { 6 }); - _ILGen_ll_emit!.Invoke(il, new object[] { opcode }); - _ILGen_emit_int!.Invoke(il, new object[] { tokenCreator.GetTokenForSig(signature) }); - } - else - { - // .NET - _ILGen_EnsureCapacity!.Invoke(il, new object[] { 7 }); - _ILGen_InternalEmit!.Invoke(il, new object[] { opcode }); - - // The only IL instruction that has VarPop behaviour, that takes a - // Signature token as a parameter is calli. Pop the parameters and - // the native function pointer. To be conservative, do not pop the - // this pointer since this information is not easily derived from - // SignatureHelper. - if (opcode.StackBehaviourPop == System.Reflection.Emit.StackBehaviour.Varpop) + // This case will only happen if the user got the signature through + // InternalGetSignature first and then called GetSignature. + if (signature.Length > currSig) { - // Pop the arguments and native function pointer off the stack. - _ILGen_UpdateStackSize!.Invoke(il, new object[] { opcode, -csite.Parameters.Count - 1 }); + temp = new byte[currSig]; + Array.Copy(signature, temp, currSig); + signature = temp; } - _ILGen_PutInteger4!.Invoke(il, new object[] { tokenCreator.GetTokenForSig(signature) }); - } + // Emit. - void AddArgument(Type clsArgument, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers) - { - if (optionalCustomModifiers != null) - foreach (var t in optionalCustomModifiers) - InternalAddTypeToken(tokenCreator.GetTokenForType(t), 0x20 /* CorElementType.CModOpt */); + if (_ILGen_emit_int != null) + { + // Mono + _ILGen_make_room!.Invoke(il, new object[] { 6 }); + _ILGen_ll_emit!.Invoke(il, new object[] { opcode }); + _ILGen_emit_int!.Invoke(il, new object[] { tokenCreator.GetTokenForSig(signature) }); + } + else + { + // .NET + _ILGen_EnsureCapacity!.Invoke(il, new object[] { 7 }); + _ILGen_InternalEmit!.Invoke(il, new object[] { opcode }); + + // The only IL instruction that has VarPop behaviour, that takes a + // Signature token as a parameter is calli. Pop the parameters and + // the native function pointer. To be conservative, do not pop the + // this pointer since this information is not easily derived from + // SignatureHelper. + if (opcode.StackBehaviourPop == System.Reflection.Emit.StackBehaviour.Varpop) + { + // Pop the arguments and native function pointer off the stack. + _ILGen_UpdateStackSize!.Invoke(il, new object[] { opcode, -csite.Parameters.Count - 1 }); + } - if (requiredCustomModifiers != null) - foreach (var t in requiredCustomModifiers) - InternalAddTypeToken(tokenCreator.GetTokenForType(t), 0x1F /* CorElementType.CModReqd */); + _ILGen_PutInteger4!.Invoke(il, new object[] { tokenCreator.GetTokenForSig(signature) }); + } - AddOneArgTypeHelper(clsArgument); - } + void AddArgument(Type clsArgument, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers) + { + if (optionalCustomModifiers != null) + foreach (var t in optionalCustomModifiers) + InternalAddTypeToken(tokenCreator.GetTokenForType(t), 0x20 /* CorElementType.CModOpt */); - void AddData(int data) - { - // A managed representation of CorSigCompressData; + if (requiredCustomModifiers != null) + foreach (var t in requiredCustomModifiers) + InternalAddTypeToken(tokenCreator.GetTokenForType(t), 0x1F /* CorElementType.CModReqd */); - if (currSig + 4 > signature!.Length) - { - signature = ExpandArray(signature); + AddOneArgTypeHelper(clsArgument); } - if (data <= 0x7F) + void AddData(int data) { - signature[currSig++] = (byte)(data & 0xFF); - } - else if (data <= 0x3FFF) - { - signature[currSig++] = (byte)((data >> 8) | 0x80); - signature[currSig++] = (byte)(data & 0xFF); + // A managed representation of CorSigCompressData; + + if (currSig + 4 > signature!.Length) + { + signature = ExpandArray(signature); + } + + if (data <= 0x7F) + { + signature[currSig++] = (byte)(data & 0xFF); + } + else if (data <= 0x3FFF) + { + signature[currSig++] = (byte)((data >> 8) | 0x80); + signature[currSig++] = (byte)(data & 0xFF); + } + else if (data <= 0x1FFFFFFF) + { + signature[currSig++] = (byte)((data >> 24) | 0xC0); + signature[currSig++] = (byte)((data >> 16) & 0xFF); + signature[currSig++] = (byte)((data >> 8) & 0xFF); + signature[currSig++] = (byte)((data) & 0xFF); + } + else + { + throw new ArgumentException("Integer or token was too large to be encoded."); + } } - else if (data <= 0x1FFFFFFF) + + byte[] ExpandArray(byte[] inArray, int requiredLength = -1) { - signature[currSig++] = (byte)((data >> 24) | 0xC0); - signature[currSig++] = (byte)((data >> 16) & 0xFF); - signature[currSig++] = (byte)((data >> 8) & 0xFF); - signature[currSig++] = (byte)((data) & 0xFF); + if (requiredLength < inArray.Length) + requiredLength = inArray.Length * 2; + + var outArray = new byte[requiredLength]; + Buffer.BlockCopy(inArray, 0, outArray, 0, inArray.Length); + return outArray; } - else + + void AddElementType(byte cvt) { - throw new ArgumentException("Integer or token was too large to be encoded."); - } - } + // Adds an element to the signature. A managed represenation of CorSigCompressElement + if (currSig + 1 > signature.Length) + signature = ExpandArray(signature); - byte[] ExpandArray(byte[] inArray, int requiredLength = -1) - { - if (requiredLength < inArray.Length) - requiredLength = inArray.Length * 2; + signature[currSig++] = cvt; + } - var outArray = new byte[requiredLength]; - Buffer.BlockCopy(inArray, 0, outArray, 0, inArray.Length); - return outArray; - } + void AddToken(int token) + { + // A managed represenation of CompressToken + // Pulls the token appart to get a rid, adds some appropriate bits + // to the token and then adds this to the signature. - void AddElementType(byte cvt) - { - // Adds an element to the signature. A managed represenation of CorSigCompressElement - if (currSig + 1 > signature.Length) - signature = ExpandArray(signature); + var rid = (token & 0x00FFFFFF); //This is RidFromToken; + var type = (token & unchecked((int)0xFF000000)); //This is TypeFromToken; - signature[currSig++] = cvt; - } + if (rid > 0x3FFFFFF) + { + // token is too big to be compressed + throw new ArgumentException("Integer or token was too large to be encoded."); + } - void AddToken(int token) - { - // A managed represenation of CompressToken - // Pulls the token appart to get a rid, adds some appropriate bits - // to the token and then adds this to the signature. + rid = (rid << 2); - var rid = (token & 0x00FFFFFF); //This is RidFromToken; - var type = (token & unchecked((int)0xFF000000)); //This is TypeFromToken; + // TypeDef is encoded with low bits 00 + // TypeRef is encoded with low bits 01 + // TypeSpec is encoded with low bits 10 + if (type == 0x01000000 /* MetadataTokenType.TypeRef */) + { + //if type is mdtTypeRef + rid |= 0x1; + } + else if (type == 0x1b000000 /* MetadataTokenType.TypeSpec */) + { + //if type is mdtTypeSpec + rid |= 0x2; + } - if (rid > 0x3FFFFFF) - { - // token is too big to be compressed - throw new ArgumentException("Integer or token was too large to be encoded."); + AddData(rid); } - rid = (rid << 2); - - // TypeDef is encoded with low bits 00 - // TypeRef is encoded with low bits 01 - // TypeSpec is encoded with low bits 10 - if (type == 0x01000000 /* MetadataTokenType.TypeRef */) + void InternalAddTypeToken(int clsToken, byte CorType) { - //if type is mdtTypeRef - rid |= 0x1; - } - else if (type == 0x1b000000 /* MetadataTokenType.TypeSpec */) - { - //if type is mdtTypeSpec - rid |= 0x2; + // Add a type token into signature. CorType will be either CorElementType.Class or CorElementType.ValueType + AddElementType(CorType); + AddToken(clsToken); } - AddData(rid); - } - - void InternalAddTypeToken(int clsToken, byte CorType) - { - // Add a type token into signature. CorType will be either CorElementType.Class or CorElementType.ValueType - AddElementType(CorType); - AddToken(clsToken); - } - - void AddOneArgTypeHelper(Type clsArgument) { AddOneArgTypeHelperWorker(clsArgument, false); } - void AddOneArgTypeHelperWorker(Type clsArgument, bool lastWasGenericInst) - { - if (clsArgument.IsGenericType && (!clsArgument.IsGenericTypeDefinition || !lastWasGenericInst)) + void AddOneArgTypeHelper(Type clsArgument) { AddOneArgTypeHelperWorker(clsArgument, false); } + void AddOneArgTypeHelperWorker(Type clsArgument, bool lastWasGenericInst) { - AddElementType(0x15 /* CorElementType.GenericInst */); + if (clsArgument.IsGenericType && (!clsArgument.IsGenericTypeDefinition || !lastWasGenericInst)) + { + AddElementType(0x15 /* CorElementType.GenericInst */); - AddOneArgTypeHelperWorker(clsArgument.GetGenericTypeDefinition(), true); + AddOneArgTypeHelperWorker(clsArgument.GetGenericTypeDefinition(), true); - var genargs = clsArgument.GetGenericArguments(); + var genargs = clsArgument.GetGenericArguments(); - AddData(genargs.Length); + AddData(genargs.Length); - foreach (var t in genargs) - AddOneArgTypeHelper(t); - } - else if (clsArgument.IsByRef) - { - AddElementType(0x10 /* CorElementType.ByRef */); - clsArgument = clsArgument.GetElementType() ?? clsArgument; - AddOneArgTypeHelper(clsArgument); - } - else if (clsArgument.IsPointer) - { - AddElementType(0x0F /* CorElementType.Ptr */); - AddOneArgTypeHelper(clsArgument.GetElementType() ?? clsArgument); - } - else if (clsArgument.IsArray) - { + foreach (var t in genargs) + AddOneArgTypeHelper(t); + } + else if (clsArgument.IsByRef) + { + AddElementType(0x10 /* CorElementType.ByRef */); + clsArgument = clsArgument.GetElementType() ?? clsArgument; + AddOneArgTypeHelper(clsArgument); + } + else if (clsArgument.IsPointer) + { + AddElementType(0x0F /* CorElementType.Ptr */); + AddOneArgTypeHelper(clsArgument.GetElementType() ?? clsArgument); + } + else if (clsArgument.IsArray) + { #if false if (clsArgument.IsArray && clsArgument == clsArgument.GetElementType().MakeArrayType()) { // .IsSZArray unavailable. AddElementType(0x1D /* CorElementType.SzArray */); @@ -400,71 +408,165 @@ void AddOneArgTypeHelperWorker(Type clsArgument, bool lastWasGenericInst) AddOneArgTypeHelper(clsArgument.GetElementType()); } else #endif - { - AddElementType(0x14 /* CorElementType.Array */); + { + AddElementType(0x14 /* CorElementType.Array */); - AddOneArgTypeHelper(clsArgument.GetElementType() ?? clsArgument); + AddOneArgTypeHelper(clsArgument.GetElementType() ?? clsArgument); - // put the rank information - var rank = clsArgument.GetArrayRank(); - AddData(rank); // rank - AddData(0); // upper bounds - AddData(rank); // lower bound - for (var i = 0; i < rank; i++) - AddData(0); + // put the rank information + var rank = clsArgument.GetArrayRank(); + AddData(rank); // rank + AddData(0); // upper bounds + AddData(rank); // lower bound + for (var i = 0; i < rank; i++) + AddData(0); + } } - } - else - { - // This isn't 100% accurate, but... oh well. - byte type = 0; // 0 is reserved anyway. - - for (var i = 0; i < CorElementTypes.Length; i++) + else { - if (clsArgument == CorElementTypes[i]) + // This isn't 100% accurate, but... oh well. + byte type = 0; // 0 is reserved anyway. + + for (var i = 0; i < CorElementTypes.Length; i++) { - type = (byte)i; - break; + if (clsArgument == CorElementTypes[i]) + { + type = (byte)i; + break; + } } - } - if (type == 0) - { - if (clsArgument == typeof(object)) + if (type == 0) + { + if (clsArgument == typeof(object)) + { + type = 0x1C /* CorElementType.Object */; + } + else if (clsArgument.IsValueType) + { + type = 0x11 /* CorElementType.ValueType */; + } + else + { + // Let's hope for the best. + type = 0x12 /* CorElementType.Class */; + } + } + + if (type <= 0x0E /* CorElementType.String */ || + type == 0x16 /* CorElementType.TypedByRef */ || + type == 0x18 /* CorElementType.I */ || + type == 0x19 /* CorElementType.U */ || + type == 0x1C /* CorElementType.Object */ + ) { - type = 0x1C /* CorElementType.Object */; + AddElementType(type); } else if (clsArgument.IsValueType) { - type = 0x11 /* CorElementType.ValueType */; + InternalAddTypeToken(tokenCreator.GetTokenForType(clsArgument), 0x11 /* CorElementType.ValueType */); } else { - // Let's hope for the best. - type = 0x12 /* CorElementType.Class */; + InternalAddTypeToken(tokenCreator.GetTokenForType(clsArgument), 0x12 /* CorElementType.Class */); } } + } + } + } - if (type <= 0x0E /* CorElementType.String */ || - type == 0x16 /* CorElementType.TypedByRef */ || - type == 0x18 /* CorElementType.I */ || - type == 0x19 /* CorElementType.U */ || - type == 0x1C /* CorElementType.Object */ - ) - { - AddElementType(type); - } - else if (clsArgument.IsValueType) - { - InternalAddTypeToken(tokenCreator.GetTokenForType(clsArgument), 0x11 /* CorElementType.ValueType */); - } - else - { - InternalAddTypeToken(tokenCreator.GetTokenForType(clsArgument), 0x12 /* CorElementType.Class */); - } + private sealed class MonoCallSiteEmitter : CallSiteEmitter + { + private FieldInfo SigHelper_callConv; + private FieldInfo SigHelper_unmanagedCallConv; + private FieldInfo SigHelper_arguments; + private FieldInfo SigHelper_modreqs; + private FieldInfo SigHelper_modopts; + + public MonoCallSiteEmitter() + { + var callConv = typeof(SignatureHelper).GetField("callConv", BindingFlags.Instance | BindingFlags.NonPublic); + var unmanagedCallConv = typeof(SignatureHelper).GetField("unmanagedCallConv", BindingFlags.Instance | BindingFlags.NonPublic); + var arguments = typeof(SignatureHelper).GetField("arguments", BindingFlags.Instance | BindingFlags.NonPublic); + var modreqs = typeof(SignatureHelper).GetField("modreqs", BindingFlags.Instance | BindingFlags.NonPublic); + var modopts = typeof(SignatureHelper).GetField("modopts", BindingFlags.Instance | BindingFlags.NonPublic); + + // if we hit this ctor, we should be running on Mono, which should mean these are all present + Helpers.Assert(callConv is not null); + Helpers.Assert(unmanagedCallConv is not null); + Helpers.Assert(arguments is not null); + Helpers.Assert(modreqs is not null); + Helpers.Assert(modopts is not null); + + SigHelper_callConv = callConv; + SigHelper_unmanagedCallConv = unmanagedCallConv; + SigHelper_arguments = arguments; + SigHelper_modreqs = modreqs; + SigHelper_modopts = modopts; + } + + public override void EmitCallSite(DynamicMethod dm, ILGenerator il, OpCode opcode, CallSite csite) + { + // On Mono, when its processing the tokens for a CallSite, it explicitly looks for a SignatureHelper, and so we CANNOT pass in + // a manually constructed signature. At all. Which sucks. It means that, on older Mono that have half-implemented SignatureHelpers, + // there's nothing we can do. + + var modReq = new List(); + var modOpt = new List(); + + // note: with the Mono signature helper, we can't represent modifiers on the return type + ResolveWithModifiers(csite.ReturnType, out var rawRetType, out _, out _, modReq, modOpt); + + // there's not a standard, public API for the metadata callconv field either, so we have to set it manually + var sigHelper = SignatureHelper.GetMethodSigHelper(CallingConventions.Standard, rawRetType); + + var arguments = new Type[csite.Parameters.Count]; + var modreqs = new Type[csite.Parameters.Count][]; + var modopts = new Type[csite.Parameters.Count][]; + + var managedCallConv = csite.CallingConvention switch + { + MethodCallingConvention.VarArg => CallingConventions.VarArgs, + _ => CallingConventions.Standard, + }; + if (csite.HasThis) managedCallConv |= CallingConventions.HasThis; + if (csite.ExplicitThis) managedCallConv |= CallingConventions.ExplicitThis; + + var unmanagedCallConv = csite.CallingConvention switch + { + MethodCallingConvention.C => CallingConvention.Cdecl, + MethodCallingConvention.StdCall => CallingConvention.StdCall, + MethodCallingConvention.ThisCall => CallingConvention.ThisCall, + MethodCallingConvention.FastCall => CallingConvention.FastCall, + _ => (CallingConvention)0, + }; + + for (var i = 0; i < csite.Parameters.Count; i++) + { + var param = csite.Parameters[i]; + + ResolveWithModifiers(param.ParameterType, out arguments[i], out modreqs[i], out modopts[i], modReq, modOpt); } + + // fill the signature helper + SigHelper_callConv.SetValue(sigHelper, managedCallConv); + SigHelper_unmanagedCallConv.SetValue(sigHelper, unmanagedCallConv); + SigHelper_arguments.SetValue(sigHelper, arguments); + SigHelper_modreqs.SetValue(sigHelper, modreqs); + SigHelper_modopts.SetValue(sigHelper, modopts); + + // emit the sighelper + _ILGen_make_room!.Invoke(il, new object[] { 6 }); + _ILGen_ll_emit!.Invoke(il, new object[] { opcode }); + _ILGen_emit_int!.Invoke(il, new object[] { DynamicMethod_AddRef!(dm, sigHelper) }); } + } + private static readonly CallSiteEmitter callSiteEmitter = DynamicMethod_AddRef is not null ? new MonoCallSiteEmitter() : new NetCallSiteEmitter(); + + internal static void _EmitCallSite(DynamicMethod dm, ILGenerator il, OpCode opcode, CallSite csite) + { + callSiteEmitter.EmitCallSite(dm, il, opcode, csite); } } diff --git a/src/MonoMod.Utils/Extensions.Unsafe.cs b/src/MonoMod.Utils/Extensions.Unsafe.cs index 54eeeeef..5df3b9e9 100644 --- a/src/MonoMod.Utils/Extensions.Unsafe.cs +++ b/src/MonoMod.Utils/Extensions.Unsafe.cs @@ -80,7 +80,7 @@ public static IntPtr GetLdftnPointer(this MethodBase m) lock (_GetLdftnPointerCache) { - return (_GetLdftnPointerCache[m] = dmd.Generate().CreateDelegate>() as Func)(); + return (_GetLdftnPointerCache[m] = dmd.Generate().CreateDelegate>())(); } } From 2843f9273533a3c69dbfeb6757b2589add882769 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 10 May 2024 21:02:01 -0500 Subject: [PATCH 002/133] Remove ref assemblies from packages --- tools/NuGet.targets | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/NuGet.targets b/tools/NuGet.targets index 8c6e1236..d0e73270 100644 --- a/tools/NuGet.targets +++ b/tools/NuGet.targets @@ -78,6 +78,7 @@ + \ No newline at end of file From b87ddc92dc3ea5c9103facc7a3f0313c712e2ec6 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 10 May 2024 21:36:25 -0500 Subject: [PATCH 003/133] Use PackageDownload instead of PackageReference in Backports for the build-time S.C.Immutable reference --- src/MonoMod.Backports/Directory.Build.targets | 14 ++++++-------- src/MonoMod.ILHelpers.Patcher/Program.cs | 8 +++++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/MonoMod.Backports/Directory.Build.targets b/src/MonoMod.Backports/Directory.Build.targets index 34ae00e8..668c6160 100644 --- a/src/MonoMod.Backports/Directory.Build.targets +++ b/src/MonoMod.Backports/Directory.Build.targets @@ -8,16 +8,14 @@ - - all;buildTransitive - all;buildTransitive - none - false - true - false - + + + + $([MSBuild]::NormalizePath('$(NuGetPackageRoot)', 'system.collections.immutable', '6.0.0')) + + diff --git a/src/MonoMod.ILHelpers.Patcher/Program.cs b/src/MonoMod.ILHelpers.Patcher/Program.cs index a8d1ed6a..79539338 100644 --- a/src/MonoMod.ILHelpers.Patcher/Program.cs +++ b/src/MonoMod.ILHelpers.Patcher/Program.cs @@ -1,5 +1,6 @@ using Mono.Cecil; using System; +using System.IO; [assembly: CLSCompliant(false)] @@ -12,11 +13,12 @@ var assemblyPath = args[0]; var verString = args[1]; var output = args.Length > 2 ? args[2] : null; +var hasSymbols = File.Exists(Path.ChangeExtension(assemblyPath, ".pdb")); -using var module = ModuleDefinition.ReadModule(assemblyPath, new(ReadingMode.Deferred) +using var module = ModuleDefinition.ReadModule(assemblyPath, new(ReadingMode.Immediate) { ReadWrite = true, - ReadSymbols = true, + ReadSymbols = hasSymbols, }); if (module.RuntimeVersion == verString && output is null) { @@ -27,7 +29,7 @@ var writerParams = new WriterParameters() { DeterministicMvid = true, - WriteSymbols = true, + WriteSymbols = hasSymbols, Timestamp = null, }; From 49dd6abef4739815dbf46b9c1601e92652128434 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 10 May 2024 21:41:51 -0500 Subject: [PATCH 004/133] Update Backports compatability suppressions to avoid reference to ref assembly --- .../CompatibilitySuppressions.xml | 124 ++++-------------- 1 file changed, 24 insertions(+), 100 deletions(-) diff --git a/src/MonoMod.Backports/CompatibilitySuppressions.xml b/src/MonoMod.Backports/CompatibilitySuppressions.xml index 24315c06..ac4346d0 100644 --- a/src/MonoMod.Backports/CompatibilitySuppressions.xml +++ b/src/MonoMod.Backports/CompatibilitySuppressions.xml @@ -4,219 +4,143 @@ CP0002 M:System.ValueTuple.#ctor - lib/net452/MonoMod.Backports.dll + lib/net35/MonoMod.Backports.dll lib/net452/MonoMod.Backports.dll - true CP0002 M:System.ValueTuple`1.#ctor - lib/net452/MonoMod.Backports.dll + lib/net35/MonoMod.Backports.dll lib/net452/MonoMod.Backports.dll - true CP0002 M:System.ValueTuple`2.#ctor - lib/net452/MonoMod.Backports.dll + lib/net35/MonoMod.Backports.dll lib/net452/MonoMod.Backports.dll - true CP0002 M:System.ValueTuple`3.#ctor - lib/net452/MonoMod.Backports.dll + lib/net35/MonoMod.Backports.dll lib/net452/MonoMod.Backports.dll - true CP0002 M:System.ValueTuple`4.#ctor - lib/net452/MonoMod.Backports.dll + lib/net35/MonoMod.Backports.dll lib/net452/MonoMod.Backports.dll - true CP0002 M:System.ValueTuple`5.#ctor - lib/net452/MonoMod.Backports.dll + lib/net35/MonoMod.Backports.dll lib/net452/MonoMod.Backports.dll - true CP0002 M:System.ValueTuple`6.#ctor - lib/net452/MonoMod.Backports.dll + lib/net35/MonoMod.Backports.dll lib/net452/MonoMod.Backports.dll - true CP0002 M:System.ValueTuple`7.#ctor - lib/net452/MonoMod.Backports.dll + lib/net35/MonoMod.Backports.dll lib/net452/MonoMod.Backports.dll - true CP0002 M:System.ValueTuple`8.#ctor - lib/net452/MonoMod.Backports.dll + lib/net35/MonoMod.Backports.dll lib/net452/MonoMod.Backports.dll - true CP0002 M:System.ValueTuple.#ctor lib/net452/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll + lib/net452/MonoMod.Backports.dll true CP0002 M:System.ValueTuple`1.#ctor lib/net452/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll + lib/net452/MonoMod.Backports.dll true CP0002 M:System.ValueTuple`2.#ctor lib/net452/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll + lib/net452/MonoMod.Backports.dll true CP0002 M:System.ValueTuple`3.#ctor lib/net452/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll + lib/net452/MonoMod.Backports.dll true CP0002 M:System.ValueTuple`4.#ctor lib/net452/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll + lib/net452/MonoMod.Backports.dll true CP0002 M:System.ValueTuple`5.#ctor lib/net452/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll + lib/net452/MonoMod.Backports.dll true CP0002 M:System.ValueTuple`6.#ctor lib/net452/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll + lib/net452/MonoMod.Backports.dll true CP0002 M:System.ValueTuple`7.#ctor lib/net452/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll + lib/net452/MonoMod.Backports.dll true CP0002 M:System.ValueTuple`8.#ctor lib/net452/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll + lib/net452/MonoMod.Backports.dll true - - CP0002 - M:System.ValueTuple.#ctor - ref/net35/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll - - - CP0002 - M:System.ValueTuple`1.#ctor - ref/net35/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll - - - CP0002 - M:System.ValueTuple`2.#ctor - ref/net35/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll - - - CP0002 - M:System.ValueTuple`3.#ctor - ref/net35/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll - - - CP0002 - M:System.ValueTuple`4.#ctor - ref/net35/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll - - - CP0002 - M:System.ValueTuple`5.#ctor - ref/net35/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll - - - CP0002 - M:System.ValueTuple`6.#ctor - ref/net35/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll - - - CP0002 - M:System.ValueTuple`7.#ctor - ref/net35/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll - - - CP0002 - M:System.ValueTuple`8.#ctor - ref/net35/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll - CP0002 M:System.HashCode.AddBytes(System.ReadOnlySpan{System.Byte}) - ref/netstandard2.0/MonoMod.Backports.dll - ref/netcoreapp2.1/MonoMod.Backports.dll + lib/netstandard2.0/MonoMod.Backports.dll + lib/netcoreapp2.1/MonoMod.Backports.dll CP0002 M:System.HashCode.AddBytes(System.ReadOnlySpan{System.Byte}) - ref/netstandard2.0/MonoMod.Backports.dll - ref/netstandard2.1/MonoMod.Backports.dll + lib/netstandard2.0/MonoMod.Backports.dll + lib/netstandard2.1/MonoMod.Backports.dll CP0008 T:System.Runtime.CompilerServices.TupleElementNamesAttribute - lib/net452/MonoMod.Backports.dll + lib/net35/MonoMod.Backports.dll lib/net452/MonoMod.Backports.dll - true CP0008 T:System.Runtime.CompilerServices.TupleElementNamesAttribute lib/net452/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll - true - - - CP0008 - T:System.Runtime.CompilerServices.TupleElementNamesAttribute - ref/net35/MonoMod.Backports.dll - ref/net452/MonoMod.Backports.dll - - - CP1002 - mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes - ref/net452/MonoMod.Backports.dll lib/net452/MonoMod.Backports.dll + true \ No newline at end of file From 69836166021d4022fdb75f93b3910c0d4bed1931 Mon Sep 17 00:00:00 2001 From: DaNike Date: Mon, 13 May 2024 16:20:25 -0500 Subject: [PATCH 005/133] Bump package versions --- src/MonoMod.Backports/MonoMod.Backports.csproj | 4 ++-- src/MonoMod.Core/MonoMod.Core.csproj | 2 +- src/MonoMod.RuntimeDetour/MonoMod.RuntimeDetour.csproj | 2 +- src/MonoMod.Utils/MonoMod.Utils.csproj | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/MonoMod.Backports/MonoMod.Backports.csproj b/src/MonoMod.Backports/MonoMod.Backports.csproj index 47a43881..37f385b9 100644 --- a/src/MonoMod.Backports/MonoMod.Backports.csproj +++ b/src/MonoMod.Backports/MonoMod.Backports.csproj @@ -10,8 +10,8 @@ false - 1.1.0 - 1.0.0 + 1.1.1 + 1.1.0 diff --git a/src/MonoMod.Core/MonoMod.Core.csproj b/src/MonoMod.Core/MonoMod.Core.csproj index 0d7e0e13..4709cf27 100644 --- a/src/MonoMod.Core/MonoMod.Core.csproj +++ b/src/MonoMod.Core/MonoMod.Core.csproj @@ -12,7 +12,7 @@ RuntimeDetour;detour;detours;$(PackageTags) - 1.1.0 + 1.1.1 1.0.0 true diff --git a/src/MonoMod.RuntimeDetour/MonoMod.RuntimeDetour.csproj b/src/MonoMod.RuntimeDetour/MonoMod.RuntimeDetour.csproj index 1da1cf94..ad47ec12 100644 --- a/src/MonoMod.RuntimeDetour/MonoMod.RuntimeDetour.csproj +++ b/src/MonoMod.RuntimeDetour/MonoMod.RuntimeDetour.csproj @@ -13,7 +13,7 @@ true - 25.1.0 + 25.1.1 25.0.0 diff --git a/src/MonoMod.Utils/MonoMod.Utils.csproj b/src/MonoMod.Utils/MonoMod.Utils.csproj index 1b58b7a4..8df1074a 100644 --- a/src/MonoMod.Utils/MonoMod.Utils.csproj +++ b/src/MonoMod.Utils/MonoMod.Utils.csproj @@ -9,7 +9,7 @@ enable false - 25.0.4 + 25.0.5 25.0.0 From d61aacf83be4c80d96cb6d7724e382bb1d6f96b3 Mon Sep 17 00:00:00 2001 From: DaNike Date: Mon, 13 May 2024 16:51:09 -0500 Subject: [PATCH 006/133] Update compatability suppressions (these seem to be quite finnicky) --- .../CompatibilitySuppressions.xml | 74 +++---------------- 1 file changed, 9 insertions(+), 65 deletions(-) diff --git a/src/MonoMod.Backports/CompatibilitySuppressions.xml b/src/MonoMod.Backports/CompatibilitySuppressions.xml index ac4346d0..8674ead1 100644 --- a/src/MonoMod.Backports/CompatibilitySuppressions.xml +++ b/src/MonoMod.Backports/CompatibilitySuppressions.xml @@ -55,69 +55,6 @@ lib/net35/MonoMod.Backports.dll lib/net452/MonoMod.Backports.dll - - CP0002 - M:System.ValueTuple.#ctor - lib/net452/MonoMod.Backports.dll - lib/net452/MonoMod.Backports.dll - true - - - CP0002 - M:System.ValueTuple`1.#ctor - lib/net452/MonoMod.Backports.dll - lib/net452/MonoMod.Backports.dll - true - - - CP0002 - M:System.ValueTuple`2.#ctor - lib/net452/MonoMod.Backports.dll - lib/net452/MonoMod.Backports.dll - true - - - CP0002 - M:System.ValueTuple`3.#ctor - lib/net452/MonoMod.Backports.dll - lib/net452/MonoMod.Backports.dll - true - - - CP0002 - M:System.ValueTuple`4.#ctor - lib/net452/MonoMod.Backports.dll - lib/net452/MonoMod.Backports.dll - true - - - CP0002 - M:System.ValueTuple`5.#ctor - lib/net452/MonoMod.Backports.dll - lib/net452/MonoMod.Backports.dll - true - - - CP0002 - M:System.ValueTuple`6.#ctor - lib/net452/MonoMod.Backports.dll - lib/net452/MonoMod.Backports.dll - true - - - CP0002 - M:System.ValueTuple`7.#ctor - lib/net452/MonoMod.Backports.dll - lib/net452/MonoMod.Backports.dll - true - - - CP0002 - M:System.ValueTuple`8.#ctor - lib/net452/MonoMod.Backports.dll - lib/net452/MonoMod.Backports.dll - true - CP0002 M:System.HashCode.AddBytes(System.ReadOnlySpan{System.Byte}) @@ -137,10 +74,17 @@ lib/net452/MonoMod.Backports.dll - CP0008 - T:System.Runtime.CompilerServices.TupleElementNamesAttribute + CP1002 + mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes lib/net452/MonoMod.Backports.dll lib/net452/MonoMod.Backports.dll true + + CP1002 + mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes + ref/net452/MonoMod.Backports.dll + lib/net452/MonoMod.Backports.dll + true + \ No newline at end of file From 85b6220ca9c57fe8b4d6d9b07b92834f44b8663b Mon Sep 17 00:00:00 2001 From: DaNike Date: Tue, 14 May 2024 22:29:20 -0500 Subject: [PATCH 007/133] Minor tweaks to SDK lookups and props --- global.json | 4 +++ nuget.config | 32 +++++++++++++++++++ src/Common/Common.csproj | 2 +- .../MonoMod.ILHelpers.ilproj | 2 +- .../MonoMod.SourceGen.Internal.csproj | 2 +- tools/Common.props | 4 +++ tools/tools.csproj | 2 +- 7 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 nuget.config diff --git a/global.json b/global.json index 7ddc0f3f..54725b04 100644 --- a/global.json +++ b/global.json @@ -3,5 +3,9 @@ "allowPrerelease": true, "rollForward": "latestMinor", "version": "8.0.100" + }, + "msbuild-sdks": { + "Microsoft.Build.NoTargets": "3.7.56", + "Microsoft.NET.Sdk.IL": "8.0.0" } } \ No newline at end of file diff --git a/nuget.config b/nuget.config new file mode 100644 index 00000000..43c17d23 --- /dev/null +++ b/nuget.config @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Common/Common.csproj b/src/Common/Common.csproj index d58e8520..d6af450e 100644 --- a/src/Common/Common.csproj +++ b/src/Common/Common.csproj @@ -1,2 +1,2 @@  - \ No newline at end of file + \ No newline at end of file diff --git a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj index 0578737d..fa892d36 100644 --- a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj +++ b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj @@ -1,4 +1,4 @@ - + diff --git a/src/MonoMod.SourceGen.Internal/MonoMod.SourceGen.Internal.csproj b/src/MonoMod.SourceGen.Internal/MonoMod.SourceGen.Internal.csproj index 9359747d..b1fdecc9 100644 --- a/src/MonoMod.SourceGen.Internal/MonoMod.SourceGen.Internal.csproj +++ b/src/MonoMod.SourceGen.Internal/MonoMod.SourceGen.Internal.csproj @@ -19,7 +19,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tools/Common.props b/tools/Common.props index 86d1b347..9176e47e 100644 --- a/tools/Common.props +++ b/tools/Common.props @@ -43,6 +43,10 @@ true false false + + + false + true diff --git a/tools/tools.csproj b/tools/tools.csproj index e00ea0ef..4d7b3159 100644 --- a/tools/tools.csproj +++ b/tools/tools.csproj @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file From 7f7b0dcba9f683ad4411753869f2721981216906 Mon Sep 17 00:00:00 2001 From: DaNike Date: Tue, 14 May 2024 22:42:50 -0500 Subject: [PATCH 008/133] Switch to using SDK artifacts layout --- azure-pipelines-postbuild.yml | 2 +- azure-pipelines.yml | 5 ++--- nuget.config | 2 +- tools/Common.props | 6 ++---- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/azure-pipelines-postbuild.yml b/azure-pipelines-postbuild.yml index 9b1d49ae..e6e7dc7f 100644 --- a/azure-pipelines-postbuild.yml +++ b/azure-pipelines-postbuild.yml @@ -10,7 +10,7 @@ steps: sourceFolder: '$(Agent.BuildDirectory)' # the leading **/ shouldn't be necessary, but it seems to be contents: | - **/artifacts/bin/**/${{parameters.buildConfiguration}}/${{parameters.targetFramework}}/**/* + **/artifacts/bin/**/${{lower(parameters.buildConfiguration)}}_${{parameters.targetFramework}}/**/* !**/MonoMod.FrameworkTests/**/* !**/Monomod.Backports.Filter/**/* !**/Monomod.ILHelpers.Patcher/**/* diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8e2176a4..7b2c6184 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -88,7 +88,6 @@ steps: inputs: packageType: sdk version: '8.0.x' - includePreviewVersions: true - task: NuGetToolInstaller@1 displayName: 'Update NuGet' inputs: @@ -142,7 +141,7 @@ steps: configuration: '$(buildConfiguration)' versioningScheme: 'byBuildNumber' #packagesToPack: 'MonoMod@(|.Common|.Utils|.RuntimeDetour|.RuntimeDetour.HookGen)/*.csproj' - packDirectory: '$(Build.ArtifactStagingDirectory)/nupkgs' + #packDirectory: '$(Build.ArtifactStagingDirectory)/nupkgs' - task: PublishBuildArtifacts@1 displayName: 'Artifacts: Publish: nupkgs' continueOnError: true @@ -155,7 +154,7 @@ steps: displayName: 'Artifacts: Push: nupkgs' inputs: command: push - packagesToPush: 'artifacts/packages/$(buildConfiguration)/*.nupkg' + packagesToPush: 'artifacts/package/${{lower(variables.buildConfiguration)}}/*.nupkg' publishVstsFeed: '572c97eb-dbaa-4a55-90e5-1d05431535bd/72ad568d-c548-4599-8b0a-9ea52b45bbbd' #- task: PowerShell@2 # condition: and(always(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables.pushNuGet, 'true')) diff --git a/nuget.config b/nuget.config index 43c17d23..f40e5a2c 100644 --- a/nuget.config +++ b/nuget.config @@ -2,7 +2,7 @@ - + diff --git a/tools/Common.props b/tools/Common.props index 9176e47e..fd6f5fb1 100644 --- a/tools/Common.props +++ b/tools/Common.props @@ -30,10 +30,8 @@ $(MMRootPath)docs\ - $(MMArtifactsPath)packages\$(Configuration)\ - $(MMArtifactsPath)obj\$(MSBuildProjectName)\ - $(BaseIntermediateOutputPath)$(Configuration)\ - $(MMArtifactsPath)bin\$(MSBuildProjectName)\ + true + $(MMArtifactsPath) true false From 5b77e7b4bb4a2c780b1ea1e7b43ccd818a05d774 Mon Sep 17 00:00:00 2001 From: DaNike Date: Tue, 14 May 2024 23:01:01 -0500 Subject: [PATCH 009/133] Pipelines fix #1 --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7b2c6184..17e33742 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -34,7 +34,7 @@ variables: buildConfiguration: 'Release' buildPlatform: 'Any CPU' xunitVer: '2.4.2' - xunit: '{0}{1}/.nuget/packages/xunit.runner.console/$(xunitVer)/tools/{2}/xunit.console.{3} artifacts/bin/MonoMod.UnitTest/Release/{6}/MonoMod.UnitTest.dll -xml testresults.{4}.{6}.xml {5}' + xunit: '{0}{1}/.nuget/packages/xunit.runner.console/$(xunitVer)/tools/{2}/xunit.console.{3} artifacts/bin/MonoMod.UnitTest/release_{6}/MonoMod.UnitTest.dll -xml testresults.{4}.{6}.xml {5}' name: '$(Date:y.M.d).$(Rev:r)' @@ -146,7 +146,7 @@ steps: displayName: 'Artifacts: Publish: nupkgs' continueOnError: true inputs: - pathtoPublish: 'artifacts/packages/$(buildConfiguration)' + pathtoPublish: 'artifacts/package/${{lower(buildConfiguration)}}' artifactName: '$(artifactPrefix)nupkgs$(artifactSuffix)' publishLocation: 'Container' - task: NuGetCommand@2 From 91d7e25bb6b0e82128b0e90e4af349f0439e8d61 Mon Sep 17 00:00:00 2001 From: DaNike Date: Tue, 14 May 2024 23:03:18 -0500 Subject: [PATCH 010/133] Pipelines fix 2 --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 17e33742..fa9b5bdd 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -146,7 +146,7 @@ steps: displayName: 'Artifacts: Publish: nupkgs' continueOnError: true inputs: - pathtoPublish: 'artifacts/package/${{lower(buildConfiguration)}}' + pathtoPublish: 'artifacts/package/${{lower(variables.buildConfiguration)}}' artifactName: '$(artifactPrefix)nupkgs$(artifactSuffix)' publishLocation: 'Container' - task: NuGetCommand@2 From e6d408eed7cd9bac00ebe69433580c4ed371debf Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 00:43:29 -0500 Subject: [PATCH 011/133] Initial build workflow pass --- .editorconfig | 3 + .github/.github.csproj | 2 + .github/workflows/build.yml | 202 ++++++++++++++++++++++++++++++++++++ MonoMod.sln | 11 ++ 4 files changed, 218 insertions(+) create mode 100644 .github/.github.csproj create mode 100644 .github/workflows/build.yml diff --git a/.editorconfig b/.editorconfig index 32e510c8..54d97477 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,6 +4,9 @@ root = true end_of_line = lf indent_style = space +[*.yml] +indent_size = 2 + [*.{csproj,ilproj,props,targets}] indent_size = 2 diff --git a/.github/.github.csproj b/.github/.github.csproj new file mode 100644 index 00000000..4d7b3159 --- /dev/null +++ b/.github/.github.csproj @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..676c8428 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,202 @@ +name: Build + Test +on: + push: + pull_request: + +defaults: + run: + shell: pwsh + +# We'll have a job for building (that runs on x64 machines only, one for each OS to make sure it actually builds) +# Then, we'll take the result from one of those (probaly Linux) and distribute build artifacts to testers to run +# a load of tests. This will (eventually) include ARM runners, where possible. +jobs: + compute-version: + runs-on: ubuntu-latest + outputs: + ver: ${{ steps.computever.outputs.ver }} + steps: + - id: computever + run: echo "ver=$(Get-Date -Format yy.MM.dd).${{ github.run_number }}.${{ github.run_attempt }}" >> $env:GITHUB_OUTPUT + + build: + needs: compute-version + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-13] + include: + - os: ubuntu-latest + name: Linux + upload-packages: true + upload-tests: true + - os: windows-latest + name: Windows + - os: macos-13 + name: MacOS + + name: 'Build #${{needs.compute-version.outputs.ver}} (${{matrix.name}})' + runs-on: ${{ matrix.os }} + env: + DOTNET_TELEMETRY_OPTOUT: true + DOTNET_NOLOGO: true + NUGET_PACKAGES: artifacts/pkg + VersionSuffix: daily.${{needs.compute-version.outputs.ver}} + steps: + - uses: actions/checkout@v4 + with: + lfs: true + submodules: recursive + + # TODO: maybe we can eventually use package locks for package caching? + - uses: actions/setup-dotnet@v4 + with: + global-json-file: global.json + + # NOTE: manual package caching + - uses: actions/cache@v4 + with: + path: ${{ env.NUGET_PACKAGES }} + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }}-v1 + restore-keys: + ${{ runner.os }}-nuget-v1 + - name: Restore + run: dotnet restore + + - name: Build + run: dotnet build --no-restore -c Release -p:ContinuousIntegrationBuild=true + + - name: Pack + run: dotnet pack --no-restore --no-build -c Release -p:ContinuousIntegrationBuild=true + + - name: Archive packages + uses: actions/upload-artifact@v4 + if: ${{ matrix.upload-packages }} + with: + name: packages + path: artifacts/package/release/*.nupkg + + - name: Upload test assets + uses: actions/upload-artifact@v4 + if: ${{ matrix.upload-tests }} + with: + name: test-assets + retention-days: 1 + path: | + artifacts/bin/MonoMod.UnitTests/*/**/* + + test: + needs: build + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-13, macos-latest] + dotnet-version: + - fx + - mono + - 2.1.x + - 3.0.x + - 3.1.x + - 5.0.x + - 6.0.x + - 7.0.x + - 8.0.x + include: + # some extra OS configuration info + - os: ubuntu-latest + osname: Linux + hasfx: false + hasmono: true + - os: windows-latest + osname: Windows + hasfx: true + hasmono: false + - os: macos-latest + osname: MacOS (latest) + hasfx: false + hasmono: true + - os: macos-13 + osname: MacOS 13 + hasfx: false + hasmono: true + + # .NET version -> TFM mapping + - dotnet-version: fx + tfm: net46 + - dotnet-version: mono + tfm: net46 + - dotnet-version: 2.1.x + tfm: netcoreapp2.1 + - dotnet-version: 3.0.x + tfm: netcoreapp3.0 + - dotnet-version: 3.1.x + tfm: netcoreapp3.1 + - dotnet-version: 5.0.x + tfm: net5.0 + - dotnet-version: 6.0.x + tfm: net6.0 + - dotnet-version: 7.0.x + tfm: net7.0 + - dotnet-version: 8.0.x + tfm: net8.0 + + exclude: + # remove Framework and Mono where appropriate + - haxfx: false + dotnet-version: fx + - hasmono: false + dotnet-version: mono + + name: Testing ${{ matrix.dotnet-version }} on ${{ matrix.osname }} + runs-on: ${{ matrix.os }} + env: + DOTNET_TELEMETRY_OPTOUT: true + DOTNET_NOLOGO: true + steps: + - name: Install global SDK + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + - name: Install test target runtime SDK + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ matrix.dotnet-version }} + - name: Download test assets + uses: actions/download-artifact@v4 + with: + name: test-assets + + - name: Run tests + if: ${{ matrix.dotnet-version != 'mono' }} + run: dotnet test -f ${{ matrix.tfm }} -l:"trx;LogFileName=testresults.${{matrix.os}}.${{matrix.dotnet-version}}.trx" release_${{ matrix.tfm }}/MonoMod.UnitTest.dll + + # TODO: Mono? + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: ${{ always() }} + with: + name: test-results-${{ matrix.osname }}-${{ matrix.dotnet-version }} + retention-days: 1 + path: '*.trx' + + publish-test-results: + if: ${{ always() }} + needs: test + runs-on: ubuntu-latest + name: Publish test results + permissions: + checks: write + pull-requests: write + steps: + - name: Download test results + uses: actions/download-artifact@v4 + with: + pattern: test-results-* + merge-multiple: true + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '*.trx' + comment_mode: ${{ (github.event.workflow_run.event == 'pull_request' || github.event_name == 'pull_request') && 'failures' || 'off' }} + report_individual_runs: true diff --git a/MonoMod.sln b/MonoMod.sln index 244d1c33..6d8a04c9 100644 --- a/MonoMod.sln +++ b/MonoMod.sln @@ -52,6 +52,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "iced", "external\iced.cspro EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoMod.Backports.Tasks", "src\MonoMod.Backports.Tasks\MonoMod.Backports.Tasks.csproj", "{ED959596-2FAE-4B3D-8DF8-8F0058E8068D}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = ".github", ".github\.github.csproj", "{179EC228-CED4-429E-934F-422C96273F74}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -188,6 +190,14 @@ Global {ED959596-2FAE-4B3D-8DF8-8F0058E8068D}.Release|Any CPU.Build.0 = Release|Any CPU {ED959596-2FAE-4B3D-8DF8-8F0058E8068D}.ReleaseTrace|Any CPU.ActiveCfg = ReleaseTrace|Any CPU {ED959596-2FAE-4B3D-8DF8-8F0058E8068D}.ReleaseTrace|Any CPU.Build.0 = ReleaseTrace|Any CPU + {179EC228-CED4-429E-934F-422C96273F74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {179EC228-CED4-429E-934F-422C96273F74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {179EC228-CED4-429E-934F-422C96273F74}.DebugTrace|Any CPU.ActiveCfg = DebugTrace|Any CPU + {179EC228-CED4-429E-934F-422C96273F74}.DebugTrace|Any CPU.Build.0 = DebugTrace|Any CPU + {179EC228-CED4-429E-934F-422C96273F74}.Release|Any CPU.ActiveCfg = Release|Any CPU + {179EC228-CED4-429E-934F-422C96273F74}.Release|Any CPU.Build.0 = Release|Any CPU + {179EC228-CED4-429E-934F-422C96273F74}.ReleaseTrace|Any CPU.ActiveCfg = ReleaseTrace|Any CPU + {179EC228-CED4-429E-934F-422C96273F74}.ReleaseTrace|Any CPU.Build.0 = ReleaseTrace|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -195,6 +205,7 @@ Global GlobalSection(NestedProjects) = preSolution {7B682BC4-241C-4B74-8EDC-2D7677A79337} = {BCABFBF3-BB48-4C21-9A52-E140015570A9} {119A01EE-2238-423C-BE5B-48C68D7D14A7} = {69977055-8E22-4EB0-850D-6EE7D592BC04} + {179EC228-CED4-429E-934F-422C96273F74} = {BCABFBF3-BB48-4C21-9A52-E140015570A9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E10A3D07-6D9A-4898-B95F-268636312A67} From da3c9aa9cba7e147ee680cb7092397b00466964e Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 00:49:31 -0500 Subject: [PATCH 012/133] Fix stupid error 1 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 676c8428..97a48db3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -141,7 +141,7 @@ jobs: exclude: # remove Framework and Mono where appropriate - - haxfx: false + - hasfx: false dotnet-version: fx - hasmono: false dotnet-version: mono From 47fc75b62877054f3d7905cced03cf8f6b2f85aa Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 00:51:21 -0500 Subject: [PATCH 013/133] Stupid errors 2 --- .github/workflows/build.yml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 97a48db3..52851791 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -104,20 +104,12 @@ jobs: # some extra OS configuration info - os: ubuntu-latest osname: Linux - hasfx: false - hasmono: true - os: windows-latest osname: Windows - hasfx: true - hasmono: false - os: macos-latest osname: MacOS (latest) - hasfx: false - hasmono: true - os: macos-13 osname: MacOS 13 - hasfx: false - hasmono: true # .NET version -> TFM mapping - dotnet-version: fx @@ -141,9 +133,9 @@ jobs: exclude: # remove Framework and Mono where appropriate - - hasfx: false + - os: [ubuntu-latest, macos-latest, macos-13] dotnet-version: fx - - hasmono: false + - os: windows-latest dotnet-version: mono name: Testing ${{ matrix.dotnet-version }} on ${{ matrix.osname }} From 63623c21aad7dcf3bda7a6e6991f8fc45032a01d Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 00:53:45 -0500 Subject: [PATCH 014/133] NUGET_PACKAGES needs an absolute path apparently --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 52851791..290ab54e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,7 +39,7 @@ jobs: env: DOTNET_TELEMETRY_OPTOUT: true DOTNET_NOLOGO: true - NUGET_PACKAGES: artifacts/pkg + NUGET_PACKAGES: ${{github.workspace}}/artifacts/pkg VersionSuffix: daily.${{needs.compute-version.outputs.ver}} steps: - uses: actions/checkout@v4 From 7f2f8559312111c11c0e9df8130a6dd0de5333ce Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 00:58:08 -0500 Subject: [PATCH 015/133] "1.1.1-daily.24.05.15 is not a valid version string" --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 290ab54e..97b800f0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: ver: ${{ steps.computever.outputs.ver }} steps: - id: computever - run: echo "ver=$(Get-Date -Format yy.MM.dd).${{ github.run_number }}.${{ github.run_attempt }}" >> $env:GITHUB_OUTPUT + run: echo "ver=$(Get-Date -Format y.M.d).${{ github.run_number }}.${{ github.run_attempt }}" >> $env:GITHUB_OUTPUT build: needs: compute-version From ca248942544bc7cb7c65908604dc96af1fe84a7f Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 01:10:22 -0500 Subject: [PATCH 016/133] dotnet build parameters --- .github/workflows/build.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 97b800f0..8b7d46af 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,13 +60,20 @@ jobs: restore-keys: ${{ runner.os }}-nuget-v1 - name: Restore - run: dotnet restore + run: dotnet restore -bl:restore.binlog -noAutoRsp - name: Build - run: dotnet build --no-restore -c Release -p:ContinuousIntegrationBuild=true + run: dotnet build --no-restore -c Release -p:ContinuousIntegrationBuild=true -bl:build.binlog -clp:NoSummary -noAutoRsp - name: Pack - run: dotnet pack --no-restore --no-build -c Release -p:ContinuousIntegrationBuild=true + run: dotnet pack --no-restore -c Release -p:ContinuousIntegrationBuild=true -bl:pack.binlog -clp:NoSummary -noAutoRsp + + - name: Upload binlogs + uses: actions/upload-artifact@v4 + if: ${{ always() }} + with: + name: binlogs-${{ matrix.osname }} + path: '*.binlog' - name: Archive packages uses: actions/upload-artifact@v4 From c492fb8bc1f84a8eafdb6729ddef0d8464a23f88 Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 01:18:49 -0500 Subject: [PATCH 017/133] Proper name for unit test project --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8b7d46af..f7b67c23 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -89,7 +89,7 @@ jobs: name: test-assets retention-days: 1 path: | - artifacts/bin/MonoMod.UnitTests/*/**/* + artifacts/bin/MonoMod.UnitTest/*/**/* test: needs: build From 748355e5727c0525a45a5b2cf8dfc50ccb146909 Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 01:21:08 -0500 Subject: [PATCH 018/133] Fix binlogs artifact name --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f7b67c23..9df8a0e0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,8 +72,9 @@ jobs: uses: actions/upload-artifact@v4 if: ${{ always() }} with: - name: binlogs-${{ matrix.osname }} + name: binlogs-${{ matrix.name }} path: '*.binlog' + retention-days: 7 - name: Archive packages uses: actions/upload-artifact@v4 From 924f03e4805e25ff48b06cc5c3230afac8ea7263 Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 01:44:17 -0500 Subject: [PATCH 019/133] Fix .NET Framework and Mono skips --- .github/workflows/build.yml | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9df8a0e0..1582f5c6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -97,7 +97,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest, macos-13, macos-latest] + os: [ubuntu-latest, windows-latest, macos-13] dotnet-version: - fx - mono @@ -122,31 +122,42 @@ jobs: # .NET version -> TFM mapping - dotnet-version: fx tfm: net46 + fxname: .NET Framework 4.x - dotnet-version: mono tfm: net46 + fxname: Mono - dotnet-version: 2.1.x tfm: netcoreapp2.1 + fxname: .NET Core 2.1 - dotnet-version: 3.0.x tfm: netcoreapp3.0 + fxname: .NET Core 3.0 - dotnet-version: 3.1.x tfm: netcoreapp3.1 + fxname: .NET Core 3.1 - dotnet-version: 5.0.x tfm: net5.0 + fxname: .NET 5.0 - dotnet-version: 6.0.x tfm: net6.0 + fxname: .NET 6.0 - dotnet-version: 7.0.x tfm: net7.0 + fxname: .NET 7.0 - dotnet-version: 8.0.x tfm: net8.0 + fxname: .NET 8.0 exclude: # remove Framework and Mono where appropriate - - os: [ubuntu-latest, macos-latest, macos-13] + - os: ubuntu-latest + dotnet-version: fx + - os: macos-13 dotnet-version: fx - os: windows-latest dotnet-version: mono - name: Testing ${{ matrix.dotnet-version }} on ${{ matrix.osname }} + name: Test ${{ matrix.fxname }} on ${{ matrix.osname }} runs-on: ${{ matrix.os }} env: DOTNET_TELEMETRY_OPTOUT: true @@ -156,15 +167,16 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: 8.0.x - - name: Install test target runtime SDK - uses: actions/setup-dotnet@v4 - with: - dotnet-version: ${{ matrix.dotnet-version }} - name: Download test assets uses: actions/download-artifact@v4 with: name: test-assets + - name: Install test target runtime SDK + uses: actions/setup-dotnet@v4 + if: ${{ matrix.dotnet-version != 'mono' && matrix.dotnet-version != 'fx' }} + with: + dotnet-version: ${{ matrix.dotnet-version }} - name: Run tests if: ${{ matrix.dotnet-version != 'mono' }} run: dotnet test -f ${{ matrix.tfm }} -l:"trx;LogFileName=testresults.${{matrix.os}}.${{matrix.dotnet-version}}.trx" release_${{ matrix.tfm }}/MonoMod.UnitTest.dll @@ -177,7 +189,7 @@ jobs: with: name: test-results-${{ matrix.osname }}-${{ matrix.dotnet-version }} retention-days: 1 - path: '*.trx' + path: 'TestResults/*.trx' publish-test-results: if: ${{ always() }} From 9b04ee933a1bbfdf43953cde77f206fbcd5fd3b0 Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 01:53:53 -0500 Subject: [PATCH 020/133] Tweak env declaration location, prevent test result publishing when build failed --- .github/workflows/build.yml | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1582f5c6..d238fe95 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,11 @@ defaults: run: shell: pwsh +env: + DOTNET_TELEMETRY_OPTOUT: true + DOTNET_NOLOGO: true + NUGET_PACKAGES: ${{github.workspace}}/artifacts/pkg + # We'll have a job for building (that runs on x64 machines only, one for each OS to make sure it actually builds) # Then, we'll take the result from one of those (probaly Linux) and distribute build artifacts to testers to run # a load of tests. This will (eventually) include ARM runners, where possible. @@ -37,27 +42,27 @@ jobs: name: 'Build #${{needs.compute-version.outputs.ver}} (${{matrix.name}})' runs-on: ${{ matrix.os }} env: - DOTNET_TELEMETRY_OPTOUT: true - DOTNET_NOLOGO: true - NUGET_PACKAGES: ${{github.workspace}}/artifacts/pkg VersionSuffix: daily.${{needs.compute-version.outputs.ver}} steps: - - uses: actions/checkout@v4 + - name: Checkout + uses: actions/checkout@v4 with: lfs: true submodules: recursive # TODO: maybe we can eventually use package locks for package caching? - - uses: actions/setup-dotnet@v4 + - name: Install .NET SDK + uses: actions/setup-dotnet@v4 with: global-json-file: global.json # NOTE: manual package caching - - uses: actions/cache@v4 + - name: Cache restored NuGet packages + uses: actions/cache@v4 with: path: ${{ env.NUGET_PACKAGES }} key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }}-v1 - restore-keys: + restore-keys: | ${{ runner.os }}-nuget-v1 - name: Restore run: dotnet restore -bl:restore.binlog -noAutoRsp @@ -159,9 +164,6 @@ jobs: name: Test ${{ matrix.fxname }} on ${{ matrix.osname }} runs-on: ${{ matrix.os }} - env: - DOTNET_TELEMETRY_OPTOUT: true - DOTNET_NOLOGO: true steps: - name: Install global SDK uses: actions/setup-dotnet@v4 @@ -192,8 +194,8 @@ jobs: path: 'TestResults/*.trx' publish-test-results: - if: ${{ always() }} - needs: test + needs: [build, test] + if: ${{ always() && needs.build.result == 'success' }} runs-on: ubuntu-latest name: Publish test results permissions: From 61e46045c3f75d0f05b8e17b7c5384dc20083700 Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 02:02:38 -0500 Subject: [PATCH 021/133] Load cached NuGet packages for .NET Core 2.1 test runs --- .github/workflows/build.yml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d238fe95..ef5ec751 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,6 +38,9 @@ jobs: name: Windows - os: macos-13 name: MacOS + + outputs: + ${{ matrix.os }}-cachekey: ${{ steps.cachekey.outputs.${{runner.os}}-cachekey }} name: 'Build #${{needs.compute-version.outputs.ver}} (${{matrix.name}})' runs-on: ${{ matrix.os }} @@ -56,12 +59,16 @@ jobs: with: global-json-file: global.json - # NOTE: manual package caching + - name: Compute cache key + id: cachekey + run: echo "${{ runner.os }}-cachekey=${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }}-v1" >> $env:GITHUB_OUTPUT + + # NOTE: manual package caching - name: Cache restored NuGet packages uses: actions/cache@v4 with: path: ${{ env.NUGET_PACKAGES }} - key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }}-v1 + key: ${{ steps.cachekey.outputs.${{runner.os}}-cachekey }} restore-keys: | ${{ runner.os }}-nuget-v1 - name: Restore @@ -173,6 +180,14 @@ jobs: uses: actions/download-artifact@v4 with: name: test-assets + + # For some reason .NET Core 2.1 (and only it!) needs a package restore; it looks there for the testhost for some reason + - name: Load cached NuGet packages + uses: actions/cache@v4 + if: ${{ matrix.tfm == 'netcoreapp2.1' }} + with: + path: ${{ env.NUGET_PACKAGES }} + key: ${{ needs.build.outputs.${{matrix.os}}-cachekey }} - name: Install test target runtime SDK uses: actions/setup-dotnet@v4 From 302335d1fb4283a4c4c8326301ee3733a15bfed5 Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 02:04:39 -0500 Subject: [PATCH 022/133] Explicitly enumerate outputs names --- .github/workflows/build.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ef5ec751..383cf4c6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,7 +40,9 @@ jobs: name: MacOS outputs: - ${{ matrix.os }}-cachekey: ${{ steps.cachekey.outputs.${{runner.os}}-cachekey }} + ubuntu-latest-cachekey: ${{ steps.cachekey.outputs.ubuntu-latest-cachekey }} + windows-latest-cachekey: ${{ steps.cachekey.outputs.windows-latest-cachekey }} + macos-13-cachekey: ${{ steps.cachekey.outputs.macos-13-cachekey }} name: 'Build #${{needs.compute-version.outputs.ver}} (${{matrix.name}})' runs-on: ${{ matrix.os }} @@ -61,14 +63,14 @@ jobs: - name: Compute cache key id: cachekey - run: echo "${{ runner.os }}-cachekey=${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }}-v1" >> $env:GITHUB_OUTPUT + run: echo "${{ matrix.os }}-cachekey=${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }}-v1" >> $env:GITHUB_OUTPUT # NOTE: manual package caching - name: Cache restored NuGet packages uses: actions/cache@v4 with: path: ${{ env.NUGET_PACKAGES }} - key: ${{ steps.cachekey.outputs.${{runner.os}}-cachekey }} + key: ${{ steps.cachekey.outputs.${{ matrix.os }}-cachekey }} restore-keys: | ${{ runner.os }}-nuget-v1 - name: Restore From cd1f14c73503d4a9bc24e582ba328cc1a1701822 Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 02:06:12 -0500 Subject: [PATCH 023/133] No nested evaluations --- .github/workflows/build.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 383cf4c6..0e269ebe 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -63,14 +63,17 @@ jobs: - name: Compute cache key id: cachekey - run: echo "${{ matrix.os }}-cachekey=${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }}-v1" >> $env:GITHUB_OUTPUT + run: | + $key = "${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }}-v1"; + echo "cachekey=$key" >> $env:GITHUB_OUTPUT + echo "${{ matrix.os }}-cachekey=$key" >> $env:GITHUB_OUTPUT # NOTE: manual package caching - name: Cache restored NuGet packages uses: actions/cache@v4 with: path: ${{ env.NUGET_PACKAGES }} - key: ${{ steps.cachekey.outputs.${{ matrix.os }}-cachekey }} + key: ${{ steps.cachekey.outputs.cachekey }} restore-keys: | ${{ runner.os }}-nuget-v1 - name: Restore From e2f64897a86b23cf494e4ee1589227b0c57b0f21 Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 02:08:05 -0500 Subject: [PATCH 024/133] Undo most of that --- .github/workflows/build.yml | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0e269ebe..fa0c5893 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,12 +38,7 @@ jobs: name: Windows - os: macos-13 name: MacOS - - outputs: - ubuntu-latest-cachekey: ${{ steps.cachekey.outputs.ubuntu-latest-cachekey }} - windows-latest-cachekey: ${{ steps.cachekey.outputs.windows-latest-cachekey }} - macos-13-cachekey: ${{ steps.cachekey.outputs.macos-13-cachekey }} - + name: 'Build #${{needs.compute-version.outputs.ver}} (${{matrix.name}})' runs-on: ${{ matrix.os }} env: @@ -60,20 +55,13 @@ jobs: uses: actions/setup-dotnet@v4 with: global-json-file: global.json - - - name: Compute cache key - id: cachekey - run: | - $key = "${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }}-v1"; - echo "cachekey=$key" >> $env:GITHUB_OUTPUT - echo "${{ matrix.os }}-cachekey=$key" >> $env:GITHUB_OUTPUT - + # NOTE: manual package caching - name: Cache restored NuGet packages uses: actions/cache@v4 with: path: ${{ env.NUGET_PACKAGES }} - key: ${{ steps.cachekey.outputs.cachekey }} + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }}-v1 restore-keys: | ${{ runner.os }}-nuget-v1 - name: Restore @@ -187,12 +175,12 @@ jobs: name: test-assets # For some reason .NET Core 2.1 (and only it!) needs a package restore; it looks there for the testhost for some reason - - name: Load cached NuGet packages - uses: actions/cache@v4 - if: ${{ matrix.tfm == 'netcoreapp2.1' }} - with: - path: ${{ env.NUGET_PACKAGES }} - key: ${{ needs.build.outputs.${{matrix.os}}-cachekey }} + #- name: Load cached NuGet packages + # uses: actions/cache@v4 + # if: ${{ matrix.tfm == 'netcoreapp2.1' }} + # with: + # path: ${{ env.NUGET_PACKAGES }} + # key: ${{ needs.build.outputs.${{matrix.os}}-cachekey }} - name: Install test target runtime SDK uses: actions/setup-dotnet@v4 From 8ab2480926fd512678952e9f59acf130ba643093 Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 02:24:14 -0500 Subject: [PATCH 025/133] Remove MacOS-latest entirely from test matrix --- .github/workflows/build.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fa0c5893..793ae5aa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -119,8 +119,6 @@ jobs: osname: Linux - os: windows-latest osname: Windows - - os: macos-latest - osname: MacOS (latest) - os: macos-13 osname: MacOS 13 From 617d6f47c7e2ff0704876030c6045456d6393a28 Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 02:37:11 -0500 Subject: [PATCH 026/133] Attempt 2 at fixing tests for .NET Core 2.1 --- .github/workflows/build.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 793ae5aa..c47ebd93 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -61,9 +61,8 @@ jobs: uses: actions/cache@v4 with: path: ${{ env.NUGET_PACKAGES }} - key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }}-v1 - restore-keys: | - ${{ runner.os }}-nuget-v1 + key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }} + restore-keys: ${{ runner.os }}-nuget-v1- - name: Restore run: dotnet restore -bl:restore.binlog -noAutoRsp @@ -173,12 +172,13 @@ jobs: name: test-assets # For some reason .NET Core 2.1 (and only it!) needs a package restore; it looks there for the testhost for some reason - #- name: Load cached NuGet packages - # uses: actions/cache@v4 - # if: ${{ matrix.tfm == 'netcoreapp2.1' }} - # with: - # path: ${{ env.NUGET_PACKAGES }} - # key: ${{ needs.build.outputs.${{matrix.os}}-cachekey }} + - name: Load cached NuGet packages + uses: actions/cache@v4 + if: ${{ matrix.tfm == 'netcoreapp2.1' }} + with: + path: ${{ env.NUGET_PACKAGES }} + restore-keys: ${{ runner.os }}-nuget-v1- + fail-on-cache-miss: true - name: Install test target runtime SDK uses: actions/setup-dotnet@v4 From 60fec4227ceddf84482e4180df88277eb4f2d468 Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 02:57:08 -0500 Subject: [PATCH 027/133] actions/cache requires with.key --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c47ebd93..8218f68e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -177,6 +177,7 @@ jobs: if: ${{ matrix.tfm == 'netcoreapp2.1' }} with: path: ${{ env.NUGET_PACKAGES }} + key: ${{ runner.os }}-nuget-v1- restore-keys: ${{ runner.os }}-nuget-v1- fail-on-cache-miss: true From 6d3b1ab13f7e3d0da4ac27765b54f8a72435ede2 Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 03:38:16 -0500 Subject: [PATCH 028/133] Remove .NET Core 2.1 cache load during test runs (I don't like this approach) --- .github/workflows/build.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8218f68e..e4dca061 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -172,14 +172,14 @@ jobs: name: test-assets # For some reason .NET Core 2.1 (and only it!) needs a package restore; it looks there for the testhost for some reason - - name: Load cached NuGet packages - uses: actions/cache@v4 - if: ${{ matrix.tfm == 'netcoreapp2.1' }} - with: - path: ${{ env.NUGET_PACKAGES }} - key: ${{ runner.os }}-nuget-v1- - restore-keys: ${{ runner.os }}-nuget-v1- - fail-on-cache-miss: true + #- name: Load cached NuGet packages + # uses: actions/cache@v4 + # if: ${{ matrix.tfm == 'netcoreapp2.1' }} + # with: + # path: ${{ env.NUGET_PACKAGES }} + # key: ${{ runner.os }}-nuget-v1- + # restore-keys: ${{ runner.os }}-nuget-v1- + # fail-on-cache-miss: true - name: Install test target runtime SDK uses: actions/setup-dotnet@v4 From 587b0299f23f68b00c001f0a34bd90c83ef6aa52 Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 04:56:07 -0500 Subject: [PATCH 029/133] Move to Powershell generation of test matrix --- .github/gen-test-matrix.ps1 | 159 ++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 .github/gen-test-matrix.ps1 diff --git a/.github/gen-test-matrix.ps1 b/.github/gen-test-matrix.ps1 new file mode 100644 index 00000000..66ad1100 --- /dev/null +++ b/.github/gen-test-matrix.ps1 @@ -0,0 +1,159 @@ +param ( + [string[]] $MatrixOutName, + [string] $GithubOutput +) + +$ErrorActionPreference = "Stop"; + +$operatingSystems = @( + [pscustomobject]@{ + name = "Windows"; + runner = "windows-latest"; + ridname = "win"; + arch = @("x86","x64"); # while .NET Framework supports Arm64, GitHub doesn't provide Arm windows runners + }, + [pscustomobject]@{ + name = "Linux"; + runner = "ubuntu-latest"; + ridname = "linux"; + arch = @("x64"); + }, + [pscustomobject]@{ + name = "MacOS 13"; + runner = "macos-13"; + ridname = "osx"; + arch = @("x64"); + }, + [pscustomobject]@{ + enable = $false; + name = "MacOS 14 (M1)"; + runner = "macos-14"; + ridname = "osx"; + arch = @("x64", "arm64"); # x64 comes from Rosetta + } +); + +$dotnetVersions = @( + [pscustomobject]@{ + name = ".NET Framework 4.x"; + id = 'fx'; + tfm = "net46"; + rids = @("win-x86","win-x64","win-arm64"); + }, + [pscustomobject]@{ + name = ".NET Core 2.1"; + sdk = "2.1.x"; + tfm = "netcoreapp2.1"; + rids = @("win-x86","win-x64","linux-x64","osx-x64"); + needsRestore = $true; + }, + [pscustomobject]@{ + name = ".NET Core 3.0"; + sdk = "3.0.x"; + tfm = "netcoreapp3.0"; + rids = @("win-x86","win-x64","linux-x64","osx-x64"); + }, + [pscustomobject]@{ + name = ".NET Core 3.1"; + sdk = "3.1.x"; + tfm = "netcoreapp3.1"; + rids = @("win-x86","win-x64","linux-x64","osx-x64"); + }, + [pscustomobject]@{ + name = ".NET 5.0"; + sdk = "5.0.x"; + tfm = "net5.0"; + rids = @("win-x86","win-x64","linux-x64","osx-x64"); + }, + [pscustomobject]@{ + name = ".NET 6.0"; + sdk = "6.0.x"; + tfm = "net6.0"; + rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64"); + pgo = $true; + }, + [pscustomobject]@{ + name = ".NET 7.0"; + sdk = "7.0.x"; + tfm = "net7.0"; + rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64"); + pgo = $true; + }, + [pscustomobject]@{ + name = ".NET 8.0"; + sdk = "8.0.x"; + tfm = "net8.0"; + rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64"); + pgo = $true; + } +); + +$jobs = @(); + +foreach ($os in $operatingSystems) +{ + if ($os.enable -eq $false) { continue; } + $outos = $os | Select-Object -ExcludeProperty arch | Select-Object -ExcludeProperty ridname + + foreach ($arch in $os.arch) + { + $rid = $os.ridname + "-" + $arch; + + foreach ($dotnet in $dotnetVersions) + { + if ($dotnet.enable -eq $false) { continue; } + + if (-not $dotnet.rids -contains $rid) + { + # the current OS/arch/runtime triple is not supported by .NET, skip + continue; + } + + $outdotnet = $dotnet | Select-Object -ExcludeProperty rids + + $title = "$($dotnet.name) $arch on $($os.name)" + if ($dotnet.pgo) + { + # this runtime supports pgo; generate 2 jobs; one with it enabled, one without + $jobs += @( + [pscustomobject]@{ + title = $title + " (PGO Off)"; + os = $outos; + dotnet = $outdotnet; + arch = $arch; + usePgo = $false; + }, + [pscustomobject]@{ + title = $title + " (PGO On)"; + os = $outos; + dotnet = $outdotnet; + arch = $arch; + usePgo = $true; + } + ); + } + else + { + # this is a normal job; only add one + $jobs += @( + [pscustomobject]@{ + title = $title; + os = $outos; + dotnet = $outdotnet; + arch = $arch; + } + ); + } + } + } +} + +# TODO: support multiple batches +if ($jobs.Length -gt 256) +{ + Write-Error "Generated more than 256 jobs; actions will fail!"; +} + +$matrixObj = [pscustomobject]@{include = $jobs;}; +$matrixStr = ConvertTo-Json -Compress -Depth 5 $matrixObj; +echo "$($MatrixOutName[0])=$matrixStr" >> $GithubOutput; \ No newline at end of file From 424d4f11e42c50b5819f2cffc2cd4a7b686ddb32 Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 04:57:22 -0500 Subject: [PATCH 030/133] Try 2 (actually update the workflow) --- .github/workflows/build.yml | 106 +++++++++++++----------------------- 1 file changed, 38 insertions(+), 68 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e4dca061..2bf10610 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -96,71 +96,29 @@ jobs: path: | artifacts/bin/MonoMod.UnitTest/*/**/* + compute-test-matrix: + name: Compute Test Matrix + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.compute-matrix.outputs.matrix }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + lfs: false + submodules: false + - name: Compute test matrix + id: compute-matrix + run: ./.github/gen-test-matrix.ps1 -MatrixOutName matrix -GithubOutput $env:GITHUB_OUTPUT + test: - needs: build + needs: [compute-test-matrix, build] strategy: fail-fast: false - matrix: - os: [ubuntu-latest, windows-latest, macos-13] - dotnet-version: - - fx - - mono - - 2.1.x - - 3.0.x - - 3.1.x - - 5.0.x - - 6.0.x - - 7.0.x - - 8.0.x - include: - # some extra OS configuration info - - os: ubuntu-latest - osname: Linux - - os: windows-latest - osname: Windows - - os: macos-13 - osname: MacOS 13 - - # .NET version -> TFM mapping - - dotnet-version: fx - tfm: net46 - fxname: .NET Framework 4.x - - dotnet-version: mono - tfm: net46 - fxname: Mono - - dotnet-version: 2.1.x - tfm: netcoreapp2.1 - fxname: .NET Core 2.1 - - dotnet-version: 3.0.x - tfm: netcoreapp3.0 - fxname: .NET Core 3.0 - - dotnet-version: 3.1.x - tfm: netcoreapp3.1 - fxname: .NET Core 3.1 - - dotnet-version: 5.0.x - tfm: net5.0 - fxname: .NET 5.0 - - dotnet-version: 6.0.x - tfm: net6.0 - fxname: .NET 6.0 - - dotnet-version: 7.0.x - tfm: net7.0 - fxname: .NET 7.0 - - dotnet-version: 8.0.x - tfm: net8.0 - fxname: .NET 8.0 - - exclude: - # remove Framework and Mono where appropriate - - os: ubuntu-latest - dotnet-version: fx - - os: macos-13 - dotnet-version: fx - - os: windows-latest - dotnet-version: mono - - name: Test ${{ matrix.fxname }} on ${{ matrix.osname }} - runs-on: ${{ matrix.os }} + matrix: ${{ fromJSON(needs.compute-test-matrix.outputs.matrix) }} + + name: Test ${{ matrix.title }} + runs-on: ${{ matrix.os.runner }} steps: - name: Install global SDK uses: actions/setup-dotnet@v4 @@ -183,12 +141,24 @@ jobs: - name: Install test target runtime SDK uses: actions/setup-dotnet@v4 - if: ${{ matrix.dotnet-version != 'mono' && matrix.dotnet-version != 'fx' }} + if: ${{ matrix.dotnet.sdk != '' }} with: - dotnet-version: ${{ matrix.dotnet-version }} + dotnet-version: ${{ matrix.dotnet.sdk }} + - name: Run tests - if: ${{ matrix.dotnet-version != 'mono' }} - run: dotnet test -f ${{ matrix.tfm }} -l:"trx;LogFileName=testresults.${{matrix.os}}.${{matrix.dotnet-version}}.trx" release_${{ matrix.tfm }}/MonoMod.UnitTest.dll + if: ${{ ! matrix.dotnet.isMono && ! matrix.dotnet.pgo }} + env: + LOG_FILE_NAME: testresults.${{ matrix.os.runner }}.${{ matrix.dotnet.id != '' && matrix.dotnet.id || matrix.dotnet.sdk }}.trx + run: dotnet test -f ${{ matrix.dotnet.tfm }} -a ${{ matrix.arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ matrix.dotnet.tfm }}/MonoMod.UnitTest.dll + + - name: Run tests (PGO) + if: ${{ matrix.dotnet.pgo }} + env: + LOG_FILE_NAME: testresults.${{ matrix.os.runner }}.${{ matrix.dotnet.id != '' && matrix.dotnet.id || matrix.dotnet.sdk }}.trx + DOTNET_ReadyToRun: ${{ matrix.usePgo && 0 || 1 }} + DOTNET_TC_QuicJitForLoops: ${{ matrix.usePgo && 1 || 0 }} + DOTNET_TieredPGO: ${{ matrix.usePgo && 1 || 0 }} + run: dotnet test -f ${{ matrix.dotnet.tfm }} -a ${{ matrix.arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ matrix.dotnet.tfm }}/MonoMod.UnitTest.dll # TODO: Mono? @@ -196,7 +166,7 @@ jobs: uses: actions/upload-artifact@v4 if: ${{ always() }} with: - name: test-results-${{ matrix.osname }}-${{ matrix.dotnet-version }} + name: test-results ${{ matrix.title }} retention-days: 1 path: 'TestResults/*.trx' @@ -212,7 +182,7 @@ jobs: - name: Download test results uses: actions/download-artifact@v4 with: - pattern: test-results-* + pattern: test-results * merge-multiple: true - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 From eaf41a8de326640b0b5028592e057a9eaf6fe43d Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 05:34:24 -0500 Subject: [PATCH 031/133] Tweak test matrix generator to prevent .NET Framework runs on non-Windows --- .github/gen-test-matrix.ps1 | 23 +++++++++++++++++------ .github/workflows/build.yml | 31 ++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/.github/gen-test-matrix.ps1 b/.github/gen-test-matrix.ps1 index 66ad1100..c22fd9fa 100644 --- a/.github/gen-test-matrix.ps1 +++ b/.github/gen-test-matrix.ps1 @@ -10,26 +10,30 @@ $operatingSystems = @( name = "Windows"; runner = "windows-latest"; ridname = "win"; - arch = @("x86","x64"); # while .NET Framework supports Arm64, GitHub doesn't provide Arm windows runners + arch = @("x86","x64"); # while .NET Framework supports Arm64, GitHub doesn't provide Arm windows runners + hasFramework = $true; }, [pscustomobject]@{ name = "Linux"; runner = "ubuntu-latest"; ridname = "linux"; - arch = @("x64"); + arch = @("x64"); + hasMono = $true; }, [pscustomobject]@{ name = "MacOS 13"; runner = "macos-13"; ridname = "osx"; - arch = @("x64"); + arch = @("x64"); + hasMono = $true; }, [pscustomobject]@{ enable = $false; name = "MacOS 14 (M1)"; runner = "macos-14"; ridname = "osx"; - arch = @("x64", "arm64"); # x64 comes from Rosetta + arch = @("x64", "arm64"); # x64 comes from Rosetta + hasMono = $true; } ); @@ -38,7 +42,8 @@ $dotnetVersions = @( name = ".NET Framework 4.x"; id = 'fx'; tfm = "net46"; - rids = @("win-x86","win-x64","win-arm64"); + rids = @("win-x86","win-x64","win-arm64"); + isFramework = $true; }, [pscustomobject]@{ name = ".NET Core 2.1"; @@ -93,7 +98,7 @@ $jobs = @(); foreach ($os in $operatingSystems) { if ($os.enable -eq $false) { continue; } - $outos = $os | Select-Object -ExcludeProperty arch | Select-Object -ExcludeProperty ridname + $outos = $os | Select-Object -ExcludeProperty arch,ridname,hasFramework,hasMono foreach ($arch in $os.arch) { @@ -103,6 +108,12 @@ foreach ($os in $operatingSystems) { if ($dotnet.enable -eq $false) { continue; } + if ($dotnet.isFramework -and -not $os.hasFramework) + { + # we're looking at .NET Framework, but this OS doesn't support it + continue; + } + if (-not $dotnet.rids -contains $rid) { # the current OS/arch/runtime triple is not supported by .NET, skip diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2bf10610..e187c479 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -119,26 +119,35 @@ jobs: name: Test ${{ matrix.title }} runs-on: ${{ matrix.os.runner }} - steps: + steps: + - name: Checkout + uses: actions/checkout@v4 + if: ${{ matrix.dotnet.needsRestore }} + with: + lfs: true + submodules: recursive + - name: Install global SDK + if: ${{ ! matrix.dotnet.needsRestore }} uses: actions/setup-dotnet@v4 with: dotnet-version: 8.0.x + + - name: Install restore SDK + if: ${{ matrix.dotnet.needsRestore }} + uses: actions/setup-dotnet@v4 + with: + global-json-file: global.json + + - name: Restore packages + if: ${{ matrix.dotnet.needsRestore }} + run: dotnet restore -noAutoRsp + - name: Download test assets uses: actions/download-artifact@v4 with: name: test-assets - # For some reason .NET Core 2.1 (and only it!) needs a package restore; it looks there for the testhost for some reason - #- name: Load cached NuGet packages - # uses: actions/cache@v4 - # if: ${{ matrix.tfm == 'netcoreapp2.1' }} - # with: - # path: ${{ env.NUGET_PACKAGES }} - # key: ${{ runner.os }}-nuget-v1- - # restore-keys: ${{ runner.os }}-nuget-v1- - # fail-on-cache-miss: true - - name: Install test target runtime SDK uses: actions/setup-dotnet@v4 if: ${{ matrix.dotnet.sdk != '' }} From 40430ab586cacab16f71d0a43f26464f5fcc62fb Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 05:40:56 -0500 Subject: [PATCH 032/133] Add cache step before restore in test runners, when appropriate --- .github/workflows/build.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e187c479..c41082ac 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -132,7 +132,14 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: 8.0.x - + + - name: Cache restored NuGet packages + uses: actions/cache@v4 + if: ${{ matrix.dotnet.needsRestore }} + with: + path: ${{ env.NUGET_PACKAGES }} + key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }} + restore-keys: ${{ runner.os }}-nuget-v1- - name: Install restore SDK if: ${{ matrix.dotnet.needsRestore }} uses: actions/setup-dotnet@v4 From 578abfd57ef6113c46584036239705efce8c8380 Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 15 May 2024 05:59:57 -0500 Subject: [PATCH 033/133] Tweaks to make sure tests don't corrupt each other --- .github/workflows/build.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c41082ac..a7bcb36b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -119,6 +119,8 @@ jobs: name: Test ${{ matrix.title }} runs-on: ${{ matrix.os.runner }} + env: + LOG_FILE_NAME: testresults.${{ matrix.os.runner }}.${{ matrix.dotnet.id != '' && matrix.dotnet.id || matrix.dotnet.sdk }}.${{ matrix.arch }}.trx steps: - name: Checkout uses: actions/checkout@v4 @@ -163,14 +165,11 @@ jobs: - name: Run tests if: ${{ ! matrix.dotnet.isMono && ! matrix.dotnet.pgo }} - env: - LOG_FILE_NAME: testresults.${{ matrix.os.runner }}.${{ matrix.dotnet.id != '' && matrix.dotnet.id || matrix.dotnet.sdk }}.trx run: dotnet test -f ${{ matrix.dotnet.tfm }} -a ${{ matrix.arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ matrix.dotnet.tfm }}/MonoMod.UnitTest.dll - name: Run tests (PGO) if: ${{ matrix.dotnet.pgo }} env: - LOG_FILE_NAME: testresults.${{ matrix.os.runner }}.${{ matrix.dotnet.id != '' && matrix.dotnet.id || matrix.dotnet.sdk }}.trx DOTNET_ReadyToRun: ${{ matrix.usePgo && 0 || 1 }} DOTNET_TC_QuicJitForLoops: ${{ matrix.usePgo && 1 || 0 }} DOTNET_TieredPGO: ${{ matrix.usePgo && 1 || 0 }} @@ -199,11 +198,11 @@ jobs: uses: actions/download-artifact@v4 with: pattern: test-results * - merge-multiple: true + merge-multiple: false - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: - files: '*.trx' + files: '**/*.trx' comment_mode: ${{ (github.event.workflow_run.event == 'pull_request' || github.event_name == 'pull_request') && 'failures' || 'off' }} report_individual_runs: true From e7530ce0400e66a0d8eb52aa2760bac7cee65586 Mon Sep 17 00:00:00 2001 From: DaNike Date: Thu, 16 May 2024 01:32:18 -0500 Subject: [PATCH 034/133] Switch to custom install script wrapper --- .github/gen-test-matrix.ps1 | 54 ++++++++++++++++++------------------- .github/workflows/build.yml | 28 ++++++++++++------- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/.github/gen-test-matrix.ps1 b/.github/gen-test-matrix.ps1 index c22fd9fa..006b3dc6 100644 --- a/.github/gen-test-matrix.ps1 +++ b/.github/gen-test-matrix.ps1 @@ -1,6 +1,6 @@ param ( [string[]] $MatrixOutName, - [string] $GithubOutput + [string] $GithubOutput ) $ErrorActionPreference = "Stop"; @@ -11,21 +11,21 @@ $operatingSystems = @( runner = "windows-latest"; ridname = "win"; arch = @("x86","x64"); # while .NET Framework supports Arm64, GitHub doesn't provide Arm windows runners - hasFramework = $true; + hasFramework = $true; }, [pscustomobject]@{ name = "Linux"; runner = "ubuntu-latest"; ridname = "linux"; arch = @("x64"); - hasMono = $true; + hasMono = $true; }, [pscustomobject]@{ name = "MacOS 13"; runner = "macos-13"; ridname = "osx"; arch = @("x64"); - hasMono = $true; + hasMono = $true; }, [pscustomobject]@{ enable = $false; @@ -33,8 +33,8 @@ $operatingSystems = @( runner = "macos-14"; ridname = "osx"; arch = @("x64", "arm64"); # x64 comes from Rosetta - hasMono = $true; - } + hasMono = $true; + } ); $dotnetVersions = @( @@ -43,54 +43,54 @@ $dotnetVersions = @( id = 'fx'; tfm = "net46"; rids = @("win-x86","win-x64","win-arm64"); - isFramework = $true; + isFramework = $true; }, [pscustomobject]@{ name = ".NET Core 2.1"; sdk = "2.1.x"; tfm = "netcoreapp2.1"; rids = @("win-x86","win-x64","linux-x64","osx-x64"); - needsRestore = $true; + needsRestore = $true; }, [pscustomobject]@{ name = ".NET Core 3.0"; sdk = "3.0.x"; tfm = "netcoreapp3.0"; - rids = @("win-x86","win-x64","linux-x64","osx-x64"); + rids = @("win-x86","win-x64","linux-x64","osx-x64"); }, [pscustomobject]@{ name = ".NET Core 3.1"; sdk = "3.1.x"; tfm = "netcoreapp3.1"; - rids = @("win-x86","win-x64","linux-x64","osx-x64"); + rids = @("win-x86","win-x64","linux-x64","osx-x64"); }, [pscustomobject]@{ name = ".NET 5.0"; sdk = "5.0.x"; tfm = "net5.0"; - rids = @("win-x86","win-x64","linux-x64","osx-x64"); + rids = @("win-x86","win-x64","linux-x64","osx-x64"); }, [pscustomobject]@{ name = ".NET 6.0"; sdk = "6.0.x"; tfm = "net6.0"; rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64"); - pgo = $true; + pgo = $true; }, [pscustomobject]@{ name = ".NET 7.0"; sdk = "7.0.x"; tfm = "net7.0"; rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64"); - pgo = $true; + pgo = $true; }, [pscustomobject]@{ name = ".NET 8.0"; sdk = "8.0.x"; tfm = "net8.0"; rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64"); - pgo = $true; - } + pgo = $true; + } ); $jobs = @(); @@ -111,13 +111,13 @@ foreach ($os in $operatingSystems) if ($dotnet.isFramework -and -not $os.hasFramework) { # we're looking at .NET Framework, but this OS doesn't support it - continue; + continue; } if (-not $dotnet.rids -contains $rid) { # the current OS/arch/runtime triple is not supported by .NET, skip - continue; + continue; } $outdotnet = $dotnet | Select-Object -ExcludeProperty rids @@ -132,16 +132,16 @@ foreach ($os in $operatingSystems) os = $outos; dotnet = $outdotnet; arch = $arch; - usePgo = $false; + usePgo = $false; }, [pscustomobject]@{ title = $title + " (PGO On)"; os = $outos; dotnet = $outdotnet; arch = $arch; - usePgo = $true; - } - ); + usePgo = $true; + } + ); } else { @@ -151,18 +151,18 @@ foreach ($os in $operatingSystems) title = $title; os = $outos; dotnet = $outdotnet; - arch = $arch; - } - ); - } + arch = $arch; + } + ); + } } - } + } } # TODO: support multiple batches if ($jobs.Length -gt 256) { - Write-Error "Generated more than 256 jobs; actions will fail!"; + Write-Error "Generated more than 256 jobs; actions will fail!"; } $matrixObj = [pscustomobject]@{include = $jobs;}; diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a7bcb36b..85c6c5aa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,10 +51,15 @@ jobs: submodules: recursive # TODO: maybe we can eventually use package locks for package caching? + #- name: Install .NET SDK + # uses: actions/setup-dotnet@v4 + # with: + # global-json-file: global.json + - name: Install .NET SDK - uses: actions/setup-dotnet@v4 + uses: nike4613/install-dotnet@aaa4ec805e8ea5ef03f98e3d398613ebcb2dd7c2 with: - global-json-file: global.json + global-json: global.json # NOTE: manual package caching - name: Cache restored NuGet packages @@ -72,6 +77,9 @@ jobs: - name: Pack run: dotnet pack --no-restore -c Release -p:ContinuousIntegrationBuild=true -bl:pack.binlog -clp:NoSummary -noAutoRsp + # TODO: it might be worth trying to do a "smoketest" test run with the installed tfm to broadly make sure that all builds + # work. We may also want to do a hash check on the packages, though I have no idea how reliable that would be. + - name: Upload binlogs uses: actions/upload-artifact@v4 if: ${{ always() }} @@ -131,9 +139,9 @@ jobs: - name: Install global SDK if: ${{ ! matrix.dotnet.needsRestore }} - uses: actions/setup-dotnet@v4 + uses: nike4613/install-dotnet@aaa4ec805e8ea5ef03f98e3d398613ebcb2dd7c2 with: - dotnet-version: 8.0.x + version: 8.0.x - name: Cache restored NuGet packages uses: actions/cache@v4 @@ -144,9 +152,9 @@ jobs: restore-keys: ${{ runner.os }}-nuget-v1- - name: Install restore SDK if: ${{ matrix.dotnet.needsRestore }} - uses: actions/setup-dotnet@v4 + uses: nike4613/install-dotnet@aaa4ec805e8ea5ef03f98e3d398613ebcb2dd7c2 with: - global-json-file: global.json + global-json: global.json - name: Restore packages if: ${{ matrix.dotnet.needsRestore }} @@ -157,11 +165,13 @@ jobs: with: name: test-assets - - name: Install test target runtime SDK - uses: actions/setup-dotnet@v4 + - name: Install test target runtime if: ${{ matrix.dotnet.sdk != '' }} + uses: nike4613/install-dotnet@aaa4ec805e8ea5ef03f98e3d398613ebcb2dd7c2 with: - dotnet-version: ${{ matrix.dotnet.sdk }} + version: ${{ matrix.dotnet.sdk }} + architecture: ${{ matrix.arch }} + runtime: dotnet - name: Run tests if: ${{ ! matrix.dotnet.isMono && ! matrix.dotnet.pgo }} From 329f86b0783d7872dc67540e3940d866fe95dbd0 Mon Sep 17 00:00:00 2001 From: DaNike Date: Thu, 16 May 2024 01:37:36 -0500 Subject: [PATCH 035/133] Update custom action --- .github/workflows/build.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 85c6c5aa..58fc6de9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,6 +17,7 @@ env: # a load of tests. This will (eventually) include ARM runners, where possible. jobs: compute-version: + name: Compute Version runs-on: ubuntu-latest outputs: ver: ${{ steps.computever.outputs.ver }} @@ -57,7 +58,7 @@ jobs: # global-json-file: global.json - name: Install .NET SDK - uses: nike4613/install-dotnet@aaa4ec805e8ea5ef03f98e3d398613ebcb2dd7c2 + uses: nike4613/install-dotnet@91131ae8e3b9af0d40074a7c4543832f0b64ae0c with: global-json: global.json @@ -152,7 +153,7 @@ jobs: restore-keys: ${{ runner.os }}-nuget-v1- - name: Install restore SDK if: ${{ matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@aaa4ec805e8ea5ef03f98e3d398613ebcb2dd7c2 + uses: nike4613/install-dotnet@91131ae8e3b9af0d40074a7c4543832f0b64ae0c with: global-json: global.json @@ -167,7 +168,7 @@ jobs: - name: Install test target runtime if: ${{ matrix.dotnet.sdk != '' }} - uses: nike4613/install-dotnet@aaa4ec805e8ea5ef03f98e3d398613ebcb2dd7c2 + uses: nike4613/install-dotnet@91131ae8e3b9af0d40074a7c4543832f0b64ae0c with: version: ${{ matrix.dotnet.sdk }} architecture: ${{ matrix.arch }} From d484bf0cf38f34acfed89f2ad44759289a4da97d Mon Sep 17 00:00:00 2001 From: DaNike Date: Thu, 16 May 2024 01:44:28 -0500 Subject: [PATCH 036/133] Above again --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 58fc6de9..92233386 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,7 +58,7 @@ jobs: # global-json-file: global.json - name: Install .NET SDK - uses: nike4613/install-dotnet@91131ae8e3b9af0d40074a7c4543832f0b64ae0c + uses: nike4613/install-dotnet@c7ceea0fdf7fe99613198bf03903beb113cd015a with: global-json: global.json @@ -140,7 +140,7 @@ jobs: - name: Install global SDK if: ${{ ! matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@aaa4ec805e8ea5ef03f98e3d398613ebcb2dd7c2 + uses: nike4613/install-dotnet@c7ceea0fdf7fe99613198bf03903beb113cd015a with: version: 8.0.x @@ -153,7 +153,7 @@ jobs: restore-keys: ${{ runner.os }}-nuget-v1- - name: Install restore SDK if: ${{ matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@91131ae8e3b9af0d40074a7c4543832f0b64ae0c + uses: nike4613/install-dotnet@c7ceea0fdf7fe99613198bf03903beb113cd015a with: global-json: global.json @@ -168,7 +168,7 @@ jobs: - name: Install test target runtime if: ${{ matrix.dotnet.sdk != '' }} - uses: nike4613/install-dotnet@91131ae8e3b9af0d40074a7c4543832f0b64ae0c + uses: nike4613/install-dotnet@c7ceea0fdf7fe99613198bf03903beb113cd015a with: version: ${{ matrix.dotnet.sdk }} architecture: ${{ matrix.arch }} From 6217702eb3206ede47bee56dfb1accf0123e48fa Mon Sep 17 00:00:00 2001 From: DaNike Date: Thu, 16 May 2024 01:52:33 -0500 Subject: [PATCH 037/133] Update again --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 92233386..1329e4ac 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,7 +58,7 @@ jobs: # global-json-file: global.json - name: Install .NET SDK - uses: nike4613/install-dotnet@c7ceea0fdf7fe99613198bf03903beb113cd015a + uses: nike4613/install-dotnet@9179880a320456c7fefcc297d4974e67ba7e36de with: global-json: global.json @@ -140,7 +140,7 @@ jobs: - name: Install global SDK if: ${{ ! matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@c7ceea0fdf7fe99613198bf03903beb113cd015a + uses: nike4613/install-dotnet@9179880a320456c7fefcc297d4974e67ba7e36de with: version: 8.0.x @@ -153,7 +153,7 @@ jobs: restore-keys: ${{ runner.os }}-nuget-v1- - name: Install restore SDK if: ${{ matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@c7ceea0fdf7fe99613198bf03903beb113cd015a + uses: nike4613/install-dotnet@9179880a320456c7fefcc297d4974e67ba7e36de with: global-json: global.json @@ -168,7 +168,7 @@ jobs: - name: Install test target runtime if: ${{ matrix.dotnet.sdk != '' }} - uses: nike4613/install-dotnet@c7ceea0fdf7fe99613198bf03903beb113cd015a + uses: nike4613/install-dotnet@9179880a320456c7fefcc297d4974e67ba7e36de with: version: ${{ matrix.dotnet.sdk }} architecture: ${{ matrix.arch }} From d81a32cd5b768b2e6409f0f4bc1e7bcdda20666b Mon Sep 17 00:00:00 2001 From: DaNike Date: Thu, 16 May 2024 01:59:03 -0500 Subject: [PATCH 038/133] Update again --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1329e4ac..53893bcf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,7 +58,7 @@ jobs: # global-json-file: global.json - name: Install .NET SDK - uses: nike4613/install-dotnet@9179880a320456c7fefcc297d4974e67ba7e36de + uses: nike4613/install-dotnet@1a5b0dd6e238065054df10fe318b4f926a9fb47d with: global-json: global.json @@ -140,7 +140,7 @@ jobs: - name: Install global SDK if: ${{ ! matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@9179880a320456c7fefcc297d4974e67ba7e36de + uses: nike4613/install-dotnet@1a5b0dd6e238065054df10fe318b4f926a9fb47d with: version: 8.0.x @@ -153,7 +153,7 @@ jobs: restore-keys: ${{ runner.os }}-nuget-v1- - name: Install restore SDK if: ${{ matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@9179880a320456c7fefcc297d4974e67ba7e36de + uses: nike4613/install-dotnet@1a5b0dd6e238065054df10fe318b4f926a9fb47d with: global-json: global.json @@ -168,7 +168,7 @@ jobs: - name: Install test target runtime if: ${{ matrix.dotnet.sdk != '' }} - uses: nike4613/install-dotnet@9179880a320456c7fefcc297d4974e67ba7e36de + uses: nike4613/install-dotnet@1a5b0dd6e238065054df10fe318b4f926a9fb47d with: version: ${{ matrix.dotnet.sdk }} architecture: ${{ matrix.arch }} From cb943b4c155479413d5c731a4e88e2df484fe084 Mon Sep 17 00:00:00 2001 From: DaNike Date: Thu, 16 May 2024 02:05:50 -0500 Subject: [PATCH 039/133] Update again --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 53893bcf..e2e88d4e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,7 +58,7 @@ jobs: # global-json-file: global.json - name: Install .NET SDK - uses: nike4613/install-dotnet@1a5b0dd6e238065054df10fe318b4f926a9fb47d + uses: nike4613/install-dotnet@a1defbb6c29fbd3834974eb8b94ebe85747fb087 with: global-json: global.json @@ -140,7 +140,7 @@ jobs: - name: Install global SDK if: ${{ ! matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@1a5b0dd6e238065054df10fe318b4f926a9fb47d + uses: nike4613/install-dotnet@a1defbb6c29fbd3834974eb8b94ebe85747fb087 with: version: 8.0.x @@ -153,7 +153,7 @@ jobs: restore-keys: ${{ runner.os }}-nuget-v1- - name: Install restore SDK if: ${{ matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@1a5b0dd6e238065054df10fe318b4f926a9fb47d + uses: nike4613/install-dotnet@a1defbb6c29fbd3834974eb8b94ebe85747fb087 with: global-json: global.json @@ -168,7 +168,7 @@ jobs: - name: Install test target runtime if: ${{ matrix.dotnet.sdk != '' }} - uses: nike4613/install-dotnet@1a5b0dd6e238065054df10fe318b4f926a9fb47d + uses: nike4613/install-dotnet@a1defbb6c29fbd3834974eb8b94ebe85747fb087 with: version: ${{ matrix.dotnet.sdk }} architecture: ${{ matrix.arch }} From e905fc4cb06dfea650ed594fbe144a380b23b542 Mon Sep 17 00:00:00 2001 From: DaNike Date: Thu, 16 May 2024 02:07:50 -0500 Subject: [PATCH 040/133] Update again --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e2e88d4e..6a92c05c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,7 +58,7 @@ jobs: # global-json-file: global.json - name: Install .NET SDK - uses: nike4613/install-dotnet@a1defbb6c29fbd3834974eb8b94ebe85747fb087 + uses: nike4613/install-dotnet@8150e2b07439ad7a9015ac97bf9af762045c2348 with: global-json: global.json @@ -140,7 +140,7 @@ jobs: - name: Install global SDK if: ${{ ! matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@a1defbb6c29fbd3834974eb8b94ebe85747fb087 + uses: nike4613/install-dotnet@8150e2b07439ad7a9015ac97bf9af762045c2348 with: version: 8.0.x @@ -153,7 +153,7 @@ jobs: restore-keys: ${{ runner.os }}-nuget-v1- - name: Install restore SDK if: ${{ matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@a1defbb6c29fbd3834974eb8b94ebe85747fb087 + uses: nike4613/install-dotnet@8150e2b07439ad7a9015ac97bf9af762045c2348 with: global-json: global.json @@ -168,7 +168,7 @@ jobs: - name: Install test target runtime if: ${{ matrix.dotnet.sdk != '' }} - uses: nike4613/install-dotnet@a1defbb6c29fbd3834974eb8b94ebe85747fb087 + uses: nike4613/install-dotnet@8150e2b07439ad7a9015ac97bf9af762045c2348 with: version: ${{ matrix.dotnet.sdk }} architecture: ${{ matrix.arch }} From 0ce90734b90ea7715280a53a9d8c85b3e6bb6595 Mon Sep 17 00:00:00 2001 From: DaNike Date: Thu, 16 May 2024 02:19:51 -0500 Subject: [PATCH 041/133] Fix versions to be dotnet-install compatible versions --- .github/gen-test-matrix.ps1 | 14 +++++++------- .github/workflows/build.yml | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/gen-test-matrix.ps1 b/.github/gen-test-matrix.ps1 index 006b3dc6..e0fb604c 100644 --- a/.github/gen-test-matrix.ps1 +++ b/.github/gen-test-matrix.ps1 @@ -47,46 +47,46 @@ $dotnetVersions = @( }, [pscustomobject]@{ name = ".NET Core 2.1"; - sdk = "2.1.x"; + sdk = "2.1"; tfm = "netcoreapp2.1"; rids = @("win-x86","win-x64","linux-x64","osx-x64"); needsRestore = $true; }, [pscustomobject]@{ name = ".NET Core 3.0"; - sdk = "3.0.x"; + sdk = "3.0"; tfm = "netcoreapp3.0"; rids = @("win-x86","win-x64","linux-x64","osx-x64"); }, [pscustomobject]@{ name = ".NET Core 3.1"; - sdk = "3.1.x"; + sdk = "3.1"; tfm = "netcoreapp3.1"; rids = @("win-x86","win-x64","linux-x64","osx-x64"); }, [pscustomobject]@{ name = ".NET 5.0"; - sdk = "5.0.x"; + sdk = "5.0"; tfm = "net5.0"; rids = @("win-x86","win-x64","linux-x64","osx-x64"); }, [pscustomobject]@{ name = ".NET 6.0"; - sdk = "6.0.x"; + sdk = "6.0"; tfm = "net6.0"; rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64"); pgo = $true; }, [pscustomobject]@{ name = ".NET 7.0"; - sdk = "7.0.x"; + sdk = "7.0"; tfm = "net7.0"; rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64"); pgo = $true; }, [pscustomobject]@{ name = ".NET 8.0"; - sdk = "8.0.x"; + sdk = "8.0"; tfm = "net8.0"; rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64"); pgo = $true; diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6a92c05c..bf73fa62 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,7 +58,7 @@ jobs: # global-json-file: global.json - name: Install .NET SDK - uses: nike4613/install-dotnet@8150e2b07439ad7a9015ac97bf9af762045c2348 + uses: nike4613/install-dotnet@42b1c002edcda8a5b5bfeeb1e0b7384fe5efaf16 with: global-json: global.json @@ -140,9 +140,9 @@ jobs: - name: Install global SDK if: ${{ ! matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@8150e2b07439ad7a9015ac97bf9af762045c2348 + uses: nike4613/install-dotnet@42b1c002edcda8a5b5bfeeb1e0b7384fe5efaf16 with: - version: 8.0.x + version: "8.0" - name: Cache restored NuGet packages uses: actions/cache@v4 @@ -153,7 +153,7 @@ jobs: restore-keys: ${{ runner.os }}-nuget-v1- - name: Install restore SDK if: ${{ matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@8150e2b07439ad7a9015ac97bf9af762045c2348 + uses: nike4613/install-dotnet@42b1c002edcda8a5b5bfeeb1e0b7384fe5efaf16 with: global-json: global.json @@ -168,7 +168,7 @@ jobs: - name: Install test target runtime if: ${{ matrix.dotnet.sdk != '' }} - uses: nike4613/install-dotnet@8150e2b07439ad7a9015ac97bf9af762045c2348 + uses: nike4613/install-dotnet@42b1c002edcda8a5b5bfeeb1e0b7384fe5efaf16 with: version: ${{ matrix.dotnet.sdk }} architecture: ${{ matrix.arch }} From 75104c685cbb9e586bd392e11aac56747a9b415d Mon Sep 17 00:00:00 2001 From: DaNike Date: Thu, 16 May 2024 22:10:42 -0500 Subject: [PATCH 042/133] Update install-dotnet --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bf73fa62..70bdcfe2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,7 +58,7 @@ jobs: # global-json-file: global.json - name: Install .NET SDK - uses: nike4613/install-dotnet@42b1c002edcda8a5b5bfeeb1e0b7384fe5efaf16 + uses: nike4613/install-dotnet@58bbd1de08d12205ec06c4ab990c6e495c73d65e with: global-json: global.json @@ -140,7 +140,7 @@ jobs: - name: Install global SDK if: ${{ ! matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@42b1c002edcda8a5b5bfeeb1e0b7384fe5efaf16 + uses: nike4613/install-dotnet@58bbd1de08d12205ec06c4ab990c6e495c73d65e with: version: "8.0" @@ -153,7 +153,7 @@ jobs: restore-keys: ${{ runner.os }}-nuget-v1- - name: Install restore SDK if: ${{ matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@42b1c002edcda8a5b5bfeeb1e0b7384fe5efaf16 + uses: nike4613/install-dotnet@58bbd1de08d12205ec06c4ab990c6e495c73d65e with: global-json: global.json @@ -168,7 +168,7 @@ jobs: - name: Install test target runtime if: ${{ matrix.dotnet.sdk != '' }} - uses: nike4613/install-dotnet@42b1c002edcda8a5b5bfeeb1e0b7384fe5efaf16 + uses: nike4613/install-dotnet@58bbd1de08d12205ec06c4ab990c6e495c73d65e with: version: ${{ matrix.dotnet.sdk }} architecture: ${{ matrix.arch }} From 2a5cb72e0742c50d8f434eac153f39c2d288ed44 Mon Sep 17 00:00:00 2001 From: DaNike Date: Thu, 16 May 2024 23:16:15 -0500 Subject: [PATCH 043/133] Upgrade ILHelpers.Patcher to .NET 8 --- src/MonoMod.ILHelpers.Patcher/MonoMod.ILHelpers.Patcher.csproj | 2 +- tools/Common.IL.targets | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MonoMod.ILHelpers.Patcher/MonoMod.ILHelpers.Patcher.csproj b/src/MonoMod.ILHelpers.Patcher/MonoMod.ILHelpers.Patcher.csproj index e40b480e..dc327f6e 100644 --- a/src/MonoMod.ILHelpers.Patcher/MonoMod.ILHelpers.Patcher.csproj +++ b/src/MonoMod.ILHelpers.Patcher/MonoMod.ILHelpers.Patcher.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 LatestMajor false diff --git a/tools/Common.IL.targets b/tools/Common.IL.targets index 31e8eba7..60debbaf 100644 --- a/tools/Common.IL.targets +++ b/tools/Common.IL.targets @@ -16,7 +16,7 @@ Reference="false" ReferenceOutputAssembly="false" OutputItemType="ILVersionPatcher" Private="false" Pack="false" - SetTargetFramework="TargetFramework=net6.0" + SetTargetFramework="TargetFramework=net8.0" SkipGetTargetFrameworkProperties="true" /> From f6a1d7a37c184d01eeacdecba5b0c76f8d028521 Mon Sep 17 00:00:00 2001 From: DaNike Date: Thu, 16 May 2024 23:28:36 -0500 Subject: [PATCH 044/133] Update install-dotnet --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 70bdcfe2..1b677639 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,7 +58,7 @@ jobs: # global-json-file: global.json - name: Install .NET SDK - uses: nike4613/install-dotnet@58bbd1de08d12205ec06c4ab990c6e495c73d65e + uses: nike4613/install-dotnet@6e785e8b036f6f294c297beab2b875d4cc28a1a0 with: global-json: global.json @@ -140,7 +140,7 @@ jobs: - name: Install global SDK if: ${{ ! matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@58bbd1de08d12205ec06c4ab990c6e495c73d65e + uses: nike4613/install-dotnet@6e785e8b036f6f294c297beab2b875d4cc28a1a0 with: version: "8.0" @@ -153,7 +153,7 @@ jobs: restore-keys: ${{ runner.os }}-nuget-v1- - name: Install restore SDK if: ${{ matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@58bbd1de08d12205ec06c4ab990c6e495c73d65e + uses: nike4613/install-dotnet@6e785e8b036f6f294c297beab2b875d4cc28a1a0 with: global-json: global.json @@ -168,7 +168,7 @@ jobs: - name: Install test target runtime if: ${{ matrix.dotnet.sdk != '' }} - uses: nike4613/install-dotnet@58bbd1de08d12205ec06c4ab990c6e495c73d65e + uses: nike4613/install-dotnet@6e785e8b036f6f294c297beab2b875d4cc28a1a0 with: version: ${{ matrix.dotnet.sdk }} architecture: ${{ matrix.arch }} From efac7c5e088fb94700a7e0aea9ef5cad906acbac Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 00:17:42 -0500 Subject: [PATCH 045/133] Force the usage of the target arch for all SDK installations on test machines --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1b677639..6386887c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -138,10 +138,12 @@ jobs: lfs: true submodules: recursive + # Note: All of the SDKs we install have to be for the target architecture. Otherwise, we get issues when the default != the target. - name: Install global SDK if: ${{ ! matrix.dotnet.needsRestore }} uses: nike4613/install-dotnet@6e785e8b036f6f294c297beab2b875d4cc28a1a0 with: + architecture: ${{ matrix.arch }} version: "8.0" - name: Cache restored NuGet packages @@ -155,6 +157,7 @@ jobs: if: ${{ matrix.dotnet.needsRestore }} uses: nike4613/install-dotnet@6e785e8b036f6f294c297beab2b875d4cc28a1a0 with: + architecture: ${{ matrix.arch }} global-json: global.json - name: Restore packages From 3d1487e21a4d27e6dc4a3d5db979debe22b6466f Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 00:36:56 -0500 Subject: [PATCH 046/133] Disable testing .NET Core 2.1 and enable MacOS 14 Rosetta --- .github/gen-test-matrix.ps1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/gen-test-matrix.ps1 b/.github/gen-test-matrix.ps1 index e0fb604c..4743e168 100644 --- a/.github/gen-test-matrix.ps1 +++ b/.github/gen-test-matrix.ps1 @@ -28,11 +28,11 @@ $operatingSystems = @( hasMono = $true; }, [pscustomobject]@{ - enable = $false; + #enable = $false; name = "MacOS 14 (M1)"; runner = "macos-14"; ridname = "osx"; - arch = @("x64", "arm64"); # x64 comes from Rosetta + arch = @("x64"<#, "arm64"#>); # x64 comes from Rosetta, and we disable arm64 mode for now because we don't support it yet hasMono = $true; } ); @@ -46,6 +46,8 @@ $dotnetVersions = @( isFramework = $true; }, [pscustomobject]@{ + enable = $false; # TODO: fix CI tests for .NET 2.1. For some reason it can't find the testhost (only on non-Linux) + # The application to execute does not exist: 'microsoft.testplatform.testhost/17.2.0\lib/netcoreapp2.1/testhost.dll' name = ".NET Core 2.1"; sdk = "2.1"; tfm = "netcoreapp2.1"; From 6ca7b63a55483f164b94f59ea80989a78b169f70 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 00:56:52 -0500 Subject: [PATCH 047/133] Add auxiliary test runs --- .github/workflows/build.yml | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6386887c..2ed5800e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,6 +44,7 @@ jobs: runs-on: ${{ matrix.os }} env: VersionSuffix: daily.${{needs.compute-version.outputs.ver}} + LOG_FILE_NAME: testresults.${{ matrix.os.runner }}.auxtests.trx steps: - name: Checkout uses: actions/checkout@v4 @@ -58,7 +59,7 @@ jobs: # global-json-file: global.json - name: Install .NET SDK - uses: nike4613/install-dotnet@6e785e8b036f6f294c297beab2b875d4cc28a1a0 + uses: nike4613/install-dotnet@b7764872282b45fd575664e90b278d02b1e3305e with: global-json: global.json @@ -80,6 +81,17 @@ jobs: # TODO: it might be worth trying to do a "smoketest" test run with the installed tfm to broadly make sure that all builds # work. We may also want to do a hash check on the packages, though I have no idea how reliable that would be. + + - name: Run auxiliary tests + run: dotnet test -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" --no-build -f "FullyQualifiedName!~MonoMod.UnitTest" + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: ${{ always() }} + with: + name: test-results aux ${{ runner.os }} + retention-days: 1 + path: 'TestResults/*.trx' - name: Upload binlogs uses: actions/upload-artifact@v4 @@ -141,7 +153,7 @@ jobs: # Note: All of the SDKs we install have to be for the target architecture. Otherwise, we get issues when the default != the target. - name: Install global SDK if: ${{ ! matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@6e785e8b036f6f294c297beab2b875d4cc28a1a0 + uses: nike4613/install-dotnet@b7764872282b45fd575664e90b278d02b1e3305e with: architecture: ${{ matrix.arch }} version: "8.0" @@ -155,7 +167,7 @@ jobs: restore-keys: ${{ runner.os }}-nuget-v1- - name: Install restore SDK if: ${{ matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@6e785e8b036f6f294c297beab2b875d4cc28a1a0 + uses: nike4613/install-dotnet@b7764872282b45fd575664e90b278d02b1e3305e with: architecture: ${{ matrix.arch }} global-json: global.json @@ -171,12 +183,15 @@ jobs: - name: Install test target runtime if: ${{ matrix.dotnet.sdk != '' }} - uses: nike4613/install-dotnet@6e785e8b036f6f294c297beab2b875d4cc28a1a0 + uses: nike4613/install-dotnet@b7764872282b45fd575664e90b278d02b1e3305e with: version: ${{ matrix.dotnet.sdk }} architecture: ${{ matrix.arch }} runtime: dotnet + - name: Print SDK info + run: dotnet --info + - name: Run tests if: ${{ ! matrix.dotnet.isMono && ! matrix.dotnet.pgo }} run: dotnet test -f ${{ matrix.dotnet.tfm }} -a ${{ matrix.arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ matrix.dotnet.tfm }}/MonoMod.UnitTest.dll From 99492b0ec5944fb5903413802d618bc5e49b37e1 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 01:06:05 -0500 Subject: [PATCH 048/133] Auxiliary test should use release configuration --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2ed5800e..a5abfd24 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -83,7 +83,7 @@ jobs: # work. We may also want to do a hash check on the packages, though I have no idea how reliable that would be. - name: Run auxiliary tests - run: dotnet test -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" --no-build -f "FullyQualifiedName!~MonoMod.UnitTest" + run: dotnet test --no-build -c Release -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" -f "FullyQualifiedName!~MonoMod.UnitTest" - name: Upload test results uses: actions/upload-artifact@v4 From 63729d5161600c57cc2004d6d1bd78852c692844 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 01:07:10 -0500 Subject: [PATCH 049/133] If auxiliary run does not produce test output, that is not an error --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a5abfd24..c5001299 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -92,6 +92,7 @@ jobs: name: test-results aux ${{ runner.os }} retention-days: 1 path: 'TestResults/*.trx' + if-no-files-found: ignore - name: Upload binlogs uses: actions/upload-artifact@v4 From 715e91b14454fc12c76a218f5d7b795d36019f79 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 01:08:40 -0500 Subject: [PATCH 050/133] Fix auxiliary test result file name --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c5001299..85585291 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,7 +44,7 @@ jobs: runs-on: ${{ matrix.os }} env: VersionSuffix: daily.${{needs.compute-version.outputs.ver}} - LOG_FILE_NAME: testresults.${{ matrix.os.runner }}.auxtests.trx + LOG_FILE_NAME: testresults.${{ matrix.os }}.auxtests.trx steps: - name: Checkout uses: actions/checkout@v4 From dffba959334f12e15aa3871de27e30e29ce979d4 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 01:29:28 -0500 Subject: [PATCH 051/133] Split out test result publish from main build workflow + update install-dotnet --- .github/workflows/build.yml | 41 +++++++++++----------------- .github/workflows/test-results.yml | 43 ++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 26 deletions(-) create mode 100644 .github/workflows/test-results.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 85585291..a9233e90 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,6 +16,17 @@ env: # Then, we'll take the result from one of those (probaly Linux) and distribute build artifacts to testers to run # a load of tests. This will (eventually) include ARM runners, where possible. jobs: + event_file: + # This job uploads an event file so that our test aggregator and recorder can understand this event + name: "Event File" + runs-on: ubuntu-latest + steps: + - name: Upload + uses: actions/upload-artifact@v4 + with: + name: test-event-file + path: ${{ github.event_path }} + compute-version: name: Compute Version runs-on: ubuntu-latest @@ -59,7 +70,7 @@ jobs: # global-json-file: global.json - name: Install .NET SDK - uses: nike4613/install-dotnet@b7764872282b45fd575664e90b278d02b1e3305e + uses: nike4613/install-dotnet@b00d6cf9321575a6035df1c53d69455eb6353041 with: global-json: global.json @@ -154,7 +165,7 @@ jobs: # Note: All of the SDKs we install have to be for the target architecture. Otherwise, we get issues when the default != the target. - name: Install global SDK if: ${{ ! matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@b7764872282b45fd575664e90b278d02b1e3305e + uses: nike4613/install-dotnet@b00d6cf9321575a6035df1c53d69455eb6353041 with: architecture: ${{ matrix.arch }} version: "8.0" @@ -168,7 +179,7 @@ jobs: restore-keys: ${{ runner.os }}-nuget-v1- - name: Install restore SDK if: ${{ matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@b7764872282b45fd575664e90b278d02b1e3305e + uses: nike4613/install-dotnet@b00d6cf9321575a6035df1c53d69455eb6353041 with: architecture: ${{ matrix.arch }} global-json: global.json @@ -184,7 +195,7 @@ jobs: - name: Install test target runtime if: ${{ matrix.dotnet.sdk != '' }} - uses: nike4613/install-dotnet@b7764872282b45fd575664e90b278d02b1e3305e + uses: nike4613/install-dotnet@b00d6cf9321575a6035df1c53d69455eb6353041 with: version: ${{ matrix.dotnet.sdk }} architecture: ${{ matrix.arch }} @@ -214,25 +225,3 @@ jobs: name: test-results ${{ matrix.title }} retention-days: 1 path: 'TestResults/*.trx' - - publish-test-results: - needs: [build, test] - if: ${{ always() && needs.build.result == 'success' }} - runs-on: ubuntu-latest - name: Publish test results - permissions: - checks: write - pull-requests: write - steps: - - name: Download test results - uses: actions/download-artifact@v4 - with: - pattern: test-results * - merge-multiple: false - - name: Publish Test Results - uses: EnricoMi/publish-unit-test-result-action@v2 - if: always() - with: - files: '**/*.trx' - comment_mode: ${{ (github.event.workflow_run.event == 'pull_request' || github.event_name == 'pull_request') && 'failures' || 'off' }} - report_individual_runs: true diff --git a/.github/workflows/test-results.yml b/.github/workflows/test-results.yml new file mode 100644 index 00000000..371a8c4f --- /dev/null +++ b/.github/workflows/test-results.yml @@ -0,0 +1,43 @@ +name: Publish Test Results + +on: + workflow_run: + workflows: ["build"] + types: [completed] +permissions: {} + +jobs: + publish-test-results: + if: github.event.workflow_run.conclusion != 'skipped' + runs-on: ubuntu-latest + name: Publish test results + permissions: + checks: write + pull-requests: write + actions: read + steps: + + - name: Download event file + uses: actions/download-artifact@v4 + with: + run_id: ${{ github.event.workflow_run.id }} + name: test-event-file + + - name: Download test results + uses: actions/download-artifact@v4 + with: + run_id: ${{ github.event.workflow_run.id }} + pattern: test-results * + merge-multiple: false + + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + commit: ${{ github.event.workflow_run.head_sha }} + event_file: event.json + event_name: ${{ github.event.workflow_run.event }} + + files: '**/*.trx' + comment_mode: ${{ (github.event.workflow_run.event == 'pull_request' || github.event_name == 'pull_request') && 'failures' || 'off' }} + report_individual_runs: true From 3149b9246220986cd6bcb5213b414839471b60b6 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 01:30:44 -0500 Subject: [PATCH 052/133] test-event-file should have retention-days=1 --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a9233e90..fc45dd62 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,6 +26,7 @@ jobs: with: name: test-event-file path: ${{ github.event_path }} + retention-days: 1 compute-version: name: Compute Version From 328993a0e42db0e4791d29b399bf32e629ab3668 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 01:47:34 -0500 Subject: [PATCH 053/133] Adjust test-results yaml and update install-dotnet --- .github/workflows/build.yml | 8 ++++---- .github/workflows/test-results.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fc45dd62..223aa3e2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -71,7 +71,7 @@ jobs: # global-json-file: global.json - name: Install .NET SDK - uses: nike4613/install-dotnet@b00d6cf9321575a6035df1c53d69455eb6353041 + uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 with: global-json: global.json @@ -166,7 +166,7 @@ jobs: # Note: All of the SDKs we install have to be for the target architecture. Otherwise, we get issues when the default != the target. - name: Install global SDK if: ${{ ! matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@b00d6cf9321575a6035df1c53d69455eb6353041 + uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 with: architecture: ${{ matrix.arch }} version: "8.0" @@ -180,7 +180,7 @@ jobs: restore-keys: ${{ runner.os }}-nuget-v1- - name: Install restore SDK if: ${{ matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@b00d6cf9321575a6035df1c53d69455eb6353041 + uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 with: architecture: ${{ matrix.arch }} global-json: global.json @@ -196,7 +196,7 @@ jobs: - name: Install test target runtime if: ${{ matrix.dotnet.sdk != '' }} - uses: nike4613/install-dotnet@b00d6cf9321575a6035df1c53d69455eb6353041 + uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 with: version: ${{ matrix.dotnet.sdk }} architecture: ${{ matrix.arch }} diff --git a/.github/workflows/test-results.yml b/.github/workflows/test-results.yml index 371a8c4f..63d8a02a 100644 --- a/.github/workflows/test-results.yml +++ b/.github/workflows/test-results.yml @@ -2,7 +2,7 @@ name: Publish Test Results on: workflow_run: - workflows: ["build"] + workflows: ["Build + Test"] types: [completed] permissions: {} From 41d80ad07127dc398e5bb81f8821412ae8474fe8 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 01:49:27 -0500 Subject: [PATCH 054/133] Rename test-results to something GitHub might be happier about --- .github/workflows/{test-results.yml => test_results.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{test-results.yml => test_results.yml} (100%) diff --git a/.github/workflows/test-results.yml b/.github/workflows/test_results.yml similarity index 100% rename from .github/workflows/test-results.yml rename to .github/workflows/test_results.yml From 34e62a487742bf29cb7b1e61e98851dc1a479845 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 02:14:03 -0500 Subject: [PATCH 055/133] Adjust target workflow name again --- .github/workflows/test_results.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index 63d8a02a..371a8c4f 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -2,7 +2,7 @@ name: Publish Test Results on: workflow_run: - workflows: ["Build + Test"] + workflows: ["build"] types: [completed] permissions: {} From 52e57842abc2c54588837947f06637ef04366e47 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 02:24:16 -0500 Subject: [PATCH 056/133] Attempt 2 to try to fix on:workflow_run --- .github/workflows/test_results.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index 371a8c4f..25c1cd13 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -2,7 +2,7 @@ name: Publish Test Results on: workflow_run: - workflows: ["build"] + workflows: ["build", "Build + Test"] types: [completed] permissions: {} From 060437938e3c2d34630d29ac233032b37918c222 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 02:30:40 -0500 Subject: [PATCH 057/133] Adjust full workflow names to try to get publish to work --- .github/workflows/build.yml | 2 +- .github/workflows/test_results.yml | 77 +++++++++++++++--------------- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 223aa3e2..7efe6a4d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Build + Test +name: Build and Test on: push: pull_request: diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index 25c1cd13..097e08e3 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -1,43 +1,44 @@ -name: Publish Test Results - -on: - workflow_run: - workflows: ["build", "Build + Test"] - types: [completed] -permissions: {} - -jobs: - publish-test-results: - if: github.event.workflow_run.conclusion != 'skipped' - runs-on: ubuntu-latest - name: Publish test results - permissions: - checks: write - pull-requests: write - actions: read +name: Publish Test Results + +on: + workflow_run: + workflows: + - Build and Test + types: [completed] +permissions: {} + +jobs: + publish-test-results: + if: github.event.workflow_run.conclusion != 'skipped' + runs-on: ubuntu-latest + name: Publish test results + permissions: + checks: write + pull-requests: write + actions: read steps: - - - name: Download event file - uses: actions/download-artifact@v4 - with: - run_id: ${{ github.event.workflow_run.id }} + + - name: Download event file + uses: actions/download-artifact@v4 + with: + run_id: ${{ github.event.workflow_run.id }} name: test-event-file - - - name: Download test results - uses: actions/download-artifact@v4 - with: - run_id: ${{ github.event.workflow_run.id }} - pattern: test-results * + + - name: Download test results + uses: actions/download-artifact@v4 + with: + run_id: ${{ github.event.workflow_run.id }} + pattern: test-results * merge-multiple: false - - - name: Publish Test Results - uses: EnricoMi/publish-unit-test-result-action@v2 - if: always() - with: - commit: ${{ github.event.workflow_run.head_sha }} - event_file: event.json + + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + commit: ${{ github.event.workflow_run.head_sha }} + event_file: event.json event_name: ${{ github.event.workflow_run.event }} - - files: '**/*.trx' - comment_mode: ${{ (github.event.workflow_run.event == 'pull_request' || github.event_name == 'pull_request') && 'failures' || 'off' }} + + files: '**/*.trx' + comment_mode: ${{ (github.event.workflow_run.event == 'pull_request' || github.event_name == 'pull_request') && 'failures' || 'off' }} report_individual_runs: true From 8005100165a18a1c60f5524c83b42de1b525bf20 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 02:44:43 -0500 Subject: [PATCH 058/133] Fix test results workflow --- .github/workflows/test_results.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index 097e08e3..2ba5c8fd 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -21,19 +21,18 @@ jobs: - name: Download event file uses: actions/download-artifact@v4 with: - run_id: ${{ github.event.workflow_run.id }} + run-id: ${{ github.event.workflow_run.id }} name: test-event-file - name: Download test results uses: actions/download-artifact@v4 with: - run_id: ${{ github.event.workflow_run.id }} + run-id: ${{ github.event.workflow_run.id }} pattern: test-results * merge-multiple: false - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 - if: always() with: commit: ${{ github.event.workflow_run.head_sha }} event_file: event.json From 25c5c8919dace156237b55d4b6828ae90f7a18f7 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 03:03:36 -0500 Subject: [PATCH 059/133] Adjust asset downloading for test results --- .github/workflows/test_results.yml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index 2ba5c8fd..d9773184 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -17,25 +17,19 @@ jobs: pull-requests: write actions: read steps: - - - name: Download event file - uses: actions/download-artifact@v4 - with: - run-id: ${{ github.event.workflow_run.id }} - name: test-event-file - + - name: Download test results uses: actions/download-artifact@v4 with: run-id: ${{ github.event.workflow_run.id }} - pattern: test-results * + pattern: test-* merge-multiple: false - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 with: commit: ${{ github.event.workflow_run.head_sha }} - event_file: event.json + event_file: test-event-file/event.json event_name: ${{ github.event.workflow_run.event }} files: '**/*.trx' From 3f2aa8cabcd760bf743566499138a7f8f658a4e2 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 03:21:28 -0500 Subject: [PATCH 060/133] Explicitly pass github.token to download-artifact --- .github/workflows/test_results.yml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index d9773184..f64f4119 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -12,24 +12,36 @@ jobs: if: github.event.workflow_run.conclusion != 'skipped' runs-on: ubuntu-latest name: Publish test results + permissions: checks: write pull-requests: write + contents: read + issues: read actions: read + steps: - - name: Download test results uses: actions/download-artifact@v4 with: run-id: ${{ github.event.workflow_run.id }} - pattern: test-* + github-token: ${{ github.token }} + name: test-event-file + merge-multiple: false + + - name: Download test results + uses: actions/download-artifact@v4 + with: + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ github.token }} + pattern: test-results * merge-multiple: false - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 with: commit: ${{ github.event.workflow_run.head_sha }} - event_file: test-event-file/event.json + event_file: event.json event_name: ${{ github.event.workflow_run.event }} files: '**/*.trx' From 7d53b87fe41ef82af16c09147e5cfb9384fdf713 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 15:16:44 -0500 Subject: [PATCH 061/133] Split logic into local shared actions --- .github/actions/do-build/action.yml | 122 +++++++++++++++ .github/actions/run-sdk-test/action.yml | 105 +++++++++++++ .github/actions/upload-event/action.yml | 13 ++ .github/workflows/build.yml | 198 +++++------------------- 4 files changed, 277 insertions(+), 161 deletions(-) create mode 100644 .github/actions/do-build/action.yml create mode 100644 .github/actions/run-sdk-test/action.yml create mode 100644 .github/actions/upload-event/action.yml diff --git a/.github/actions/do-build/action.yml b/.github/actions/do-build/action.yml new file mode 100644 index 00000000..ac26a4b5 --- /dev/null +++ b/.github/actions/do-build/action.yml @@ -0,0 +1,122 @@ +name: do-build +description: "" + +inputs: + nuget-packages: + description: env.NUGET_PACKAGES + default: ${{ env.NUGET_PACKAGES }} + do-not-add-suffix: + description: "" + version-suffix: + description: VersionSuffix + upload-packages: + description: "" + default: "false" + upload-tests: + description: "" + default: "false" + +runs: + using: "composite" + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + lfs: true + submodules: recursive + + # TODO: maybe we can eventually use package locks for package caching? + #- name: Install .NET SDK + # uses: actions/setup-dotnet@v4 + # with: + # global-json-file: global.json + + - name: Install .NET SDK + uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 + with: + global-json: global.json + + # NOTE: manual package caching + - name: Cache restored NuGet packages + uses: actions/cache@v4 + with: + path: ${{ env.NUGET_PACKAGES }} + key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }} + restore-keys: ${{ runner.os }}-nuget-v1- + - name: Restore + shell: pwsh + env: + NUGET_PACKAGS: ${{ inputs.nuget-packages }} + DOTNET_TELEMETRY_OPTOUT: 'true' + DOTNET_NOLOGO: 'true' + VersionSuffix: daily.${{inputs.version-suffix}} + DoNotAddSuffix: ${{inputs.do-not-add-suffix}} + run: dotnet restore -bl:restore.binlog -noAutoRsp + + - name: Build + shell: pwsh + env: + NUGET_PACKAGS: ${{ inputs.nuget-packages }} + DOTNET_TELEMETRY_OPTOUT: 'true' + DOTNET_NOLOGO: 'true' + VersionSuffix: daily.${{inputs.version-suffix}} + DoNotAddSuffix: ${{inputs.do-not-add-suffix}} + run: dotnet build --no-restore -c Release -p:ContinuousIntegrationBuild=true -bl:build.binlog -clp:NoSummary -noAutoRsp + + - name: Pack + shell: pwsh + env: + NUGET_PACKAGS: ${{ inputs.nuget-packages }} + DOTNET_TELEMETRY_OPTOUT: 'true' + DOTNET_NOLOGO: 'true' + VersionSuffix: daily.${{inputs.version-suffix}} + DoNotAddSuffix: ${{inputs.do-not-add-suffix}} + run: dotnet pack --no-restore -c Release -p:ContinuousIntegrationBuild=true -bl:pack.binlog -clp:NoSummary -noAutoRsp + + # TODO: it might be worth trying to do a "smoketest" test run with the installed tfm to broadly make sure that all builds + # work. We may also want to do a hash check on the packages, though I have no idea how reliable that would be. + + - name: Run auxiliary tests + shell: pwsh + env: + NUGET_PACKAGS: ${{ inputs.nuget-packages }} + DOTNET_TELEMETRY_OPTOUT: 'true' + DOTNET_NOLOGO: 'true' + VersionSuffix: daily.${{inputs.version-suffix}} + DoNotAddSuffix: ${{inputs.do-not-add-suffix}} + LOG_FILE_NAME: testresults.${{ runner.os }}.auxtests.trx + run: dotnet test --no-build -c Release -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" -f "FullyQualifiedName!~MonoMod.UnitTest" + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: ${{ always() }} + with: + name: test-results aux ${{ runner.os }} + retention-days: 1 + path: 'TestResults/*.trx' + if-no-files-found: ignore + + - name: Upload binlogs + uses: actions/upload-artifact@v4 + if: ${{ always() }} + with: + name: binlogs-${{ runner.os }} + path: '*.binlog' + retention-days: 7 + + - name: Archive packages + uses: actions/upload-artifact@v4 + if: ${{ inputs.upload-packages }} + with: + name: packages + path: artifacts/package/release/*.nupkg + + - name: Upload test assets + uses: actions/upload-artifact@v4 + if: ${{ inputs.upload-tests }} + with: + name: test-assets + retention-days: 1 + path: | + artifacts/bin/MonoMod.UnitTest/*/**/* \ No newline at end of file diff --git a/.github/actions/run-sdk-test/action.yml b/.github/actions/run-sdk-test/action.yml new file mode 100644 index 00000000..b9767cfc --- /dev/null +++ b/.github/actions/run-sdk-test/action.yml @@ -0,0 +1,105 @@ +name: upload-event +description: "" + +inputs: + nuget-packages: + description: env.NUGET_PACKAGES + default: ${{ env.NUGET_PACKAGES }} + needsRestore: + description: do we need to run a restore + default: 'false' + arch: + description: arch + sdk: + description: target sdk version + pgo: + description: is this a pgo run + default: 'false' + usePgo: + description: should this run use pgo + tfm: + description: the test tfm + title: + description: the test title + log-file-name: + description: test log file name + +runs: + using: "composite" + + steps: + - name: Checkout + uses: actions/checkout@v4 + if: ${{ inputs.needsRestore }} + with: + lfs: true + submodules: recursive + + # Note: All of the SDKs we install have to be for the target architecture. Otherwise, we get issues when the default != the target. + - name: Install global SDK + if: ${{ ! inputs.needsRestore }} + uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 + with: + architecture: ${{ inputs.arch }} + version: "8.0" + + - name: Cache restored NuGet packages + uses: actions/cache@v4 + if: ${{ inputs.needsRestore }} + with: + path: ${{ inputs.nuget-packages }} + key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }} + restore-keys: ${{ runner.os }}-nuget-v1- + - name: Install restore SDK + if: ${{ inputs.needsRestore }} + uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 + with: + architecture: ${{ matrix.arch }} + global-json: global.json + + - name: Restore packages + if: ${{ inputs.needsRestore }} + shell: pwsh + env: + DOTNET_TELEMETRY_OPTOUT: 'true' + DOTNET_NOLOGO: 'true' + NUGET_PACKAGES: ${{ inputs.nuget-packages }} + run: dotnet restore -noAutoRsp + + - name: Download test assets + uses: actions/download-artifact@v4 + with: + name: test-assets + + - name: Install test target runtime + if: ${{ inputs.sdk != '' }} + uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 + with: + version: ${{ inputs.sdk }} + architecture: ${{ inputs.arch }} + runtime: dotnet + + - name: Print SDK info + run: dotnet --info + + - name: Run tests + if: ${{ ! inputs.pgo }} + run: dotnet test -f ${{ inputs.tfm }} -a ${{ inputs.arch }} -l:"trx;LogFileName=${{ inputs.log-file-name }}" release_${{ inputs.tfm }}/MonoMod.UnitTest.dll + + - name: Run tests (PGO) + if: ${{ matrix.dotnet.pgo }} + env: + DOTNET_ReadyToRun: ${{ matrix.usePgo && 0 || 1 }} + DOTNET_TC_QuicJitForLoops: ${{ matrix.usePgo && 1 || 0 }} + DOTNET_TieredPGO: ${{ matrix.usePgo && 1 || 0 }} + run: dotnet test -f ${{ inputs.tfm }} -a ${{ inputs.arch }} -l:"trx;LogFileName=${{ inputs.log-file-name }}" release_${{ inputs.tfm }}/MonoMod.UnitTest.dll + + # TODO: Mono? + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: ${{ always() }} + with: + name: test-results ${{ inputs.title }} + retention-days: 1 + path: 'TestResults/*.trx' \ No newline at end of file diff --git a/.github/actions/upload-event/action.yml b/.github/actions/upload-event/action.yml new file mode 100644 index 00000000..d4c463cd --- /dev/null +++ b/.github/actions/upload-event/action.yml @@ -0,0 +1,13 @@ +name: upload-event +description: "" + +runs: + using: "composite" + + steps: + - name: Upload + uses: actions/upload-artifact@v4 + with: + name: test-event-file + path: ${{ github.event_path }} + retention-days: 1 \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7efe6a4d..23370db2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,10 +11,7 @@ env: DOTNET_TELEMETRY_OPTOUT: true DOTNET_NOLOGO: true NUGET_PACKAGES: ${{github.workspace}}/artifacts/pkg - -# We'll have a job for building (that runs on x64 machines only, one for each OS to make sure it actually builds) -# Then, we'll take the result from one of those (probaly Linux) and distribute build artifacts to testers to run -# a load of tests. This will (eventually) include ARM runners, where possible. + jobs: event_file: # This job uploads an event file so that our test aggregator and recorder can understand this event @@ -22,11 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Upload - uses: actions/upload-artifact@v4 - with: - name: test-event-file - path: ${{ github.event_path }} - retention-days: 1 + uses: ./.github/actions/upload-event compute-version: name: Compute Version @@ -37,16 +30,24 @@ jobs: - id: computever run: echo "ver=$(Get-Date -Format y.M.d).${{ github.run_number }}.${{ github.run_attempt }}" >> $env:GITHUB_OUTPUT - build: + build-testassets: needs: compute-version + name: 'Build #${{needs.compute-version.outputs.ver}} (Linux)' + runs-on: ubuntu-latest + steps: + - name: Build + uses: ./.github/actions/do-build + with: + nuget-packages: ${{ env.NUGET_PACKAGES }} + version-suffix: daily.${{needs.compute-version.outputs.ver}} + upload-packages: true + upload-tests: true + + build: strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-13] + os: [windows-latest, macos-13] include: - - os: ubuntu-latest - name: Linux - upload-packages: true - upload-tests: true - os: windows-latest name: Windows - os: macos-13 @@ -54,82 +55,15 @@ jobs: name: 'Build #${{needs.compute-version.outputs.ver}} (${{matrix.name}})' runs-on: ${{ matrix.os }} - env: - VersionSuffix: daily.${{needs.compute-version.outputs.ver}} - LOG_FILE_NAME: testresults.${{ matrix.os }}.auxtests.trx steps: - - name: Checkout - uses: actions/checkout@v4 - with: - lfs: true - submodules: recursive - - # TODO: maybe we can eventually use package locks for package caching? - #- name: Install .NET SDK - # uses: actions/setup-dotnet@v4 - # with: - # global-json-file: global.json - - - name: Install .NET SDK - uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 - with: - global-json: global.json - - # NOTE: manual package caching - - name: Cache restored NuGet packages - uses: actions/cache@v4 - with: - path: ${{ env.NUGET_PACKAGES }} - key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }} - restore-keys: ${{ runner.os }}-nuget-v1- - - name: Restore - run: dotnet restore -bl:restore.binlog -noAutoRsp - - name: Build - run: dotnet build --no-restore -c Release -p:ContinuousIntegrationBuild=true -bl:build.binlog -clp:NoSummary -noAutoRsp - - - name: Pack - run: dotnet pack --no-restore -c Release -p:ContinuousIntegrationBuild=true -bl:pack.binlog -clp:NoSummary -noAutoRsp - - # TODO: it might be worth trying to do a "smoketest" test run with the installed tfm to broadly make sure that all builds - # work. We may also want to do a hash check on the packages, though I have no idea how reliable that would be. - - - name: Run auxiliary tests - run: dotnet test --no-build -c Release -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" -f "FullyQualifiedName!~MonoMod.UnitTest" - - - name: Upload test results - uses: actions/upload-artifact@v4 - if: ${{ always() }} - with: - name: test-results aux ${{ runner.os }} - retention-days: 1 - path: 'TestResults/*.trx' - if-no-files-found: ignore - - - name: Upload binlogs - uses: actions/upload-artifact@v4 - if: ${{ always() }} + uses: ./.github/actions/do-build with: - name: binlogs-${{ matrix.name }} - path: '*.binlog' - retention-days: 7 - - - name: Archive packages - uses: actions/upload-artifact@v4 - if: ${{ matrix.upload-packages }} - with: - name: packages - path: artifacts/package/release/*.nupkg - - - name: Upload test assets - uses: actions/upload-artifact@v4 - if: ${{ matrix.upload-tests }} - with: - name: test-assets - retention-days: 1 - path: | - artifacts/bin/MonoMod.UnitTest/*/**/* - + nuget-packages: ${{ env.NUGET_PACKAGES }} + version-suffix: daily.${{needs.compute-version.outputs.ver}} + upload-packages: ${{ matrix.upload-packages }} + upload-tests: ${{ matrix.upload-tests}} + compute-test-matrix: name: Compute Test Matrix runs-on: ubuntu-latest @@ -146,83 +80,25 @@ jobs: run: ./.github/gen-test-matrix.ps1 -MatrixOutName matrix -GithubOutput $env:GITHUB_OUTPUT test: - needs: [compute-test-matrix, build] + needs: [compute-test-matrix, build-testassets] strategy: fail-fast: false matrix: ${{ fromJSON(needs.compute-test-matrix.outputs.matrix) }} name: Test ${{ matrix.title }} runs-on: ${{ matrix.os.runner }} - env: - LOG_FILE_NAME: testresults.${{ matrix.os.runner }}.${{ matrix.dotnet.id != '' && matrix.dotnet.id || matrix.dotnet.sdk }}.${{ matrix.arch }}.trx - steps: - - name: Checkout - uses: actions/checkout@v4 - if: ${{ matrix.dotnet.needsRestore }} - with: - lfs: true - submodules: recursive - - # Note: All of the SDKs we install have to be for the target architecture. Otherwise, we get issues when the default != the target. - - name: Install global SDK - if: ${{ ! matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 - with: - architecture: ${{ matrix.arch }} - version: "8.0" - - - name: Cache restored NuGet packages - uses: actions/cache@v4 - if: ${{ matrix.dotnet.needsRestore }} - with: - path: ${{ env.NUGET_PACKAGES }} - key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }} - restore-keys: ${{ runner.os }}-nuget-v1- - - name: Install restore SDK - if: ${{ matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 - with: - architecture: ${{ matrix.arch }} - global-json: global.json - - - name: Restore packages - if: ${{ matrix.dotnet.needsRestore }} - run: dotnet restore -noAutoRsp - - - name: Download test assets - uses: actions/download-artifact@v4 - with: - name: test-assets + steps: + - uses: ./.github/actions/run-sdk-test + name: Run test suite + if: ${{ ! matrix.dotnet.isMono }} + with: + nuget-packages: ${{ env.NUGET_PACKAGES }} + needsRestore: ${{ matrix.dotnet.needsRestore }} + arch: ${{ matrix.arch }} + sdk: ${{ matrix.dotnet.sdk }} + pgo: ${{ matrix.dotnet.pgo }} + usePgo: ${{ matrix.usePgo }} + tfm: ${{ matrix.dotnet.tfm }} + title: ${{ matrix.title }} + log-file-name: testresults.${{ matrix.os.runner }}.${{ matrix.dotnet.id != '' && matrix.dotnet.id || matrix.dotnet.sdk }}.${{ matrix.arch }}.trx - - name: Install test target runtime - if: ${{ matrix.dotnet.sdk != '' }} - uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 - with: - version: ${{ matrix.dotnet.sdk }} - architecture: ${{ matrix.arch }} - runtime: dotnet - - - name: Print SDK info - run: dotnet --info - - - name: Run tests - if: ${{ ! matrix.dotnet.isMono && ! matrix.dotnet.pgo }} - run: dotnet test -f ${{ matrix.dotnet.tfm }} -a ${{ matrix.arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ matrix.dotnet.tfm }}/MonoMod.UnitTest.dll - - - name: Run tests (PGO) - if: ${{ matrix.dotnet.pgo }} - env: - DOTNET_ReadyToRun: ${{ matrix.usePgo && 0 || 1 }} - DOTNET_TC_QuicJitForLoops: ${{ matrix.usePgo && 1 || 0 }} - DOTNET_TieredPGO: ${{ matrix.usePgo && 1 || 0 }} - run: dotnet test -f ${{ matrix.dotnet.tfm }} -a ${{ matrix.arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ matrix.dotnet.tfm }}/MonoMod.UnitTest.dll - - # TODO: Mono? - - - name: Upload test results - uses: actions/upload-artifact@v4 - if: ${{ always() }} - with: - name: test-results ${{ matrix.title }} - retention-days: 1 - path: 'TestResults/*.trx' From 0b958ea9d7d1db8ad5390b093545e56e01d00ee7 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 15:18:32 -0500 Subject: [PATCH 062/133] Revert "Explicitly pass github.token to download-artifact" This reverts commit 3f2aa8cabcd760bf743566499138a7f8f658a4e2. --- .github/workflows/test_results.yml | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index f64f4119..d9773184 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -12,36 +12,24 @@ jobs: if: github.event.workflow_run.conclusion != 'skipped' runs-on: ubuntu-latest name: Publish test results - permissions: checks: write pull-requests: write - contents: read - issues: read actions: read - steps: + - name: Download test results uses: actions/download-artifact@v4 with: run-id: ${{ github.event.workflow_run.id }} - github-token: ${{ github.token }} - name: test-event-file - merge-multiple: false - - - name: Download test results - uses: actions/download-artifact@v4 - with: - run-id: ${{ github.event.workflow_run.id }} - github-token: ${{ github.token }} - pattern: test-results * + pattern: test-* merge-multiple: false - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 with: commit: ${{ github.event.workflow_run.head_sha }} - event_file: event.json + event_file: test-event-file/event.json event_name: ${{ github.event.workflow_run.event }} files: '**/*.trx' From 7bf5d36bf3f102e509a61244d9ed1523efb96024 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 15:18:44 -0500 Subject: [PATCH 063/133] Reapply "Explicitly pass github.token to download-artifact" This reverts commit 0b958ea9d7d1db8ad5390b093545e56e01d00ee7. --- .github/workflows/test_results.yml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index d9773184..f64f4119 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -12,24 +12,36 @@ jobs: if: github.event.workflow_run.conclusion != 'skipped' runs-on: ubuntu-latest name: Publish test results + permissions: checks: write pull-requests: write + contents: read + issues: read actions: read + steps: - - name: Download test results uses: actions/download-artifact@v4 with: run-id: ${{ github.event.workflow_run.id }} - pattern: test-* + github-token: ${{ github.token }} + name: test-event-file + merge-multiple: false + + - name: Download test results + uses: actions/download-artifact@v4 + with: + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ github.token }} + pattern: test-results * merge-multiple: false - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 with: commit: ${{ github.event.workflow_run.head_sha }} - event_file: test-event-file/event.json + event_file: event.json event_name: ${{ github.event.workflow_run.event }} files: '**/*.trx' From cd10290f22123fa2e0ffbf37f64fd51afd2e9ad0 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 15:18:55 -0500 Subject: [PATCH 064/133] Revert "Split logic into local shared actions" This reverts commit 7d53b87fe41ef82af16c09147e5cfb9384fdf713. --- .github/actions/do-build/action.yml | 122 --------------- .github/actions/run-sdk-test/action.yml | 105 ------------- .github/actions/upload-event/action.yml | 13 -- .github/workflows/build.yml | 198 +++++++++++++++++++----- 4 files changed, 161 insertions(+), 277 deletions(-) delete mode 100644 .github/actions/do-build/action.yml delete mode 100644 .github/actions/run-sdk-test/action.yml delete mode 100644 .github/actions/upload-event/action.yml diff --git a/.github/actions/do-build/action.yml b/.github/actions/do-build/action.yml deleted file mode 100644 index ac26a4b5..00000000 --- a/.github/actions/do-build/action.yml +++ /dev/null @@ -1,122 +0,0 @@ -name: do-build -description: "" - -inputs: - nuget-packages: - description: env.NUGET_PACKAGES - default: ${{ env.NUGET_PACKAGES }} - do-not-add-suffix: - description: "" - version-suffix: - description: VersionSuffix - upload-packages: - description: "" - default: "false" - upload-tests: - description: "" - default: "false" - -runs: - using: "composite" - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - lfs: true - submodules: recursive - - # TODO: maybe we can eventually use package locks for package caching? - #- name: Install .NET SDK - # uses: actions/setup-dotnet@v4 - # with: - # global-json-file: global.json - - - name: Install .NET SDK - uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 - with: - global-json: global.json - - # NOTE: manual package caching - - name: Cache restored NuGet packages - uses: actions/cache@v4 - with: - path: ${{ env.NUGET_PACKAGES }} - key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }} - restore-keys: ${{ runner.os }}-nuget-v1- - - name: Restore - shell: pwsh - env: - NUGET_PACKAGS: ${{ inputs.nuget-packages }} - DOTNET_TELEMETRY_OPTOUT: 'true' - DOTNET_NOLOGO: 'true' - VersionSuffix: daily.${{inputs.version-suffix}} - DoNotAddSuffix: ${{inputs.do-not-add-suffix}} - run: dotnet restore -bl:restore.binlog -noAutoRsp - - - name: Build - shell: pwsh - env: - NUGET_PACKAGS: ${{ inputs.nuget-packages }} - DOTNET_TELEMETRY_OPTOUT: 'true' - DOTNET_NOLOGO: 'true' - VersionSuffix: daily.${{inputs.version-suffix}} - DoNotAddSuffix: ${{inputs.do-not-add-suffix}} - run: dotnet build --no-restore -c Release -p:ContinuousIntegrationBuild=true -bl:build.binlog -clp:NoSummary -noAutoRsp - - - name: Pack - shell: pwsh - env: - NUGET_PACKAGS: ${{ inputs.nuget-packages }} - DOTNET_TELEMETRY_OPTOUT: 'true' - DOTNET_NOLOGO: 'true' - VersionSuffix: daily.${{inputs.version-suffix}} - DoNotAddSuffix: ${{inputs.do-not-add-suffix}} - run: dotnet pack --no-restore -c Release -p:ContinuousIntegrationBuild=true -bl:pack.binlog -clp:NoSummary -noAutoRsp - - # TODO: it might be worth trying to do a "smoketest" test run with the installed tfm to broadly make sure that all builds - # work. We may also want to do a hash check on the packages, though I have no idea how reliable that would be. - - - name: Run auxiliary tests - shell: pwsh - env: - NUGET_PACKAGS: ${{ inputs.nuget-packages }} - DOTNET_TELEMETRY_OPTOUT: 'true' - DOTNET_NOLOGO: 'true' - VersionSuffix: daily.${{inputs.version-suffix}} - DoNotAddSuffix: ${{inputs.do-not-add-suffix}} - LOG_FILE_NAME: testresults.${{ runner.os }}.auxtests.trx - run: dotnet test --no-build -c Release -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" -f "FullyQualifiedName!~MonoMod.UnitTest" - - - name: Upload test results - uses: actions/upload-artifact@v4 - if: ${{ always() }} - with: - name: test-results aux ${{ runner.os }} - retention-days: 1 - path: 'TestResults/*.trx' - if-no-files-found: ignore - - - name: Upload binlogs - uses: actions/upload-artifact@v4 - if: ${{ always() }} - with: - name: binlogs-${{ runner.os }} - path: '*.binlog' - retention-days: 7 - - - name: Archive packages - uses: actions/upload-artifact@v4 - if: ${{ inputs.upload-packages }} - with: - name: packages - path: artifacts/package/release/*.nupkg - - - name: Upload test assets - uses: actions/upload-artifact@v4 - if: ${{ inputs.upload-tests }} - with: - name: test-assets - retention-days: 1 - path: | - artifacts/bin/MonoMod.UnitTest/*/**/* \ No newline at end of file diff --git a/.github/actions/run-sdk-test/action.yml b/.github/actions/run-sdk-test/action.yml deleted file mode 100644 index b9767cfc..00000000 --- a/.github/actions/run-sdk-test/action.yml +++ /dev/null @@ -1,105 +0,0 @@ -name: upload-event -description: "" - -inputs: - nuget-packages: - description: env.NUGET_PACKAGES - default: ${{ env.NUGET_PACKAGES }} - needsRestore: - description: do we need to run a restore - default: 'false' - arch: - description: arch - sdk: - description: target sdk version - pgo: - description: is this a pgo run - default: 'false' - usePgo: - description: should this run use pgo - tfm: - description: the test tfm - title: - description: the test title - log-file-name: - description: test log file name - -runs: - using: "composite" - - steps: - - name: Checkout - uses: actions/checkout@v4 - if: ${{ inputs.needsRestore }} - with: - lfs: true - submodules: recursive - - # Note: All of the SDKs we install have to be for the target architecture. Otherwise, we get issues when the default != the target. - - name: Install global SDK - if: ${{ ! inputs.needsRestore }} - uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 - with: - architecture: ${{ inputs.arch }} - version: "8.0" - - - name: Cache restored NuGet packages - uses: actions/cache@v4 - if: ${{ inputs.needsRestore }} - with: - path: ${{ inputs.nuget-packages }} - key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }} - restore-keys: ${{ runner.os }}-nuget-v1- - - name: Install restore SDK - if: ${{ inputs.needsRestore }} - uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 - with: - architecture: ${{ matrix.arch }} - global-json: global.json - - - name: Restore packages - if: ${{ inputs.needsRestore }} - shell: pwsh - env: - DOTNET_TELEMETRY_OPTOUT: 'true' - DOTNET_NOLOGO: 'true' - NUGET_PACKAGES: ${{ inputs.nuget-packages }} - run: dotnet restore -noAutoRsp - - - name: Download test assets - uses: actions/download-artifact@v4 - with: - name: test-assets - - - name: Install test target runtime - if: ${{ inputs.sdk != '' }} - uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 - with: - version: ${{ inputs.sdk }} - architecture: ${{ inputs.arch }} - runtime: dotnet - - - name: Print SDK info - run: dotnet --info - - - name: Run tests - if: ${{ ! inputs.pgo }} - run: dotnet test -f ${{ inputs.tfm }} -a ${{ inputs.arch }} -l:"trx;LogFileName=${{ inputs.log-file-name }}" release_${{ inputs.tfm }}/MonoMod.UnitTest.dll - - - name: Run tests (PGO) - if: ${{ matrix.dotnet.pgo }} - env: - DOTNET_ReadyToRun: ${{ matrix.usePgo && 0 || 1 }} - DOTNET_TC_QuicJitForLoops: ${{ matrix.usePgo && 1 || 0 }} - DOTNET_TieredPGO: ${{ matrix.usePgo && 1 || 0 }} - run: dotnet test -f ${{ inputs.tfm }} -a ${{ inputs.arch }} -l:"trx;LogFileName=${{ inputs.log-file-name }}" release_${{ inputs.tfm }}/MonoMod.UnitTest.dll - - # TODO: Mono? - - - name: Upload test results - uses: actions/upload-artifact@v4 - if: ${{ always() }} - with: - name: test-results ${{ inputs.title }} - retention-days: 1 - path: 'TestResults/*.trx' \ No newline at end of file diff --git a/.github/actions/upload-event/action.yml b/.github/actions/upload-event/action.yml deleted file mode 100644 index d4c463cd..00000000 --- a/.github/actions/upload-event/action.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: upload-event -description: "" - -runs: - using: "composite" - - steps: - - name: Upload - uses: actions/upload-artifact@v4 - with: - name: test-event-file - path: ${{ github.event_path }} - retention-days: 1 \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 23370db2..7efe6a4d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,10 @@ env: DOTNET_TELEMETRY_OPTOUT: true DOTNET_NOLOGO: true NUGET_PACKAGES: ${{github.workspace}}/artifacts/pkg - + +# We'll have a job for building (that runs on x64 machines only, one for each OS to make sure it actually builds) +# Then, we'll take the result from one of those (probaly Linux) and distribute build artifacts to testers to run +# a load of tests. This will (eventually) include ARM runners, where possible. jobs: event_file: # This job uploads an event file so that our test aggregator and recorder can understand this event @@ -19,7 +22,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Upload - uses: ./.github/actions/upload-event + uses: actions/upload-artifact@v4 + with: + name: test-event-file + path: ${{ github.event_path }} + retention-days: 1 compute-version: name: Compute Version @@ -30,24 +37,16 @@ jobs: - id: computever run: echo "ver=$(Get-Date -Format y.M.d).${{ github.run_number }}.${{ github.run_attempt }}" >> $env:GITHUB_OUTPUT - build-testassets: - needs: compute-version - name: 'Build #${{needs.compute-version.outputs.ver}} (Linux)' - runs-on: ubuntu-latest - steps: - - name: Build - uses: ./.github/actions/do-build - with: - nuget-packages: ${{ env.NUGET_PACKAGES }} - version-suffix: daily.${{needs.compute-version.outputs.ver}} - upload-packages: true - upload-tests: true - build: + needs: compute-version strategy: matrix: - os: [windows-latest, macos-13] + os: [ubuntu-latest, windows-latest, macos-13] include: + - os: ubuntu-latest + name: Linux + upload-packages: true + upload-tests: true - os: windows-latest name: Windows - os: macos-13 @@ -55,15 +54,82 @@ jobs: name: 'Build #${{needs.compute-version.outputs.ver}} (${{matrix.name}})' runs-on: ${{ matrix.os }} + env: + VersionSuffix: daily.${{needs.compute-version.outputs.ver}} + LOG_FILE_NAME: testresults.${{ matrix.os }}.auxtests.trx steps: - - name: Build - uses: ./.github/actions/do-build + - name: Checkout + uses: actions/checkout@v4 with: - nuget-packages: ${{ env.NUGET_PACKAGES }} - version-suffix: daily.${{needs.compute-version.outputs.ver}} - upload-packages: ${{ matrix.upload-packages }} - upload-tests: ${{ matrix.upload-tests}} + lfs: true + submodules: recursive + + # TODO: maybe we can eventually use package locks for package caching? + #- name: Install .NET SDK + # uses: actions/setup-dotnet@v4 + # with: + # global-json-file: global.json + + - name: Install .NET SDK + uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 + with: + global-json: global.json + # NOTE: manual package caching + - name: Cache restored NuGet packages + uses: actions/cache@v4 + with: + path: ${{ env.NUGET_PACKAGES }} + key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }} + restore-keys: ${{ runner.os }}-nuget-v1- + - name: Restore + run: dotnet restore -bl:restore.binlog -noAutoRsp + + - name: Build + run: dotnet build --no-restore -c Release -p:ContinuousIntegrationBuild=true -bl:build.binlog -clp:NoSummary -noAutoRsp + + - name: Pack + run: dotnet pack --no-restore -c Release -p:ContinuousIntegrationBuild=true -bl:pack.binlog -clp:NoSummary -noAutoRsp + + # TODO: it might be worth trying to do a "smoketest" test run with the installed tfm to broadly make sure that all builds + # work. We may also want to do a hash check on the packages, though I have no idea how reliable that would be. + + - name: Run auxiliary tests + run: dotnet test --no-build -c Release -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" -f "FullyQualifiedName!~MonoMod.UnitTest" + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: ${{ always() }} + with: + name: test-results aux ${{ runner.os }} + retention-days: 1 + path: 'TestResults/*.trx' + if-no-files-found: ignore + + - name: Upload binlogs + uses: actions/upload-artifact@v4 + if: ${{ always() }} + with: + name: binlogs-${{ matrix.name }} + path: '*.binlog' + retention-days: 7 + + - name: Archive packages + uses: actions/upload-artifact@v4 + if: ${{ matrix.upload-packages }} + with: + name: packages + path: artifacts/package/release/*.nupkg + + - name: Upload test assets + uses: actions/upload-artifact@v4 + if: ${{ matrix.upload-tests }} + with: + name: test-assets + retention-days: 1 + path: | + artifacts/bin/MonoMod.UnitTest/*/**/* + compute-test-matrix: name: Compute Test Matrix runs-on: ubuntu-latest @@ -80,25 +146,83 @@ jobs: run: ./.github/gen-test-matrix.ps1 -MatrixOutName matrix -GithubOutput $env:GITHUB_OUTPUT test: - needs: [compute-test-matrix, build-testassets] + needs: [compute-test-matrix, build] strategy: fail-fast: false matrix: ${{ fromJSON(needs.compute-test-matrix.outputs.matrix) }} name: Test ${{ matrix.title }} runs-on: ${{ matrix.os.runner }} - steps: - - uses: ./.github/actions/run-sdk-test - name: Run test suite - if: ${{ ! matrix.dotnet.isMono }} - with: - nuget-packages: ${{ env.NUGET_PACKAGES }} - needsRestore: ${{ matrix.dotnet.needsRestore }} - arch: ${{ matrix.arch }} - sdk: ${{ matrix.dotnet.sdk }} - pgo: ${{ matrix.dotnet.pgo }} - usePgo: ${{ matrix.usePgo }} - tfm: ${{ matrix.dotnet.tfm }} - title: ${{ matrix.title }} - log-file-name: testresults.${{ matrix.os.runner }}.${{ matrix.dotnet.id != '' && matrix.dotnet.id || matrix.dotnet.sdk }}.${{ matrix.arch }}.trx + env: + LOG_FILE_NAME: testresults.${{ matrix.os.runner }}.${{ matrix.dotnet.id != '' && matrix.dotnet.id || matrix.dotnet.sdk }}.${{ matrix.arch }}.trx + steps: + - name: Checkout + uses: actions/checkout@v4 + if: ${{ matrix.dotnet.needsRestore }} + with: + lfs: true + submodules: recursive + # Note: All of the SDKs we install have to be for the target architecture. Otherwise, we get issues when the default != the target. + - name: Install global SDK + if: ${{ ! matrix.dotnet.needsRestore }} + uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 + with: + architecture: ${{ matrix.arch }} + version: "8.0" + + - name: Cache restored NuGet packages + uses: actions/cache@v4 + if: ${{ matrix.dotnet.needsRestore }} + with: + path: ${{ env.NUGET_PACKAGES }} + key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }} + restore-keys: ${{ runner.os }}-nuget-v1- + - name: Install restore SDK + if: ${{ matrix.dotnet.needsRestore }} + uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 + with: + architecture: ${{ matrix.arch }} + global-json: global.json + + - name: Restore packages + if: ${{ matrix.dotnet.needsRestore }} + run: dotnet restore -noAutoRsp + + - name: Download test assets + uses: actions/download-artifact@v4 + with: + name: test-assets + + - name: Install test target runtime + if: ${{ matrix.dotnet.sdk != '' }} + uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 + with: + version: ${{ matrix.dotnet.sdk }} + architecture: ${{ matrix.arch }} + runtime: dotnet + + - name: Print SDK info + run: dotnet --info + + - name: Run tests + if: ${{ ! matrix.dotnet.isMono && ! matrix.dotnet.pgo }} + run: dotnet test -f ${{ matrix.dotnet.tfm }} -a ${{ matrix.arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ matrix.dotnet.tfm }}/MonoMod.UnitTest.dll + + - name: Run tests (PGO) + if: ${{ matrix.dotnet.pgo }} + env: + DOTNET_ReadyToRun: ${{ matrix.usePgo && 0 || 1 }} + DOTNET_TC_QuicJitForLoops: ${{ matrix.usePgo && 1 || 0 }} + DOTNET_TieredPGO: ${{ matrix.usePgo && 1 || 0 }} + run: dotnet test -f ${{ matrix.dotnet.tfm }} -a ${{ matrix.arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ matrix.dotnet.tfm }}/MonoMod.UnitTest.dll + + # TODO: Mono? + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: ${{ always() }} + with: + name: test-results ${{ matrix.title }} + retention-days: 1 + path: 'TestResults/*.trx' From 6f396c18fadbfdc49b7403055736938219b0a96a Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 17:59:05 -0500 Subject: [PATCH 065/133] Try to split into shared workflows --- .github/workflows/build.yml | 174 ++++++------------------------------ .github/workflows/ci.yml | 84 +++++++++++++++++ .github/workflows/test.yml | 96 ++++++++++++++++++++ 3 files changed, 208 insertions(+), 146 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7efe6a4d..923d6320 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,7 +1,23 @@ -name: Build and Test +name: Build + on: - push: - pull_request: + workflow_call: + inputs: + os: + required: true + type: string + version: + required: true + type: string + no-suffix: + type: boolean + default: false + upload-packages: + type: boolean + default: false + upload-tests: + type: boolean + default: false defaults: run: @@ -12,51 +28,14 @@ env: DOTNET_NOLOGO: true NUGET_PACKAGES: ${{github.workspace}}/artifacts/pkg -# We'll have a job for building (that runs on x64 machines only, one for each OS to make sure it actually builds) -# Then, we'll take the result from one of those (probaly Linux) and distribute build artifacts to testers to run -# a load of tests. This will (eventually) include ARM runners, where possible. jobs: - event_file: - # This job uploads an event file so that our test aggregator and recorder can understand this event - name: "Event File" - runs-on: ubuntu-latest - steps: - - name: Upload - uses: actions/upload-artifact@v4 - with: - name: test-event-file - path: ${{ github.event_path }} - retention-days: 1 - - compute-version: - name: Compute Version - runs-on: ubuntu-latest - outputs: - ver: ${{ steps.computever.outputs.ver }} - steps: - - id: computever - run: echo "ver=$(Get-Date -Format y.M.d).${{ github.run_number }}.${{ github.run_attempt }}" >> $env:GITHUB_OUTPUT - build: - needs: compute-version - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macos-13] - include: - - os: ubuntu-latest - name: Linux - upload-packages: true - upload-tests: true - - os: windows-latest - name: Windows - - os: macos-13 - name: MacOS - - name: 'Build #${{needs.compute-version.outputs.ver}} (${{matrix.name}})' - runs-on: ${{ matrix.os }} + runs-on: ${{ inputs.os }} + name: 'Build #${{ inputs.version }} (${{ runner.os }})' env: - VersionSuffix: daily.${{needs.compute-version.outputs.ver}} - LOG_FILE_NAME: testresults.${{ matrix.os }}.auxtests.trx + LOG_FILE_NAME: testresults.${{ inputs.os }}.auxtests.trx + VersionSuffix: ${{ !no-suffix && format('daily.{0}', inputs.version) || '' }} + DoNotAddSuffix: ${{ no-suffix && '1' || '' }} steps: - name: Checkout uses: actions/checkout@v4 @@ -110,119 +89,22 @@ jobs: uses: actions/upload-artifact@v4 if: ${{ always() }} with: - name: binlogs-${{ matrix.name }} + name: binlogs-${{ runner.os }} path: '*.binlog' retention-days: 7 - name: Archive packages uses: actions/upload-artifact@v4 - if: ${{ matrix.upload-packages }} + if: ${{ inputs.upload-packages }} with: name: packages path: artifacts/package/release/*.nupkg - name: Upload test assets uses: actions/upload-artifact@v4 - if: ${{ matrix.upload-tests }} + if: ${{ inputs.upload-tests }} with: name: test-assets retention-days: 1 path: | - artifacts/bin/MonoMod.UnitTest/*/**/* - - compute-test-matrix: - name: Compute Test Matrix - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.compute-matrix.outputs.matrix }} - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - lfs: false - submodules: false - - name: Compute test matrix - id: compute-matrix - run: ./.github/gen-test-matrix.ps1 -MatrixOutName matrix -GithubOutput $env:GITHUB_OUTPUT - - test: - needs: [compute-test-matrix, build] - strategy: - fail-fast: false - matrix: ${{ fromJSON(needs.compute-test-matrix.outputs.matrix) }} - - name: Test ${{ matrix.title }} - runs-on: ${{ matrix.os.runner }} - env: - LOG_FILE_NAME: testresults.${{ matrix.os.runner }}.${{ matrix.dotnet.id != '' && matrix.dotnet.id || matrix.dotnet.sdk }}.${{ matrix.arch }}.trx - steps: - - name: Checkout - uses: actions/checkout@v4 - if: ${{ matrix.dotnet.needsRestore }} - with: - lfs: true - submodules: recursive - - # Note: All of the SDKs we install have to be for the target architecture. Otherwise, we get issues when the default != the target. - - name: Install global SDK - if: ${{ ! matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 - with: - architecture: ${{ matrix.arch }} - version: "8.0" - - - name: Cache restored NuGet packages - uses: actions/cache@v4 - if: ${{ matrix.dotnet.needsRestore }} - with: - path: ${{ env.NUGET_PACKAGES }} - key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }} - restore-keys: ${{ runner.os }}-nuget-v1- - - name: Install restore SDK - if: ${{ matrix.dotnet.needsRestore }} - uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 - with: - architecture: ${{ matrix.arch }} - global-json: global.json - - - name: Restore packages - if: ${{ matrix.dotnet.needsRestore }} - run: dotnet restore -noAutoRsp - - - name: Download test assets - uses: actions/download-artifact@v4 - with: - name: test-assets - - - name: Install test target runtime - if: ${{ matrix.dotnet.sdk != '' }} - uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 - with: - version: ${{ matrix.dotnet.sdk }} - architecture: ${{ matrix.arch }} - runtime: dotnet - - - name: Print SDK info - run: dotnet --info - - - name: Run tests - if: ${{ ! matrix.dotnet.isMono && ! matrix.dotnet.pgo }} - run: dotnet test -f ${{ matrix.dotnet.tfm }} -a ${{ matrix.arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ matrix.dotnet.tfm }}/MonoMod.UnitTest.dll - - - name: Run tests (PGO) - if: ${{ matrix.dotnet.pgo }} - env: - DOTNET_ReadyToRun: ${{ matrix.usePgo && 0 || 1 }} - DOTNET_TC_QuicJitForLoops: ${{ matrix.usePgo && 1 || 0 }} - DOTNET_TieredPGO: ${{ matrix.usePgo && 1 || 0 }} - run: dotnet test -f ${{ matrix.dotnet.tfm }} -a ${{ matrix.arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ matrix.dotnet.tfm }}/MonoMod.UnitTest.dll - - # TODO: Mono? - - - name: Upload test results - uses: actions/upload-artifact@v4 - if: ${{ always() }} - with: - name: test-results ${{ matrix.title }} - retention-days: 1 - path: 'TestResults/*.trx' + artifacts/bin/MonoMod.UnitTest/*/**/* \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..7ce9bd3f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,84 @@ +name: Build and Test +on: + push: + pull_request: + +defaults: + run: + shell: pwsh + +env: + DOTNET_TELEMETRY_OPTOUT: true + DOTNET_NOLOGO: true + NUGET_PACKAGES: ${{github.workspace}}/artifacts/pkg + +# We'll have a job for building (that runs on x64 machines only, one for each OS to make sure it actually builds) +# Then, we'll take the result from one of those (probaly Linux) and distribute build artifacts to testers to run +# a load of tests. This will (eventually) include ARM runners, where possible. +jobs: + event_file: + # This job uploads an event file so that our test aggregator and recorder can understand this event + name: "Event File" + runs-on: ubuntu-latest + steps: + - name: Upload + uses: actions/upload-artifact@v4 + with: + name: test-event-file + path: ${{ github.event_path }} + retention-days: 1 + + compute-version: + name: Compute Version + runs-on: ubuntu-latest + outputs: + ver: ${{ steps.computever.outputs.ver }} + steps: + - id: computever + run: echo "ver=$(Get-Date -Format y.M.d).${{ github.run_number }}.${{ github.run_attempt }}" >> $env:GITHUB_OUTPUT + + build: + needs: compute-version + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-13] + include: + - os: ubuntu-latest + name: Linux + upload-packages: true + upload-tests: true + - os: windows-latest + name: Windows + - os: macos-13 + name: MacOS + + uses: ./.github/workflows/build.yml + with: + os: ${{ matrix.os }} + version: ${{ needs.compute-version.outputs.ver }} + upload-packages: ${{ matrix.upload-packages }} + upload-tests: ${{ matrix.upload-tests }} + + compute-test-matrix: + name: Compute Test Matrix + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.compute-matrix.outputs.matrix }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + lfs: false + submodules: false + - name: Compute test matrix + id: compute-matrix + run: ./.github/gen-test-matrix.ps1 -MatrixOutName matrix -GithubOutput $env:GITHUB_OUTPUT + + test: + needs: [compute-test-matrix, build] + strategy: + fail-fast: false + matrix: ${{ fromJSON(needs.compute-test-matrix.outputs.matrix) }} + uses: ./.github/workflows/test.yml + with: + matrix: ${{ toJSON(matrix) }} \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..4b8d7edd --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,96 @@ +name: Test + +on: + workflow_call: + inputs: + matrix: + required: true + type: string + +defaults: + run: + shell: pwsh + +env: + DOTNET_TELEMETRY_OPTOUT: true + DOTNET_NOLOGO: true + NUGET_PACKAGES: ${{github.workspace}}/artifacts/pkg + +jobs: + test: + name: Test ${{ fromJSON(matrix).title }} + runs-on: ${{ fromJSON(matrix).os.runner }} + env: + LOG_FILE_NAME: testresults.${{ fromJSON(matrix).os.runner }}.${{ fromJSON(matrix).dotnet.id != '' && fromJSON(matrix).dotnet.id || fromJSON(matrix).dotnet.sdk }}.${{ fromJSON(matrix).arch }}.trx + steps: + - name: Checkout + uses: actions/checkout@v4 + if: ${{ fromJSON(matrix).dotnet.needsRestore }} + with: + lfs: true + submodules: recursive + + # Note: All of the SDKs we install have to be for the target architecture. Otherwise, we get issues when the default != the target. + - name: Install global SDK + if: ${{ ! fromJSON(matrix).dotnet.needsRestore }} + uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 + with: + architecture: ${{ fromJSON(matrix).arch }} + version: "8.0" + + - name: Cache restored NuGet packages + uses: actions/cache@v4 + if: ${{ fromJSON(matrix).dotnet.needsRestore }} + with: + path: ${{ env.NUGET_PACKAGES }} + key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }} + restore-keys: ${{ runner.os }}-nuget-v1- + + - name: Install restore SDK + if: ${{ fromJSON(matrix).dotnet.needsRestore }} + uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 + with: + architecture: ${{ fromJSON(matrix).arch }} + global-json: global.json + + - name: Restore packages + if: ${{ fromJSON(matrix).dotnet.needsRestore }} + run: dotnet restore -noAutoRsp + + - name: Download test assets + uses: actions/download-artifact@v4 + with: + name: test-assets + + - name: Install test target runtime + if: ${{ fromJSON(matrix).dotnet.sdk != '' }} + uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 + with: + version: ${{ fromJSON(matrix).dotnet.sdk }} + architecture: ${{ fromJSON(matrix).arch }} + runtime: dotnet + + - name: Print SDK info + run: dotnet --info + + - name: Run tests + if: ${{ ! fromJSON(matrix).dotnet.isMono && ! fromJSON(matrix).dotnet.pgo }} + run: dotnet test -f ${{ fromJSON(matrix).dotnet.tfm }} -a ${{ fromJSON(matrix).arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ fromJSON(matrix).dotnet.tfm }}/MonoMod.UnitTest.dll + + - name: Run tests (PGO) + if: ${{ fromJSON(matrix).dotnet.pgo }} + env: + DOTNET_ReadyToRun: ${{ fromJSON(matrix).usePgo && 0 || 1 }} + DOTNET_TC_QuicJitForLoops: ${{ fromJSON(matrix).usePgo && 1 || 0 }} + DOTNET_TieredPGO: ${{ fromJSON(matrix).usePgo && 1 || 0 }} + run: dotnet test -f ${{ fromJSON(matrix).dotnet.tfm }} -a ${{ fromJSON(matrix).arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ fromJSON(matrix).dotnet.tfm }}/MonoMod.UnitTest.dll + + # TODO: Mono? + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: ${{ always() }} + with: + name: test-results ${{ fromJSON(matrix).title }} + retention-days: 1 + path: 'TestResults/*.trx' From 4e5f23937cd92f035f953a79bf87c313577d4cab Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 18:02:47 -0500 Subject: [PATCH 066/133] Fixes --- .github/workflows/build.yml | 9 +++++--- .github/workflows/ci.yml | 1 + .github/workflows/test.yml | 42 ++++++++++++++++++------------------- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 923d6320..22f6009f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,6 +6,9 @@ on: os: required: true type: string + osname: + required: true + type: string version: required: true type: string @@ -31,11 +34,11 @@ env: jobs: build: runs-on: ${{ inputs.os }} - name: 'Build #${{ inputs.version }} (${{ runner.os }})' + name: 'Build #${{ inputs.version }} (${{ inputs.osname }})' env: LOG_FILE_NAME: testresults.${{ inputs.os }}.auxtests.trx - VersionSuffix: ${{ !no-suffix && format('daily.{0}', inputs.version) || '' }} - DoNotAddSuffix: ${{ no-suffix && '1' || '' }} + VersionSuffix: ${{ !inputs.no-suffix && format('daily.{0}', inputs.version) || '' }} + DoNotAddSuffix: ${{ inputs.no-suffix && '1' || '' }} steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ce9bd3f..1292ae43 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,6 +55,7 @@ jobs: uses: ./.github/workflows/build.yml with: os: ${{ matrix.os }} + osname: ${{ matrix.name }} version: ${{ needs.compute-version.outputs.ver }} upload-packages: ${{ matrix.upload-packages }} upload-tests: ${{ matrix.upload-tests }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4b8d7edd..4afde55a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,43 +18,43 @@ env: jobs: test: - name: Test ${{ fromJSON(matrix).title }} - runs-on: ${{ fromJSON(matrix).os.runner }} + name: Test ${{ fromJSON(inputs.matrix).title }} + runs-on: ${{ fromJSON(inputs.matrix).os.runner }} env: - LOG_FILE_NAME: testresults.${{ fromJSON(matrix).os.runner }}.${{ fromJSON(matrix).dotnet.id != '' && fromJSON(matrix).dotnet.id || fromJSON(matrix).dotnet.sdk }}.${{ fromJSON(matrix).arch }}.trx + LOG_FILE_NAME: testresults.${{ fromJSON(inputs.matrix).os.runner }}.${{ fromJSON(inputs.matrix).dotnet.id != '' && fromJSON(inputs.matrix).dotnet.id || fromJSON(inputs.matrix).dotnet.sdk }}.${{ fromJSON(inputs.matrix).arch }}.trx steps: - name: Checkout uses: actions/checkout@v4 - if: ${{ fromJSON(matrix).dotnet.needsRestore }} + if: ${{ fromJSON(inputs.matrix).dotnet.needsRestore }} with: lfs: true submodules: recursive # Note: All of the SDKs we install have to be for the target architecture. Otherwise, we get issues when the default != the target. - name: Install global SDK - if: ${{ ! fromJSON(matrix).dotnet.needsRestore }} + if: ${{ ! fromJSON(inputs.matrix).dotnet.needsRestore }} uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 with: - architecture: ${{ fromJSON(matrix).arch }} + architecture: ${{ fromJSON(inputs.matrix).arch }} version: "8.0" - name: Cache restored NuGet packages uses: actions/cache@v4 - if: ${{ fromJSON(matrix).dotnet.needsRestore }} + if: ${{ fromJSON(inputs.matrix).dotnet.needsRestore }} with: path: ${{ env.NUGET_PACKAGES }} key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }} restore-keys: ${{ runner.os }}-nuget-v1- - name: Install restore SDK - if: ${{ fromJSON(matrix).dotnet.needsRestore }} + if: ${{ fromJSON(inputs.matrix).dotnet.needsRestore }} uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 with: - architecture: ${{ fromJSON(matrix).arch }} + architecture: ${{ fromJSON(inputs.matrix).arch }} global-json: global.json - name: Restore packages - if: ${{ fromJSON(matrix).dotnet.needsRestore }} + if: ${{ fromJSON(inputs.matrix).dotnet.needsRestore }} run: dotnet restore -noAutoRsp - name: Download test assets @@ -63,27 +63,27 @@ jobs: name: test-assets - name: Install test target runtime - if: ${{ fromJSON(matrix).dotnet.sdk != '' }} + if: ${{ fromJSON(inputs.matrix).dotnet.sdk != '' }} uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 with: - version: ${{ fromJSON(matrix).dotnet.sdk }} - architecture: ${{ fromJSON(matrix).arch }} + version: ${{ fromJSON(inputs.matrix).dotnet.sdk }} + architecture: ${{ fromJSON(inputs.matrix).arch }} runtime: dotnet - name: Print SDK info run: dotnet --info - name: Run tests - if: ${{ ! fromJSON(matrix).dotnet.isMono && ! fromJSON(matrix).dotnet.pgo }} - run: dotnet test -f ${{ fromJSON(matrix).dotnet.tfm }} -a ${{ fromJSON(matrix).arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ fromJSON(matrix).dotnet.tfm }}/MonoMod.UnitTest.dll + if: ${{ ! fromJSON(inputs.matrix).dotnet.isMono && ! fromJSON(inputs.matrix).dotnet.pgo }} + run: dotnet test -f ${{ fromJSON(inputs.matrix).dotnet.tfm }} -a ${{ fromJSON(inputs.matrix).arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.dll - name: Run tests (PGO) - if: ${{ fromJSON(matrix).dotnet.pgo }} + if: ${{ fromJSON(inputs.matrix).dotnet.pgo }} env: - DOTNET_ReadyToRun: ${{ fromJSON(matrix).usePgo && 0 || 1 }} - DOTNET_TC_QuicJitForLoops: ${{ fromJSON(matrix).usePgo && 1 || 0 }} - DOTNET_TieredPGO: ${{ fromJSON(matrix).usePgo && 1 || 0 }} - run: dotnet test -f ${{ fromJSON(matrix).dotnet.tfm }} -a ${{ fromJSON(matrix).arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ fromJSON(matrix).dotnet.tfm }}/MonoMod.UnitTest.dll + DOTNET_ReadyToRun: ${{ fromJSON(inputs.matrix).usePgo && 0 || 1 }} + DOTNET_TC_QuicJitForLoops: ${{ fromJSON(inputs.matrix).usePgo && 1 || 0 }} + DOTNET_TieredPGO: ${{ fromJSON(inputs.matrix).usePgo && 1 || 0 }} + run: dotnet test -f ${{ fromJSON(inputs.matrix).dotnet.tfm }} -a ${{ fromJSON(inputs.matrix).arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.dll # TODO: Mono? @@ -91,6 +91,6 @@ jobs: uses: actions/upload-artifact@v4 if: ${{ always() }} with: - name: test-results ${{ fromJSON(matrix).title }} + name: test-results ${{ fromJSON(inputs.matrix).title }} retention-days: 1 path: 'TestResults/*.trx' From 8868bdb4ce7998429e92f7e0aa1dd223b9f2f93c Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 18:04:40 -0500 Subject: [PATCH 067/133] Fixes 2 --- .github/workflows/build.yml | 4 ++-- .github/workflows/test.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 22f6009f..3d7270c3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,8 +37,8 @@ jobs: name: 'Build #${{ inputs.version }} (${{ inputs.osname }})' env: LOG_FILE_NAME: testresults.${{ inputs.os }}.auxtests.trx - VersionSuffix: ${{ !inputs.no-suffix && format('daily.{0}', inputs.version) || '' }} - DoNotAddSuffix: ${{ inputs.no-suffix && '1' || '' }} + VersionSuffix: ${{ !inputs.no-suffix && format('daily.{0}', inputs.version) }} + DoNotAddSuffix: ${{ inputs.no-suffix && '1' }} steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4afde55a..df4d23b8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -80,7 +80,7 @@ jobs: - name: Run tests (PGO) if: ${{ fromJSON(inputs.matrix).dotnet.pgo }} env: - DOTNET_ReadyToRun: ${{ fromJSON(inputs.matrix).usePgo && 0 || 1 }} + DOTNET_ReadyToRun: ${{ !fromJSON(inputs.matrix).usePgo && 1 || 0 }} DOTNET_TC_QuicJitForLoops: ${{ fromJSON(inputs.matrix).usePgo && 1 || 0 }} DOTNET_TieredPGO: ${{ fromJSON(inputs.matrix).usePgo && 1 || 0 }} run: dotnet test -f ${{ fromJSON(inputs.matrix).dotnet.tfm }} -a ${{ fromJSON(inputs.matrix).arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.dll From 4d9a14ce54a9bb5d75a0fb670262417f022c96a1 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 18:06:06 -0500 Subject: [PATCH 068/133] Fixes 3 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1292ae43..d91b7d5f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,8 +57,8 @@ jobs: os: ${{ matrix.os }} osname: ${{ matrix.name }} version: ${{ needs.compute-version.outputs.ver }} - upload-packages: ${{ matrix.upload-packages }} - upload-tests: ${{ matrix.upload-tests }} + upload-packages: ${{ matrix.upload-packages || false }} + upload-tests: ${{ matrix.upload-tests || false }} compute-test-matrix: name: Compute Test Matrix From 8c48e384bf7fcd1e26ede5c07d4f6e8d772baca5 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 20:13:34 -0500 Subject: [PATCH 069/133] Separate build-testassets (linux) from macos and windows builds --- .github/workflows/ci.yml | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d91b7d5f..ebc612ee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,28 +37,36 @@ jobs: - id: computever run: echo "ver=$(Get-Date -Format y.M.d).${{ github.run_number }}.${{ github.run_attempt }}" >> $env:GITHUB_OUTPUT + build-testassets: + needs: compute-version + name: 'Build #${{ needs.compute-version.outputs.ver }} (Linux)' + uses: ./.github/workflows/build.yml + with: + os: ubuntu-latest + osname: Linux + version: ${{ needs.compute-version.outputs.ver }} + upload-packages: true + upload-tests: true + build: needs: compute-version strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-13] + os: [windows-latest, macos-13] include: - - os: ubuntu-latest - name: Linux - upload-packages: true - upload-tests: true - os: windows-latest name: Windows - os: macos-13 name: MacOS + name: 'Build #${{ needs.compute-version.outputs.ver }} (${{ matrix.name }})' uses: ./.github/workflows/build.yml with: os: ${{ matrix.os }} osname: ${{ matrix.name }} version: ${{ needs.compute-version.outputs.ver }} - upload-packages: ${{ matrix.upload-packages || false }} - upload-tests: ${{ matrix.upload-tests || false }} + upload-packages: false + upload-tests: false compute-test-matrix: name: Compute Test Matrix @@ -76,10 +84,11 @@ jobs: run: ./.github/gen-test-matrix.ps1 -MatrixOutName matrix -GithubOutput $env:GITHUB_OUTPUT test: - needs: [compute-test-matrix, build] + needs: [compute-test-matrix, build-testassets] strategy: fail-fast: false matrix: ${{ fromJSON(needs.compute-test-matrix.outputs.matrix) }} uses: ./.github/workflows/test.yml + name: Test ${{ matrix.title }} with: matrix: ${{ toJSON(matrix) }} \ No newline at end of file From 036e45a6b2c9529ab36e1b4fb01ae1cddfbe51d5 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 22:35:44 -0500 Subject: [PATCH 070/133] Slightly rearrange jobs --- .github/workflows/ci.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ebc612ee..bd12fe91 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,6 +36,21 @@ jobs: steps: - id: computever run: echo "ver=$(Get-Date -Format y.M.d).${{ github.run_number }}.${{ github.run_attempt }}" >> $env:GITHUB_OUTPUT + + compute-test-matrix: + name: Compute Test Matrix + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.compute-matrix.outputs.matrix }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + lfs: false + submodules: false + - name: Compute test matrix + id: compute-matrix + run: ./.github/gen-test-matrix.ps1 -MatrixOutName matrix -GithubOutput $env:GITHUB_OUTPUT build-testassets: needs: compute-version @@ -67,21 +82,6 @@ jobs: version: ${{ needs.compute-version.outputs.ver }} upload-packages: false upload-tests: false - - compute-test-matrix: - name: Compute Test Matrix - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.compute-matrix.outputs.matrix }} - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - lfs: false - submodules: false - - name: Compute test matrix - id: compute-matrix - run: ./.github/gen-test-matrix.ps1 -MatrixOutName matrix -GithubOutput $env:GITHUB_OUTPUT test: needs: [compute-test-matrix, build-testassets] From 8c580cb4ed1b85d01923c239bfc8a609f78c5612 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 22:59:41 -0500 Subject: [PATCH 071/133] Always upload package artifacts, but change retention and name according to parameter --- .github/workflows/build.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3d7270c3..c2ce09b9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -98,10 +98,11 @@ jobs: - name: Archive packages uses: actions/upload-artifact@v4 - if: ${{ inputs.upload-packages }} + #if: ${{ inputs.upload-packages }} with: - name: packages + name: packages${{ !inputs.upload-packages && format(' {0}', runner.os) }} path: artifacts/package/release/*.nupkg + retention-days: ${{ !inputs.upload-packages && 7 }} - name: Upload test assets uses: actions/upload-artifact@v4 From dc05d20bc8d162dd2515da2d841ad365c2c3082b Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 23:12:17 -0500 Subject: [PATCH 072/133] Fix retention-days --- .github/workflows/build.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c2ce09b9..6b5244e2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -98,11 +98,18 @@ jobs: - name: Archive packages uses: actions/upload-artifact@v4 - #if: ${{ inputs.upload-packages }} + if: ${{ inputs.upload-packages }} with: - name: packages${{ !inputs.upload-packages && format(' {0}', runner.os) }} + name: packages path: artifacts/package/release/*.nupkg - retention-days: ${{ !inputs.upload-packages && 7 }} + + - name: Archive packages + uses: actions/upload-artifact@v4 + if: ${{ !inputs.upload-packages }} + with: + name: packages ${{ runner.os }} + path: artifacts/package/release/*.nupkg + retention-days: 7 - name: Upload test assets uses: actions/upload-artifact@v4 From 3d31c086d85d85aedcd2c8c6c3d8cb4e00d134c0 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 17 May 2024 23:56:44 -0500 Subject: [PATCH 073/133] Configure git before checkout --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6b5244e2..49f94c78 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,6 +40,10 @@ jobs: VersionSuffix: ${{ !inputs.no-suffix && format('daily.{0}', inputs.version) }} DoNotAddSuffix: ${{ inputs.no-suffix && '1' }} steps: + - name: Configure git + run: | + git config --global core.autocrlf input + - name: Checkout uses: actions/checkout@v4 with: From 307d58059cc6a84f349fa05778df8409d8a7cf01 Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 18 May 2024 05:22:54 -0500 Subject: [PATCH 074/133] Re-enable .NET Core 2.1 --- .github/gen-test-matrix.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/gen-test-matrix.ps1 b/.github/gen-test-matrix.ps1 index 4743e168..548f5f87 100644 --- a/.github/gen-test-matrix.ps1 +++ b/.github/gen-test-matrix.ps1 @@ -46,7 +46,7 @@ $dotnetVersions = @( isFramework = $true; }, [pscustomobject]@{ - enable = $false; # TODO: fix CI tests for .NET 2.1. For some reason it can't find the testhost (only on non-Linux) + #enable = $false; # TODO: fix CI tests for .NET 2.1. For some reason it can't find the testhost (only on non-Linux) # The application to execute does not exist: 'microsoft.testplatform.testhost/17.2.0\lib/netcoreapp2.1/testhost.dll' name = ".NET Core 2.1"; sdk = "2.1"; From d39d46a54fec455fa9890e800a3dce1585a921c8 Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 18 May 2024 05:38:15 -0500 Subject: [PATCH 075/133] Update Microsoft.NET.Test.Sdk --- src/MonoMod.UnitTest/MonoMod.UnitTest.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MonoMod.UnitTest/MonoMod.UnitTest.csproj b/src/MonoMod.UnitTest/MonoMod.UnitTest.csproj index d7930b3c..b5176f07 100644 --- a/src/MonoMod.UnitTest/MonoMod.UnitTest.csproj +++ b/src/MonoMod.UnitTest/MonoMod.UnitTest.csproj @@ -67,7 +67,7 @@ runtime; build; native; contentfiles; analyzers all - + From 0e5d9b029c5a2f1cf21f2e9d3b8ab5090e303a52 Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 18 May 2024 06:13:49 -0500 Subject: [PATCH 076/133] Add diag log generation to test runs --- .github/workflows/test.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index df4d23b8..fb07c403 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -75,7 +75,10 @@ jobs: - name: Run tests if: ${{ ! fromJSON(inputs.matrix).dotnet.isMono && ! fromJSON(inputs.matrix).dotnet.pgo }} - run: dotnet test -f ${{ fromJSON(inputs.matrix).dotnet.tfm }} -a ${{ fromJSON(inputs.matrix).arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.dll + run: | + dotnet test -f ${{ fromJSON(inputs.matrix).dotnet.tfm }} -a ${{ fromJSON(inputs.matrix).arch }} ` + -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.dll ` + -v diag -d diaglog.txt - name: Run tests (PGO) if: ${{ fromJSON(inputs.matrix).dotnet.pgo }} @@ -83,7 +86,10 @@ jobs: DOTNET_ReadyToRun: ${{ !fromJSON(inputs.matrix).usePgo && 1 || 0 }} DOTNET_TC_QuicJitForLoops: ${{ fromJSON(inputs.matrix).usePgo && 1 || 0 }} DOTNET_TieredPGO: ${{ fromJSON(inputs.matrix).usePgo && 1 || 0 }} - run: dotnet test -f ${{ fromJSON(inputs.matrix).dotnet.tfm }} -a ${{ fromJSON(inputs.matrix).arch }} -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.dll + run: | + dotnet test -f ${{ fromJSON(inputs.matrix).dotnet.tfm }} -a ${{ fromJSON(inputs.matrix).arch }} ` + -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.dll ` + -v diag -d diaglog.txt # TODO: Mono? @@ -93,4 +99,6 @@ jobs: with: name: test-results ${{ fromJSON(inputs.matrix).title }} retention-days: 1 - path: 'TestResults/*.trx' + path: | + TestResults/*.trx + diaglog.* From eb1f76b341629c5e447c09a752967b7475b09240 Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 18 May 2024 22:35:55 -0500 Subject: [PATCH 077/133] Tweak UnitTest (and test workflow) to (hopefully) make core 2.1 tests easier to get to run --- .github/gen-test-matrix.ps1 | 2 +- .github/workflows/test.yml | 7 +++---- global.json | 2 +- src/MonoMod.UnitTest/MonoMod.UnitTest.csproj | 21 ++++++++++++++------ src/MonoMod.UnitTest/Program.cs | 4 ++++ 5 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 src/MonoMod.UnitTest/Program.cs diff --git a/.github/gen-test-matrix.ps1 b/.github/gen-test-matrix.ps1 index 548f5f87..7884e891 100644 --- a/.github/gen-test-matrix.ps1 +++ b/.github/gen-test-matrix.ps1 @@ -41,7 +41,7 @@ $dotnetVersions = @( [pscustomobject]@{ name = ".NET Framework 4.x"; id = 'fx'; - tfm = "net46"; + tfm = "net462"; rids = @("win-x86","win-x64","win-arm64"); isFramework = $true; }, diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fb07c403..f1727ab4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,6 +22,7 @@ jobs: runs-on: ${{ fromJSON(inputs.matrix).os.runner }} env: LOG_FILE_NAME: testresults.${{ fromJSON(inputs.matrix).os.runner }}.${{ fromJSON(inputs.matrix).dotnet.id != '' && fromJSON(inputs.matrix).dotnet.id || fromJSON(inputs.matrix).dotnet.sdk }}.${{ fromJSON(inputs.matrix).arch }}.trx + steps: - name: Checkout uses: actions/checkout@v4 @@ -77,8 +78,7 @@ jobs: if: ${{ ! fromJSON(inputs.matrix).dotnet.isMono && ! fromJSON(inputs.matrix).dotnet.pgo }} run: | dotnet test -f ${{ fromJSON(inputs.matrix).dotnet.tfm }} -a ${{ fromJSON(inputs.matrix).arch }} ` - -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.dll ` - -v diag -d diaglog.txt + -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.dll - name: Run tests (PGO) if: ${{ fromJSON(inputs.matrix).dotnet.pgo }} @@ -88,8 +88,7 @@ jobs: DOTNET_TieredPGO: ${{ fromJSON(inputs.matrix).usePgo && 1 || 0 }} run: | dotnet test -f ${{ fromJSON(inputs.matrix).dotnet.tfm }} -a ${{ fromJSON(inputs.matrix).arch }} ` - -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.dll ` - -v diag -d diaglog.txt + -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.dll # TODO: Mono? diff --git a/global.json b/global.json index 54725b04..a2065e21 100644 --- a/global.json +++ b/global.json @@ -2,7 +2,7 @@ "sdk": { "allowPrerelease": true, "rollForward": "latestMinor", - "version": "8.0.100" + "version": "8.0.203" }, "msbuild-sdks": { "Microsoft.Build.NoTargets": "3.7.56", diff --git a/src/MonoMod.UnitTest/MonoMod.UnitTest.csproj b/src/MonoMod.UnitTest/MonoMod.UnitTest.csproj index b5176f07..46e45cc3 100644 --- a/src/MonoMod.UnitTest/MonoMod.UnitTest.csproj +++ b/src/MonoMod.UnitTest/MonoMod.UnitTest.csproj @@ -12,17 +12,21 @@ $(NoWarn);CA1852 - net6.0;net5.0;net7.0;net8.0;net46;netcoreapp2.1;netcoreapp3.0;netcoreapp3.1 - Library + net6.0;net5.0;net7.0;net8.0;net462;netcoreapp2.1;netcoreapp3.0;netcoreapp3.1 skip annotations false + Exe + false + true + true + 2.4.2 $(XunitVersion) - [2.*,2.4.3] + [2.*,2.4.3] @@ -33,11 +37,13 @@ + @@ -51,11 +57,11 @@ - + @@ -67,8 +73,11 @@ runtime; build; native; contentfiles; analyzers all - + + + + \ No newline at end of file diff --git a/src/MonoMod.UnitTest/Program.cs b/src/MonoMod.UnitTest/Program.cs new file mode 100644 index 00000000..98cf5681 --- /dev/null +++ b/src/MonoMod.UnitTest/Program.cs @@ -0,0 +1,4 @@ +// This file has been auto generated. +using System; +[Microsoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode] +class AutoGeneratedProgram { static void Main(string[] args) { } } \ No newline at end of file From 8249e81c35027d387af8292e4a1fc373ebf859bb Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 18 May 2024 22:50:34 -0500 Subject: [PATCH 078/133] Attempt to fix .NET Core test resolution issues by fixing runtimeconfig --- .github/workflows/build.yml | 2 +- .github/workflows/test.yml | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 49f94c78..7133bd23 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -81,7 +81,7 @@ jobs: # work. We may also want to do a hash check on the packages, though I have no idea how reliable that would be. - name: Run auxiliary tests - run: dotnet test --no-build -c Release -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" -f "FullyQualifiedName!~MonoMod.UnitTest" + run: dotnet test --no-build -c Release -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" --filter "FullyQualifiedName!~MonoMod.UnitTest" - name: Upload test results uses: actions/upload-artifact@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f1727ab4..eafa12e7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,7 @@ env: jobs: test: - name: Test ${{ fromJSON(inputs.matrix).title }} + name: Test runs-on: ${{ fromJSON(inputs.matrix).os.runner }} env: LOG_FILE_NAME: testresults.${{ fromJSON(inputs.matrix).os.runner }}.${{ fromJSON(inputs.matrix).dotnet.id != '' && fromJSON(inputs.matrix).dotnet.id || fromJSON(inputs.matrix).dotnet.sdk }}.${{ fromJSON(inputs.matrix).arch }}.trx @@ -73,6 +73,11 @@ jobs: - name: Print SDK info run: dotnet --info + + - name: Fix runtimeconfig.json probing paths + if: ${{ fromJSON(inputs.matrix).dotnet.needsRestore }} + run: | + ConvertTo-Json @{runtimeOptions=@{additionalProbingPaths=@($env:NUGET_PACKAGES)}} > release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.runtimeconfig.dev.json - name: Run tests if: ${{ ! fromJSON(inputs.matrix).dotnet.isMono && ! fromJSON(inputs.matrix).dotnet.pgo }} From 9f2f8d4d55f8cf601a994057d4f1023b7e403ce7 Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 18 May 2024 23:04:27 -0500 Subject: [PATCH 079/133] Remove aux test run, which was always broken (but somehow didn't cause problems before) --- .github/workflows/build.yml | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7133bd23..f208cd79 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,7 +34,7 @@ env: jobs: build: runs-on: ${{ inputs.os }} - name: 'Build #${{ inputs.version }} (${{ inputs.osname }})' + name: Build env: LOG_FILE_NAME: testresults.${{ inputs.os }}.auxtests.trx VersionSuffix: ${{ !inputs.no-suffix && format('daily.{0}', inputs.version) }} @@ -51,11 +51,7 @@ jobs: submodules: recursive # TODO: maybe we can eventually use package locks for package caching? - #- name: Install .NET SDK - # uses: actions/setup-dotnet@v4 - # with: - # global-json-file: global.json - + - name: Install .NET SDK uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 with: @@ -80,17 +76,18 @@ jobs: # TODO: it might be worth trying to do a "smoketest" test run with the installed tfm to broadly make sure that all builds # work. We may also want to do a hash check on the packages, though I have no idea how reliable that would be. - - name: Run auxiliary tests - run: dotnet test --no-build -c Release -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" --filter "FullyQualifiedName!~MonoMod.UnitTest" - - - name: Upload test results - uses: actions/upload-artifact@v4 - if: ${{ always() }} - with: - name: test-results aux ${{ runner.os }} - retention-days: 1 - path: 'TestResults/*.trx' - if-no-files-found: ignore + # TODO: If/when we add other test projects aside from MonoMod.UnitTest, we should run tests here + #- name: Run auxiliary tests + # run: dotnet test --no-build -c Release -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" --filter "FullyQualifiedName!~MonoMod.UnitTest" + # + #- name: Upload test results + # uses: actions/upload-artifact@v4 + # if: ${{ always() }} + # with: + # name: test-results aux ${{ runner.os }} + # retention-days: 1 + # path: 'TestResults/*.trx' + # if-no-files-found: ignore - name: Upload binlogs uses: actions/upload-artifact@v4 From 92be6ec4cfa1265320ff1106503b3dd5880c83dc Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 18 May 2024 23:14:16 -0500 Subject: [PATCH 080/133] Try out a different test reporter action --- .github/workflows/test_results.yml | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index f64f4119..b2565040 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -37,13 +37,25 @@ jobs: pattern: test-results * merge-multiple: false - - name: Publish Test Results - uses: EnricoMi/publish-unit-test-result-action@v2 - with: - commit: ${{ github.event.workflow_run.head_sha }} - event_file: event.json - event_name: ${{ github.event.workflow_run.event }} + #- name: Publish Test Results + # uses: EnricoMi/publish-unit-test-result-action@v2 + # with: + # commit: ${{ github.event.workflow_run.head_sha }} + # event_file: event.json + # event_name: ${{ github.event.workflow_run.event }} + # + # files: '**/*.trx' + # comment_mode: ${{ (github.event.workflow_run.event == 'pull_request' || github.event_name == 'pull_request') && 'failures' || 'off' }} + # report_individual_runs: true - files: '**/*.trx' - comment_mode: ${{ (github.event.workflow_run.event == 'pull_request' || github.event_name == 'pull_request') && 'failures' || 'off' }} - report_individual_runs: true + - name: Test Reporting + uses: phoenix-actions/test-reporting@v9 + with: + name: Test Report + path: '**/*.trx' + reporter: dotnet-trx + list-suites: failed + list-tests: failed + fail-on-error: false + output-to: step-summary + From 3209c0a3c4fa5e6be307aa3ff73677db6c1d9e77 Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 18 May 2024 23:28:42 -0500 Subject: [PATCH 081/133] Revert some changes to UnitTest to fix .NET Framework tests --- src/MonoMod.UnitTest/MonoMod.UnitTest.csproj | 5 +---- src/MonoMod.UnitTest/Program.cs | 4 ---- 2 files changed, 1 insertion(+), 8 deletions(-) delete mode 100644 src/MonoMod.UnitTest/Program.cs diff --git a/src/MonoMod.UnitTest/MonoMod.UnitTest.csproj b/src/MonoMod.UnitTest/MonoMod.UnitTest.csproj index 46e45cc3..473eebd8 100644 --- a/src/MonoMod.UnitTest/MonoMod.UnitTest.csproj +++ b/src/MonoMod.UnitTest/MonoMod.UnitTest.csproj @@ -18,10 +18,7 @@ false - Exe - false - true - true + Library 2.4.2 $(XunitVersion) diff --git a/src/MonoMod.UnitTest/Program.cs b/src/MonoMod.UnitTest/Program.cs deleted file mode 100644 index 98cf5681..00000000 --- a/src/MonoMod.UnitTest/Program.cs +++ /dev/null @@ -1,4 +0,0 @@ -// This file has been auto generated. -using System; -[Microsoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode] -class AutoGeneratedProgram { static void Main(string[] args) { } } \ No newline at end of file From 34a1f295303a41d472383073da41ff3a1ef4ab9e Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 18 May 2024 23:41:13 -0500 Subject: [PATCH 082/133] Revert to other test report publisher --- .github/workflows/test_results.yml | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index b2565040..48ad770c 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -37,25 +37,13 @@ jobs: pattern: test-results * merge-multiple: false - #- name: Publish Test Results - # uses: EnricoMi/publish-unit-test-result-action@v2 - # with: - # commit: ${{ github.event.workflow_run.head_sha }} - # event_file: event.json - # event_name: ${{ github.event.workflow_run.event }} - # - # files: '**/*.trx' - # comment_mode: ${{ (github.event.workflow_run.event == 'pull_request' || github.event_name == 'pull_request') && 'failures' || 'off' }} - # report_individual_runs: true - - - name: Test Reporting - uses: phoenix-actions/test-reporting@v9 + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 with: - name: Test Report - path: '**/*.trx' - reporter: dotnet-trx - list-suites: failed - list-tests: failed - fail-on-error: false - output-to: step-summary - + commit: ${{ github.event.workflow_run.head_sha }} + event_file: event.json + event_name: ${{ github.event.workflow_run.event }} + + files: '**/*.trx' + comment_mode: ${{ (github.event.workflow_run.event == 'pull_request' || github.event_name == 'pull_request') && 'failures' || 'off' }} + report_individual_runs: true From dd6e69c11bace8f25ab339c4ac19671900f3d5f0 Mon Sep 17 00:00:00 2001 From: DaNike Date: Tue, 21 May 2024 04:16:59 -0500 Subject: [PATCH 083/133] Try using my own test result publisher --- .github/workflows/test_results.yml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index 48ad770c..2d599a18 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -37,13 +37,25 @@ jobs: pattern: test-results * merge-multiple: false + #- name: Publish Test Results + # uses: EnricoMi/publish-unit-test-result-action@v2 + # with: + # commit: ${{ github.event.workflow_run.head_sha }} + # event_file: event.json + # event_name: ${{ github.event.workflow_run.event }} + # + # files: '**/*.trx' + # comment_mode: ${{ (github.event.workflow_run.event == 'pull_request' || github.event_name == 'pull_request') && 'failures' || 'off' }} + # report_individual_runs: true + # compare_to_earlier_commit: false + - name: Publish Test Results - uses: EnricoMi/publish-unit-test-result-action@v2 + uses: nike4613/actions-test-results@c19f2dc1cbe80353424205ecac808c526c8c1ba1 with: commit: ${{ github.event.workflow_run.head_sha }} event_file: event.json event_name: ${{ github.event.workflow_run.event }} files: '**/*.trx' - comment_mode: ${{ (github.event.workflow_run.event == 'pull_request' || github.event_name == 'pull_request') && 'failures' || 'off' }} - report_individual_runs: true + comment_mode: failures + comment_on_commit: true From bbd31aaf53181c1acad1556c5b7a17a40fea4973 Mon Sep 17 00:00:00 2001 From: DaNike Date: Tue, 21 May 2024 04:21:12 -0500 Subject: [PATCH 084/133] Update test_results --- .github/workflows/test_results.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index 2d599a18..c28905a6 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -50,7 +50,7 @@ jobs: # compare_to_earlier_commit: false - name: Publish Test Results - uses: nike4613/actions-test-results@c19f2dc1cbe80353424205ecac808c526c8c1ba1 + uses: nike4613/actions-test-results@1cdf9825abb56cfcd33153b9cc13e95840752f5a with: commit: ${{ github.event.workflow_run.head_sha }} event_file: event.json From 1240ea89586f6d77e2f3470e2db61ff710c0eae6 Mon Sep 17 00:00:00 2001 From: DaNike Date: Tue, 21 May 2024 04:24:37 -0500 Subject: [PATCH 085/133] Prevent publish-test-results from running when conclusion is skipped --- .github/workflows/test_results.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index c28905a6..5b21708f 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -9,7 +9,7 @@ permissions: {} jobs: publish-test-results: - if: github.event.workflow_run.conclusion != 'skipped' + if: github.event.workflow_run.conclusion != 'skipped' && github.event.workflow_run.conclusion != 'cancelled' runs-on: ubuntu-latest name: Publish test results From d6cce799119d524bb9491684a1a434f4ad06970f Mon Sep 17 00:00:00 2001 From: DaNike Date: Tue, 21 May 2024 05:16:05 -0500 Subject: [PATCH 086/133] Update test_results --- .github/workflows/test_results.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index 5b21708f..5c61f631 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -50,7 +50,7 @@ jobs: # compare_to_earlier_commit: false - name: Publish Test Results - uses: nike4613/actions-test-results@1cdf9825abb56cfcd33153b9cc13e95840752f5a + uses: nike4613/actions-test-results@37fbd04533e6e96f23a6958cdf7e6693554e8ea9 with: commit: ${{ github.event.workflow_run.head_sha }} event_file: event.json From d4b68131e17508834abc8d6e8c489888ca5fde6b Mon Sep 17 00:00:00 2001 From: DaNike Date: Tue, 21 May 2024 05:32:58 -0500 Subject: [PATCH 087/133] Add variant of action which always uses latest container build (dangerous) --- .github/workflows/test_results.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index 5c61f631..21cf8031 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -50,7 +50,7 @@ jobs: # compare_to_earlier_commit: false - name: Publish Test Results - uses: nike4613/actions-test-results@37fbd04533e6e96f23a6958cdf7e6693554e8ea9 + uses: nike4613/actions-test-results/master@369bd6cd6e58598b107eddc931613bba4c95e43b with: commit: ${{ github.event.workflow_run.head_sha }} event_file: event.json From dea64c06c2a8312567e84481f56a1b9e8c37f458 Mon Sep 17 00:00:00 2001 From: DaNike Date: Tue, 21 May 2024 05:48:04 -0500 Subject: [PATCH 088/133] Change test-results action to target master branch (since it effectively is anyway) --- .github/workflows/test_results.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index 21cf8031..ffefd43a 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -50,7 +50,7 @@ jobs: # compare_to_earlier_commit: false - name: Publish Test Results - uses: nike4613/actions-test-results/master@369bd6cd6e58598b107eddc931613bba4c95e43b + uses: nike4613/actions-test-results/master@master with: commit: ${{ github.event.workflow_run.head_sha }} event_file: event.json From 6b6cf3c4efb5783fed8ed5849ea8c3c9e2d2898a Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 22 May 2024 00:17:24 -0500 Subject: [PATCH 089/133] Switch test results to v1 --- .github/workflows/test_results.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index ffefd43a..7fdf36ae 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -50,7 +50,7 @@ jobs: # compare_to_earlier_commit: false - name: Publish Test Results - uses: nike4613/actions-test-results/master@master + uses: nike4613/actions-test-results@v1 with: commit: ${{ github.event.workflow_run.head_sha }} event_file: event.json From fe5574a96b4464ac483c1f7797a812d77d145afd Mon Sep 17 00:00:00 2001 From: Chicken-Bones Date: Tue, 28 May 2024 12:29:41 +1000 Subject: [PATCH 090/133] Improve windbg-memlog with file dump and use instructions --- tools/windbg-memlog.js | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/tools/windbg-memlog.js b/tools/windbg-memlog.js index 81e469fe..f389e8d4 100644 --- a/tools/windbg-memlog.js +++ b/tools/windbg-memlog.js @@ -1,4 +1,11 @@ -"use strict"; +"use strict"; + +// To dump the memory log to a .tmp file +// .scriptrun +// +// To get navigable version of the log +// .scriptload // +// dx -g @$scriptContents.GetMMLog() function initializeScript() { @@ -11,6 +18,29 @@ function initializeScript() return [new host.apiVersionSupport(1, 7)]; } +function invokeScript() +{ + var log = GetMMLog(); + + var dbgOutput = host.diagnostics.debugLog; + + var file = host.namespace.Debugger.Utility.FileSystem.CreateTempFile(); + var textWriter = host.namespace.Debugger.Utility.FileSystem.CreateTextWriter(file); + + dbgOutput("Dumping Memory log to tmp file\n"); + + for (const e of log) { + if (e.Index > 0 && e.Index % 1000 == 0) + dbgOutput(e.Index + "...\n"); + + textWriter.WriteLine("[" + e.Time + "] [" + e.Source + "] " + e.Level + ": " + e.Message); + } + + file.Close(); + + dbgOutput("Dumped MonoMod Log to: " + file.Path); +} + function GetMMLog() { var ctl = host.namespace.Debugger.Utility.Control; @@ -235,5 +265,5 @@ function formatDateTime(dt) { var date = isUtc ? new Date(Date.UTC(year, month - 1, day, hours, minutes, seconds, millis)) : new Date(year, month - 1, day, hours, minutes, seconds, millis); - return date.toLocaleString(); + return date.toLocaleString().replace(/[^\w:/\\ ]/g, ""); } \ No newline at end of file From 16f860b19175e50a99b783d42199e7a6d831644a Mon Sep 17 00:00:00 2001 From: DaNike Date: Thu, 30 May 2024 22:02:31 -0500 Subject: [PATCH 091/133] Run unit tests on system Mono, when available --- .github/gen-test-matrix.ps1 | 45 ++++++++++++++++++++++++++++-- .github/workflows/test.yml | 18 +++++++++--- .github/workflows/test_results.yml | 6 ++-- 3 files changed, 60 insertions(+), 9 deletions(-) diff --git a/.github/gen-test-matrix.ps1 b/.github/gen-test-matrix.ps1 index 7884e891..93e478f2 100644 --- a/.github/gen-test-matrix.ps1 +++ b/.github/gen-test-matrix.ps1 @@ -12,6 +12,8 @@ $operatingSystems = @( ridname = "win"; arch = @("x86","x64"); # while .NET Framework supports Arm64, GitHub doesn't provide Arm windows runners hasFramework = $true; + monoArch = @("win32", "win64", "win_arm64"); + monoDll = "mono-2.0-bdwgc.dll"; }, [pscustomobject]@{ name = "Linux"; @@ -19,6 +21,8 @@ $operatingSystems = @( ridname = "linux"; arch = @("x64"); hasMono = $true; + monoArch = @("linux64"); + monoDll = "limonobdwgc-2.0.so"; # TODO }, [pscustomobject]@{ name = "MacOS 13"; @@ -26,6 +30,8 @@ $operatingSystems = @( ridname = "osx"; arch = @("x64"); hasMono = $true; + monoArch = @("macos_x64"); + monoDll = "limonobdwgc-2.0.dylib"; }, [pscustomobject]@{ #enable = $false; @@ -34,6 +40,8 @@ $operatingSystems = @( ridname = "osx"; arch = @("x64"<#, "arm64"#>); # x64 comes from Rosetta, and we disable arm64 mode for now because we don't support it yet hasMono = $true; + monoArch = @("macos_x64", "macos_arm64"); + monoDll = "limonobdwgc-2.0.dylib"; } ); @@ -46,8 +54,6 @@ $dotnetVersions = @( isFramework = $true; }, [pscustomobject]@{ - #enable = $false; # TODO: fix CI tests for .NET 2.1. For some reason it can't find the testhost (only on non-Linux) - # The application to execute does not exist: 'microsoft.testplatform.testhost/17.2.0\lib/netcoreapp2.1/testhost.dll' name = ".NET Core 2.1"; sdk = "2.1"; tfm = "netcoreapp2.1"; @@ -95,12 +101,24 @@ $dotnetVersions = @( } ); +$monoTfm = "net462"; + +$monoVersions = @( + <# + [pscustomobject]@{ + name = "Unity Mono 6000.0.2"; + unityVersion = "6000.0.2"; + monoName = "MonoBleedingEdge"; + } + #> +); + $jobs = @(); foreach ($os in $operatingSystems) { if ($os.enable -eq $false) { continue; } - $outos = $os | Select-Object -ExcludeProperty arch,ridname,hasFramework,hasMono + $outos = $os | Select-Object -ExcludeProperty arch,ridname,hasFramework,hasMono,monoArch,monoDll foreach ($arch in $os.arch) { @@ -158,6 +176,27 @@ foreach ($os in $operatingSystems) ); } } + + if ($os.hasMono) + { + # this OS has a system mono, emit a job for that + $jobs += @( + [pscustomobject]@{ + title = "System Mono"; + os = $outos; + dotnet = [pscustomobject]@{ + name = "Mono"; + needsRestore = $true; # Monos always need restore + isMono = $true; + systemMono = $true; + tfm = $monoTfm; + }; + arch = $arch; + } + ); + } + + # TODO: non-system mono } } diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eafa12e7..6eabdf53 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,13 +15,14 @@ env: DOTNET_TELEMETRY_OPTOUT: true DOTNET_NOLOGO: true NUGET_PACKAGES: ${{github.workspace}}/artifacts/pkg + XunitVersion: "2.4.2" jobs: test: name: Test runs-on: ${{ fromJSON(inputs.matrix).os.runner }} env: - LOG_FILE_NAME: testresults.${{ fromJSON(inputs.matrix).os.runner }}.${{ fromJSON(inputs.matrix).dotnet.id != '' && fromJSON(inputs.matrix).dotnet.id || fromJSON(inputs.matrix).dotnet.sdk }}.${{ fromJSON(inputs.matrix).arch }}.trx + LOG_FILE_NAME: testresults.${{ fromJSON(inputs.matrix).os.runner }}.${{ fromJSON(inputs.matrix).dotnet.id != '' && fromJSON(inputs.matrix).dotnet.id || fromJSON(inputs.matrix).dotnet.sdk }}.${{ fromJSON(inputs.matrix).arch }} steps: - name: Checkout @@ -83,7 +84,7 @@ jobs: if: ${{ ! fromJSON(inputs.matrix).dotnet.isMono && ! fromJSON(inputs.matrix).dotnet.pgo }} run: | dotnet test -f ${{ fromJSON(inputs.matrix).dotnet.tfm }} -a ${{ fromJSON(inputs.matrix).arch }} ` - -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.dll + -l:"trx;LogFileName=$($env:LOG_FILE_NAME).trx" release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.dll - name: Run tests (PGO) if: ${{ fromJSON(inputs.matrix).dotnet.pgo }} @@ -93,9 +94,17 @@ jobs: DOTNET_TieredPGO: ${{ fromJSON(inputs.matrix).usePgo && 1 || 0 }} run: | dotnet test -f ${{ fromJSON(inputs.matrix).dotnet.tfm }} -a ${{ fromJSON(inputs.matrix).arch }} ` - -l:"trx;LogFileName=$($env:LOG_FILE_NAME)" release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.dll + -l:"trx;LogFileName=$($env:LOG_FILE_NAME).trx" release_${{ fromJSON(inputs.matrix).dotnet.tfm }}/MonoMod.UnitTest.dll + + - name: Run tests (Mono) + if: ${{ fromJSON(inputs.matrix).dotnet.isMono && fromJSON(inputs.matrix).dotnet.systemMono }} + env: + TFM: ${{ fromJSON(inputs.matrix).dotnet.tfm }} + run: | + mono "$($env:NUGET_PACKAGES)/xunit.runner.console/$($env:XunitVersion)/tools/$($env:TFM)/xunit.console.exe" ` + "release_$($env:TFM)/MonoMod.UnitTest.dll" -junit "$($env:LOG_FILE_NAME).xml" - # TODO: Mono? + # TODO: Non-system Mono - name: Upload test results uses: actions/upload-artifact@v4 @@ -105,4 +114,5 @@ jobs: retention-days: 1 path: | TestResults/*.trx + *.xml diaglog.* diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index 7fdf36ae..4278ca3d 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -50,12 +50,14 @@ jobs: # compare_to_earlier_commit: false - name: Publish Test Results - uses: nike4613/actions-test-results@v1 + uses: nike4613/actions-test-results/master@master with: commit: ${{ github.event.workflow_run.head_sha }} event_file: event.json event_name: ${{ github.event.workflow_run.event }} - files: '**/*.trx' + files: | + **/*.trx + **/*.xml comment_mode: failures comment_on_commit: true From ab9b31e484171bbb3092afaf63633ddadde3dd9a Mon Sep 17 00:00:00 2001 From: DaNike Date: Thu, 30 May 2024 22:04:35 -0500 Subject: [PATCH 092/133] Ensure that system Mono tests have a runtime ID defined --- .github/gen-test-matrix.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/gen-test-matrix.ps1 b/.github/gen-test-matrix.ps1 index 93e478f2..077c8469 100644 --- a/.github/gen-test-matrix.ps1 +++ b/.github/gen-test-matrix.ps1 @@ -186,6 +186,7 @@ foreach ($os in $operatingSystems) os = $outos; dotnet = [pscustomobject]@{ name = "Mono"; + id = "sysmono"; needsRestore = $true; # Monos always need restore isMono = $true; systemMono = $true; From 357f1974ef77111dd79ca1e53730797681ff5650 Mon Sep 17 00:00:00 2001 From: DaNike Date: Thu, 30 May 2024 22:15:05 -0500 Subject: [PATCH 093/133] Include OS in system mono test run title --- .github/gen-test-matrix.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/gen-test-matrix.ps1 b/.github/gen-test-matrix.ps1 index 077c8469..76789234 100644 --- a/.github/gen-test-matrix.ps1 +++ b/.github/gen-test-matrix.ps1 @@ -182,7 +182,7 @@ foreach ($os in $operatingSystems) # this OS has a system mono, emit a job for that $jobs += @( [pscustomobject]@{ - title = "System Mono"; + title = "System Mono on $($os.name)"; os = $outos; dotnet = [pscustomobject]@{ name = "Mono"; From 25cfb3f7fdd95fb122d51a300d26dd5e45c9337c Mon Sep 17 00:00:00 2001 From: DaNike Date: Thu, 30 May 2024 22:21:28 -0500 Subject: [PATCH 094/133] Only generate 1 System Mono job per OS (for the target arch of the OS) --- .github/gen-test-matrix.ps1 | 46 ++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/.github/gen-test-matrix.ps1 b/.github/gen-test-matrix.ps1 index 76789234..10796af4 100644 --- a/.github/gen-test-matrix.ps1 +++ b/.github/gen-test-matrix.ps1 @@ -11,6 +11,7 @@ $operatingSystems = @( runner = "windows-latest"; ridname = "win"; arch = @("x86","x64"); # while .NET Framework supports Arm64, GitHub doesn't provide Arm windows runners + runnerArch = 1; hasFramework = $true; monoArch = @("win32", "win64", "win_arm64"); monoDll = "mono-2.0-bdwgc.dll"; @@ -20,6 +21,7 @@ $operatingSystems = @( runner = "ubuntu-latest"; ridname = "linux"; arch = @("x64"); + runnerArch = 0; hasMono = $true; monoArch = @("linux64"); monoDll = "limonobdwgc-2.0.so"; # TODO @@ -29,6 +31,7 @@ $operatingSystems = @( runner = "macos-13"; ridname = "osx"; arch = @("x64"); + runnerArch = 0; hasMono = $true; monoArch = @("macos_x64"); monoDll = "limonobdwgc-2.0.dylib"; @@ -39,6 +42,7 @@ $operatingSystems = @( runner = "macos-14"; ridname = "osx"; arch = @("x64"<#, "arm64"#>); # x64 comes from Rosetta, and we disable arm64 mode for now because we don't support it yet + runnerArch = 1; hasMono = $true; monoArch = @("macos_x64", "macos_arm64"); monoDll = "limonobdwgc-2.0.dylib"; @@ -118,7 +122,27 @@ $jobs = @(); foreach ($os in $operatingSystems) { if ($os.enable -eq $false) { continue; } - $outos = $os | Select-Object -ExcludeProperty arch,ridname,hasFramework,hasMono,monoArch,monoDll + $outos = $os | Select-Object -ExcludeProperty arch,ridname,hasFramework,hasMono,monoArch,monoDll,runnerArch + + if ($os.hasMono && $os.runnerArch < $os.arch.Length) + { + # this OS has a system mono, emit a job for that + $jobs += @( + [pscustomobject]@{ + title = "System Mono on $($os.name)"; + os = $outos; + dotnet = [pscustomobject]@{ + name = "Mono"; + id = "sysmono"; + needsRestore = $true; # Monos always need restore + isMono = $true; + systemMono = $true; + tfm = $monoTfm; + }; + arch = $os.arch[$os.runnerArch]; + } + ); + } foreach ($arch in $os.arch) { @@ -177,26 +201,6 @@ foreach ($os in $operatingSystems) } } - if ($os.hasMono) - { - # this OS has a system mono, emit a job for that - $jobs += @( - [pscustomobject]@{ - title = "System Mono on $($os.name)"; - os = $outos; - dotnet = [pscustomobject]@{ - name = "Mono"; - id = "sysmono"; - needsRestore = $true; # Monos always need restore - isMono = $true; - systemMono = $true; - tfm = $monoTfm; - }; - arch = $arch; - } - ); - } - # TODO: non-system mono } } From 8a66a48f3e09ff9721cd7a637cdc844ed8340a14 Mon Sep 17 00:00:00 2001 From: DaNike Date: Thu, 30 May 2024 22:26:38 -0500 Subject: [PATCH 095/133] Fix powershell mistake --- .github/gen-test-matrix.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/gen-test-matrix.ps1 b/.github/gen-test-matrix.ps1 index 10796af4..d0dcbc4e 100644 --- a/.github/gen-test-matrix.ps1 +++ b/.github/gen-test-matrix.ps1 @@ -124,7 +124,7 @@ foreach ($os in $operatingSystems) if ($os.enable -eq $false) { continue; } $outos = $os | Select-Object -ExcludeProperty arch,ridname,hasFramework,hasMono,monoArch,monoDll,runnerArch - if ($os.hasMono && $os.runnerArch < $os.arch.Length) + if ($os.hasMono && $os.runnerArch -lt $os.arch.Length) { # this OS has a system mono, emit a job for that $jobs += @( From 06beca637853cffcbcaeeefb7a9b707a73f74c8a Mon Sep 17 00:00:00 2001 From: DaNike Date: Thu, 30 May 2024 22:40:56 -0500 Subject: [PATCH 096/133] Fix conditional in test matrix generation --- .github/gen-test-matrix.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/gen-test-matrix.ps1 b/.github/gen-test-matrix.ps1 index d0dcbc4e..6b10552f 100644 --- a/.github/gen-test-matrix.ps1 +++ b/.github/gen-test-matrix.ps1 @@ -124,7 +124,7 @@ foreach ($os in $operatingSystems) if ($os.enable -eq $false) { continue; } $outos = $os | Select-Object -ExcludeProperty arch,ridname,hasFramework,hasMono,monoArch,monoDll,runnerArch - if ($os.hasMono && $os.runnerArch -lt $os.arch.Length) + if ($os.hasMono -and $os.runnerArch -lt $os.arch.Length) { # this OS has a system mono, emit a job for that $jobs += @( From 9d4ac312ed2f3fa16e404de34147e0d17e1c8b99 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 31 May 2024 00:37:43 -0500 Subject: [PATCH 097/133] Switch to actions-test-results@v2 --- .github/gen-test-matrix.ps1 | 418 ++++++++++++++--------------- .github/workflows/test_results.yml | 2 +- 2 files changed, 210 insertions(+), 210 deletions(-) diff --git a/.github/gen-test-matrix.ps1 b/.github/gen-test-matrix.ps1 index 6b10552f..5509425b 100644 --- a/.github/gen-test-matrix.ps1 +++ b/.github/gen-test-matrix.ps1 @@ -1,216 +1,216 @@ -param ( - [string[]] $MatrixOutName, - [string] $GithubOutput -) - -$ErrorActionPreference = "Stop"; - -$operatingSystems = @( - [pscustomobject]@{ - name = "Windows"; - runner = "windows-latest"; - ridname = "win"; - arch = @("x86","x64"); # while .NET Framework supports Arm64, GitHub doesn't provide Arm windows runners - runnerArch = 1; - hasFramework = $true; - monoArch = @("win32", "win64", "win_arm64"); - monoDll = "mono-2.0-bdwgc.dll"; - }, - [pscustomobject]@{ - name = "Linux"; - runner = "ubuntu-latest"; - ridname = "linux"; - arch = @("x64"); - runnerArch = 0; - hasMono = $true; - monoArch = @("linux64"); - monoDll = "limonobdwgc-2.0.so"; # TODO - }, - [pscustomobject]@{ - name = "MacOS 13"; - runner = "macos-13"; - ridname = "osx"; - arch = @("x64"); - runnerArch = 0; - hasMono = $true; - monoArch = @("macos_x64"); - monoDll = "limonobdwgc-2.0.dylib"; - }, - [pscustomobject]@{ - #enable = $false; - name = "MacOS 14 (M1)"; - runner = "macos-14"; - ridname = "osx"; - arch = @("x64"<#, "arm64"#>); # x64 comes from Rosetta, and we disable arm64 mode for now because we don't support it yet - runnerArch = 1; - hasMono = $true; - monoArch = @("macos_x64", "macos_arm64"); - monoDll = "limonobdwgc-2.0.dylib"; - } -); - -$dotnetVersions = @( - [pscustomobject]@{ - name = ".NET Framework 4.x"; - id = 'fx'; - tfm = "net462"; - rids = @("win-x86","win-x64","win-arm64"); - isFramework = $true; - }, - [pscustomobject]@{ - name = ".NET Core 2.1"; - sdk = "2.1"; - tfm = "netcoreapp2.1"; - rids = @("win-x86","win-x64","linux-x64","osx-x64"); - needsRestore = $true; - }, - [pscustomobject]@{ - name = ".NET Core 3.0"; - sdk = "3.0"; - tfm = "netcoreapp3.0"; - rids = @("win-x86","win-x64","linux-x64","osx-x64"); - }, - [pscustomobject]@{ - name = ".NET Core 3.1"; - sdk = "3.1"; - tfm = "netcoreapp3.1"; - rids = @("win-x86","win-x64","linux-x64","osx-x64"); - }, - [pscustomobject]@{ - name = ".NET 5.0"; - sdk = "5.0"; - tfm = "net5.0"; - rids = @("win-x86","win-x64","linux-x64","osx-x64"); - }, - [pscustomobject]@{ - name = ".NET 6.0"; - sdk = "6.0"; - tfm = "net6.0"; - rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64"); - pgo = $true; - }, - [pscustomobject]@{ - name = ".NET 7.0"; - sdk = "7.0"; - tfm = "net7.0"; - rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64"); - pgo = $true; - }, - [pscustomobject]@{ - name = ".NET 8.0"; - sdk = "8.0"; - tfm = "net8.0"; - rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64"); - pgo = $true; - } -); - -$monoTfm = "net462"; - -$monoVersions = @( - <# - [pscustomobject]@{ - name = "Unity Mono 6000.0.2"; - unityVersion = "6000.0.2"; +param ( + [string[]] $MatrixOutName, + [string] $GithubOutput +) + +$ErrorActionPreference = "Stop"; + +$operatingSystems = @( + [pscustomobject]@{ + name = "Windows"; + runner = "windows-latest"; + ridname = "win"; + arch = @("x86","x64"); # while .NET Framework supports Arm64, GitHub doesn't provide Arm windows runners + runnerArch = 1; + hasFramework = $true; + monoArch = @("win32", "win64", "win_arm64"); + monoDll = "mono-2.0-bdwgc.dll"; + }, + [pscustomobject]@{ + name = "Linux"; + runner = "ubuntu-latest"; + ridname = "linux"; + arch = @("x64"); + runnerArch = 0; + hasMono = $true; + monoArch = @("linux64"); + monoDll = "limonobdwgc-2.0.so"; # TODO + }, + [pscustomobject]@{ + name = "MacOS 13"; + runner = "macos-13"; + ridname = "osx"; + arch = @("x64"); + runnerArch = 0; + hasMono = $true; + monoArch = @("macos_x64"); + monoDll = "limonobdwgc-2.0.dylib"; + }, + [pscustomobject]@{ + #enable = $false; + name = "MacOS 14 (M1)"; + runner = "macos-14"; + ridname = "osx"; + arch = @("x64"<#, "arm64"#>); # x64 comes from Rosetta, and we disable arm64 mode for now because we don't support it yet + runnerArch = 1; + hasMono = $true; + monoArch = @("macos_x64", "macos_arm64"); + monoDll = "limonobdwgc-2.0.dylib"; + } +); + +$dotnetVersions = @( + [pscustomobject]@{ + name = ".NET Framework 4.x"; + id = 'fx'; + tfm = "net462"; + rids = @("win-x86","win-x64","win-arm64"); + isFramework = $true; + }, + [pscustomobject]@{ + name = ".NET Core 2.1"; + sdk = "2.1"; + tfm = "netcoreapp2.1"; + rids = @("win-x86","win-x64","linux-x64","osx-x64"); + needsRestore = $true; + }, + [pscustomobject]@{ + name = ".NET Core 3.0"; + sdk = "3.0"; + tfm = "netcoreapp3.0"; + rids = @("win-x86","win-x64","linux-x64","osx-x64"); + }, + [pscustomobject]@{ + name = ".NET Core 3.1"; + sdk = "3.1"; + tfm = "netcoreapp3.1"; + rids = @("win-x86","win-x64","linux-x64","osx-x64"); + }, + [pscustomobject]@{ + name = ".NET 5.0"; + sdk = "5.0"; + tfm = "net5.0"; + rids = @("win-x86","win-x64","linux-x64","osx-x64"); + }, + [pscustomobject]@{ + name = ".NET 6.0"; + sdk = "6.0"; + tfm = "net6.0"; + rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64"); + pgo = $true; + }, + [pscustomobject]@{ + name = ".NET 7.0"; + sdk = "7.0"; + tfm = "net7.0"; + rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64"); + pgo = $true; + }, + [pscustomobject]@{ + name = ".NET 8.0"; + sdk = "8.0"; + tfm = "net8.0"; + rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64"); + pgo = $true; + } +); + +$monoTfm = "net462"; + +$monoVersions = @( + <# + [pscustomobject]@{ + name = "Unity Mono 6000.0.2"; + unityVersion = "6000.0.2"; monoName = "MonoBleedingEdge"; - } + } #> -); - -$jobs = @(); - -foreach ($os in $operatingSystems) -{ - if ($os.enable -eq $false) { continue; } - $outos = $os | Select-Object -ExcludeProperty arch,ridname,hasFramework,hasMono,monoArch,monoDll,runnerArch - - if ($os.hasMono -and $os.runnerArch -lt $os.arch.Length) - { - # this OS has a system mono, emit a job for that - $jobs += @( - [pscustomobject]@{ - title = "System Mono on $($os.name)"; - os = $outos; - dotnet = [pscustomobject]@{ - name = "Mono"; - id = "sysmono"; - needsRestore = $true; # Monos always need restore - isMono = $true; - systemMono = $true; +); + +$jobs = @(); + +foreach ($os in $operatingSystems) +{ + if ($os.enable -eq $false) { continue; } + $outos = $os | Select-Object -ExcludeProperty arch,ridname,hasFramework,hasMono,monoArch,monoDll,runnerArch + + if ($os.hasMono -and $os.runnerArch -lt $os.arch.Length) + { + # this OS has a system mono, emit a job for that + $jobs += @( + [pscustomobject]@{ + title = "System Mono on $($os.name)"; + os = $outos; + dotnet = [pscustomobject]@{ + name = "Mono"; + id = "sysmono"; + needsRestore = $true; # Monos always need restore + isMono = $true; + systemMono = $true; tfm = $monoTfm; - }; + }; arch = $os.arch[$os.runnerArch]; } ); - } - - foreach ($arch in $os.arch) - { - $rid = $os.ridname + "-" + $arch; - - foreach ($dotnet in $dotnetVersions) - { - if ($dotnet.enable -eq $false) { continue; } - - if ($dotnet.isFramework -and -not $os.hasFramework) - { - # we're looking at .NET Framework, but this OS doesn't support it - continue; - } - - if (-not $dotnet.rids -contains $rid) - { - # the current OS/arch/runtime triple is not supported by .NET, skip - continue; - } - - $outdotnet = $dotnet | Select-Object -ExcludeProperty rids - - $title = "$($dotnet.name) $arch on $($os.name)" - if ($dotnet.pgo) - { - # this runtime supports pgo; generate 2 jobs; one with it enabled, one without - $jobs += @( - [pscustomobject]@{ - title = $title + " (PGO Off)"; - os = $outos; - dotnet = $outdotnet; - arch = $arch; - usePgo = $false; - }, - [pscustomobject]@{ - title = $title + " (PGO On)"; - os = $outos; - dotnet = $outdotnet; - arch = $arch; - usePgo = $true; - } - ); - } - else - { - # this is a normal job; only add one - $jobs += @( - [pscustomobject]@{ - title = $title; - os = $outos; - dotnet = $outdotnet; - arch = $arch; - } - ); - } - } - - # TODO: non-system mono - } -} - -# TODO: support multiple batches -if ($jobs.Length -gt 256) -{ - Write-Error "Generated more than 256 jobs; actions will fail!"; -} - -$matrixObj = [pscustomobject]@{include = $jobs;}; -$matrixStr = ConvertTo-Json -Compress -Depth 5 $matrixObj; + } + + foreach ($arch in $os.arch) + { + $rid = $os.ridname + "-" + $arch; + + foreach ($dotnet in $dotnetVersions) + { + if ($dotnet.enable -eq $false) { continue; } + + if ($dotnet.isFramework -and -not $os.hasFramework) + { + # we're looking at .NET Framework, but this OS doesn't support it + continue; + } + + if (-not $dotnet.rids -contains $rid) + { + # the current OS/arch/runtime triple is not supported by .NET, skip + continue; + } + + $outdotnet = $dotnet | Select-Object -ExcludeProperty rids + + $title = "$($dotnet.name) $arch on $($os.name)" + if ($dotnet.pgo) + { + # this runtime supports pgo; generate 2 jobs; one with it enabled, one without + $jobs += @( + [pscustomobject]@{ + title = $title + " (PGO Off)"; + os = $outos; + dotnet = $outdotnet; + arch = $arch; + usePgo = $false; + }, + [pscustomobject]@{ + title = $title + " (PGO On)"; + os = $outos; + dotnet = $outdotnet; + arch = $arch; + usePgo = $true; + } + ); + } + else + { + # this is a normal job; only add one + $jobs += @( + [pscustomobject]@{ + title = $title; + os = $outos; + dotnet = $outdotnet; + arch = $arch; + } + ); + } + } + + # TODO: non-system mono + } +} + +# TODO: support multiple batches +if ($jobs.Length -gt 256) +{ + Write-Error "Generated more than 256 jobs; actions will fail!"; +} + +$matrixObj = [pscustomobject]@{include = $jobs;}; +$matrixStr = ConvertTo-Json -Compress -Depth 5 $matrixObj; echo "$($MatrixOutName[0])=$matrixStr" >> $GithubOutput; \ No newline at end of file diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml index 4278ca3d..c19958db 100644 --- a/.github/workflows/test_results.yml +++ b/.github/workflows/test_results.yml @@ -50,7 +50,7 @@ jobs: # compare_to_earlier_commit: false - name: Publish Test Results - uses: nike4613/actions-test-results/master@master + uses: nike4613/actions-test-results@v2 with: commit: ${{ github.event.workflow_run.head_sha }} event_file: event.json From 5d5112c75ebe30dbb60c43266d95431d4b1691eb Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 31 May 2024 01:12:34 -0500 Subject: [PATCH 098/133] Remove old Azure Pipelines based CI files --- MonoMod.sln | 5 - azure-pipelines-coretest.yml | 90 --------- azure-pipelines-docfx.ps1 | 31 --- azure-pipelines-postbuild.yml | 32 ---- azure-pipelines-prepushcommon.ps1 | 10 - azure-pipelines.yml | 301 ------------------------------ 6 files changed, 469 deletions(-) delete mode 100644 azure-pipelines-coretest.yml delete mode 100644 azure-pipelines-docfx.ps1 delete mode 100644 azure-pipelines-postbuild.yml delete mode 100644 azure-pipelines-prepushcommon.ps1 delete mode 100644 azure-pipelines.yml diff --git a/MonoMod.sln b/MonoMod.sln index 6d8a04c9..7f9366e7 100644 --- a/MonoMod.sln +++ b/MonoMod.sln @@ -23,11 +23,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig .gitignore = .gitignore - azure-pipelines-coretest.yml = azure-pipelines-coretest.yml - azure-pipelines-docfx.ps1 = azure-pipelines-docfx.ps1 - azure-pipelines-postbuild.yml = azure-pipelines-postbuild.yml - azure-pipelines-prepushcommon.ps1 = azure-pipelines-prepushcommon.ps1 - azure-pipelines.yml = azure-pipelines.yml Directory.Build.rsp = Directory.Build.rsp global.json = global.json icon.png = icon.png diff --git a/azure-pipelines-coretest.yml b/azure-pipelines-coretest.yml deleted file mode 100644 index 18434389..00000000 --- a/azure-pipelines-coretest.yml +++ /dev/null @@ -1,90 +0,0 @@ -parameters: -- name: targetFramework - type: string - default: '' -- name: buildConfiguration - type: string - default: 'Release' -- name: arch - type: string - default: '' -- name: condition - type: string - default: true -- name: usePgo - type: string - default: false -- name: pgoTimeout - type: string - default: 5 - -steps: -- ${{ if eq(length('${{parameters.arch}}'), 0) }}: - - task: CmdLine@2 - condition: and(always(), ${{parameters.condition}}) - displayName: 'Test: core: ${{parameters.targetFramework}}' - inputs: - script: | - dotnet test --no-build -c ${{parameters.buildConfiguration}} -f ${{parameters.targetFramework}} -l:"trx;LogFileName=testresults.core.${{parameters.targetFramework}}.trx" - - task: PublishTestResults@2 - condition: and(always(), ${{parameters.condition}}) - displayName: 'Test: Publish: core: ${{parameters.targetFramework}}' - inputs: - testResultsFormat: 'VSTest' - testResultsFiles: '**/testresults.core.${{parameters.targetFramework}}.trx' - testRunTitle: 'Tests @ $(jobArchName) core ${{parameters.targetFramework}}' - -- ${{ if ne(length('${{parameters.arch}}'), 0) }}: - - task: CmdLine@2 - condition: and(always(), ${{parameters.condition}}) - displayName: 'Test: core: ${{parameters.targetFramework}}-${{parameters.arch}}' - inputs: - script: | - dotnet test --no-build -c ${{parameters.buildConfiguration}} -f ${{parameters.targetFramework}} --arch '${{parameters.arch}}' -l:"trx;LogFileName=testresults.core.${{parameters.targetFramework}}-${{parameters.arch}}.trx" - - task: PublishTestResults@2 - condition: and(always(), ${{parameters.condition}}) - displayName: 'Test: Publish: core: ${{parameters.targetFramework}}-${{parameters.arch}}' - inputs: - testResultsFormat: 'VSTest' - testResultsFiles: '**/testresults.core.${{parameters.targetFramework}}-${{parameters.arch}}.trx' - testRunTitle: 'Tests @ $(jobArchName) core ${{parameters.targetFramework}}-${{parameters.arch}}' - -- ${{ if and(eq('${{parameters.usePgo}}', 'true'), eq(length('${{parameters.arch}}'), 0)) }}: - - task: CmdLine@2 - condition: and(always(), ${{parameters.condition}}) - timeoutInMinutes: ${{parameters.pgoTimeout}} - displayName: 'Test: core: ${{parameters.targetFramework}} PGO' - env: - DOTNET_ReadyToRun: 0 - DOTNET_TC_QuickJitForLoops: 1 - DOTNET_TieredPGO: 1 - inputs: - script: | - dotnet test --no-build -c ${{parameters.buildConfiguration}} -f ${{parameters.targetFramework}} -l:"trx;LogFileName=testresults.core.${{parameters.targetFramework}}.trx" - - task: PublishTestResults@2 - condition: and(always(), ${{parameters.condition}}) - displayName: 'Test: Publish: core: ${{parameters.targetFramework}}' - inputs: - testResultsFormat: 'VSTest' - testResultsFiles: '**/testresults.core.${{parameters.targetFramework}}.pgo.trx' - testRunTitle: 'Tests @ $(jobArchName) core ${{parameters.targetFramework}} PGO' - -- ${{ if and(eq('${{parameters.usePgo}}', 'true'), ne(length('${{parameters.arch}}'), 0)) }}: - - task: CmdLine@2 - condition: and(always(), ${{parameters.condition}}) - timeoutInMinutes: ${{parameters.pgoTimeout}} - displayName: 'Test: core: ${{parameters.targetFramework}}-${{parameters.arch}} PGO' - env: - DOTNET_ReadyToRun: 0 - DOTNET_TC_QuickJitForLoops: 1 - DOTNET_TieredPGO: 1 - inputs: - script: | - dotnet test --no-build -c ${{parameters.buildConfiguration}} -f ${{parameters.targetFramework}} --arch '${{parameters.arch}}' -l:"trx;LogFileName=testresults.core.${{parameters.targetFramework}}-${{parameters.arch}}.trx" - - task: PublishTestResults@2 - condition: and(always(), ${{parameters.condition}}) - displayName: 'Test: Publish: core: ${{parameters.targetFramework}}-${{parameters.arch}}' - inputs: - testResultsFormat: 'VSTest' - testResultsFiles: '**/testresults.core.${{parameters.targetFramework}}-${{parameters.arch}}.pgo.trx' - testRunTitle: 'Tests @ $(jobArchName) core ${{parameters.targetFramework}}-${{parameters.arch}} PGO' \ No newline at end of file diff --git a/azure-pipelines-docfx.ps1 b/azure-pipelines-docfx.ps1 deleted file mode 100644 index ca10e6af..00000000 --- a/azure-pipelines-docfx.ps1 +++ /dev/null @@ -1,31 +0,0 @@ -$GitHubBotName=$args[0] -$GitHubBotEmail=$args[1] -$GitHubBotToken=$args[2] - -# PowerShell and git don't work well together. -$ErrorActionPreference="Continue" - -Write-Output "Setting up config" -$DocFXRepo="https://$GitHubBotToken@github.com/MonoMod/MonoMod.github.io.git" -git config --global user.name $GitHubBotName 2>&1 | ForEach-Object { "$_" } -git config --global user.email $GitHubBotEmail 2>&1 | ForEach-Object { "$_" } - -Write-Output "Setting up file hierarchy" -git clone --recursive --branch docfx $DocFXRepo docfx 2>&1 | ForEach-Object { "$_" } -Set-Location docfx -git clone --recursive --branch master $DocFXRepo _site 2>&1 | ForEach-Object { "$_" } - -Write-Output "Installing DocFX" -choco install docfx -y - -Write-Output "Running docfx build" -docfx metadata -docfx build - -Write-Output "Pushing updated _site to master branch on GitHub" -Set-Location _site -git add . 2>&1 | ForEach-Object { "$_" } -git commit --allow-empty -m "Rebuild (automatic commit via Azure Pipelines)" 2>&1 | ForEach-Object { "$_" } -git push 2>&1 | ForEach-Object { "$_" } - -Write-Output "Done" diff --git a/azure-pipelines-postbuild.yml b/azure-pipelines-postbuild.yml deleted file mode 100644 index e6e7dc7f..00000000 --- a/azure-pipelines-postbuild.yml +++ /dev/null @@ -1,32 +0,0 @@ -parameters: - targetFramework: '' - buildConfiguration: 'Release' - -steps: -- task: CopyFiles@2 - displayName: 'Artifacts: Pack: ${{parameters.targetFramework}}' - continueOnError: true - inputs: - sourceFolder: '$(Agent.BuildDirectory)' - # the leading **/ shouldn't be necessary, but it seems to be - contents: | - **/artifacts/bin/**/${{lower(parameters.buildConfiguration)}}_${{parameters.targetFramework}}/**/* - !**/MonoMod.FrameworkTests/**/* - !**/Monomod.Backports.Filter/**/* - !**/Monomod.ILHelpers.Patcher/**/* - !**/MonoMod.UnitTest/**/* - !**/xunit.* - !**/testhost* - !**/System.* - !**/Microsoft.* - targetFolder: '$(Build.ArtifactStagingDirectory)/${{parameters.targetFramework}}' - cleanTargetFolder: true - overWrite: true - flattenFolders: true -- task: PublishBuildArtifacts@1 - displayName: 'Artifacts: Publish: ${{parameters.targetFramework}}' - continueOnError: true - inputs: - pathtoPublish: '$(Build.ArtifactStagingDirectory)/${{parameters.targetFramework}}' - artifactName: '$(artifactPrefix)${{parameters.targetFramework}}$(artifactSuffix)' - publishLocation: 'Container' diff --git a/azure-pipelines-prepushcommon.ps1 b/azure-pipelines-prepushcommon.ps1 deleted file mode 100644 index a97dc263..00000000 --- a/azure-pipelines-prepushcommon.ps1 +++ /dev/null @@ -1,10 +0,0 @@ -# MonoMod.Common doesn't exist. -#if ($(git rev-parse "@:MonoMod.Common") -eq $(git rev-parse "@~:MonoMod.Common")) { -# Write-Output "MonoMod.Common wasn't changed." -# for ($i = 0; $i -lt $args.Length; $i++) { -# Write-Output "Deleting $($args[$i])" -# Remove-Item -Path "$($args[$i])" -# } -#} else { -# Write-Output "MonoMod.Common was changed." -#} diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index fa9b5bdd..00000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,301 +0,0 @@ -strategy: - matrix: - windows: - jobArchName: 'Windows' - imageName: 'windows-latest' - artifactPrefix: '' - artifactSuffix: '' - testers: ';fx;core2.1;core3;core3.1;core5;core6;core7;core8;' - testx86: 'true' - generateDocFX: 'false' - pushNuGet: 'true' - linux: - jobArchName: 'Linux' - imageName: 'ubuntu-latest' - artifactPrefix: '~dbg.linux.' - artifactSuffix: '' - testers: ';mono;core2.1;core3;core3.1;core5;core6;core7;core8;' - testx86: 'false' - generateDocFX: 'false' - pushNuGet: 'false' - macos: - jobArchName: 'macOS' - imageName: 'macOS-latest' - artifactPrefix: '~dbg.macos.' - artifactSuffix: '' - testers: ';mono;core2.1;core3;core3.1;core5;core6;core7;core8;' - testx86: 'false' - generateDocFX: 'false' - pushNuGet: 'false' - maxParallel: 4 - -variables: - solution: '**/*.sln' - buildConfiguration: 'Release' - buildPlatform: 'Any CPU' - xunitVer: '2.4.2' - xunit: '{0}{1}/.nuget/packages/xunit.runner.console/$(xunitVer)/tools/{2}/xunit.console.{3} artifacts/bin/MonoMod.UnitTest/release_{6}/MonoMod.UnitTest.dll -xml testresults.{4}.{6}.xml {5}' - -name: '$(Date:y.M.d).$(Rev:r)' - -pool: - vmImage: '$(imageName)' - -steps: -- checkout: self - clean: false - fetchDepth: 2 - lfs: false - submodules: recursive - -# Run pre-build steps. -- task: UseDotNet@2 - displayName: 'Install .NET Core SDK 2.1.x' - inputs: - packageType: sdk - version: '2.1.x' -- task: UseDotNet@2 - displayName: 'Install .NET Core SDK 3.0.x' - inputs: - packageType: sdk - version: '3.0.x' -- task: UseDotNet@2 - displayName: 'Install .NET Core SDK 3.1.x' - inputs: - packageType: sdk - version: '3.1.x' -- task: UseDotNet@2 - condition: contains(variables.testers, ';core5;') - displayName: 'Install .NET SDK 5.0.x' - inputs: - packageType: sdk - version: '5.0.x' -- task: UseDotNet@2 - condition: contains(variables.testers, ';core6;') - displayName: 'Install .NET SDK 6.0.x' - inputs: - packageType: sdk - version: '6.0.x' -- task: UseDotNet@2 - condition: contains(variables.testers, ';core7;') - displayName: 'Install .NET SDK 7.0.x' - inputs: - packageType: sdk - version: '7.0.x' -- task: UseDotNet@2 - condition: contains(variables.testers, ';core8;') - displayName: 'Install .NET SDK 8.0.x' - inputs: - packageType: sdk - version: '8.0.x' -- task: NuGetToolInstaller@1 - displayName: 'Update NuGet' - inputs: - checkLatest: true -- task: DotNetCoreCLI@2 - displayName: 'dotnet: Restore' - inputs: - command: 'restore' - arguments: '-p:XunitVersion="$(xunitVer)"' - -# Build using core. Mono's msbuild started to cause too many issues. -- task: DotNetCoreCLI@2 - displayName: 'dotnet: Build' - inputs: - command: 'build' - arguments: '-c $(buildConfiguration) -p:XunitVersion="$(xunitVer)" -p:VersionSuffix="daily.$(Build.BuildNumber)" -p:ContinuousIntegrationBuild=true -tl:off' - -# Create and "publish" main artifacts. -- template: 'azure-pipelines-postbuild.yml' - parameters: - targetFramework: 'net35' -- template: 'azure-pipelines-postbuild.yml' - parameters: - targetFramework: 'net452' -- template: 'azure-pipelines-postbuild.yml' - parameters: - targetFramework: 'netstandard2.0' -- template: 'azure-pipelines-postbuild.yml' - parameters: - targetFramework: 'net5.0' -- template: 'azure-pipelines-postbuild.yml' - parameters: - targetFramework: 'net6.0' -- template: 'azure-pipelines-postbuild.yml' - parameters: - targetFramework: 'net7.0' -# - template: 'azure-pipelines-postbuild.yml' -# parameters: -# targetFramework: 'net8.0' - -# Create and "publish" nupkg artifacts. -- task: DotNetCoreCLI@2 - displayName: 'Artifacts: Pack: nupkgs' - continueOnError: true - inputs: - #command: 'pack' - command: 'custom' # if you use command: pack, then you *have* to set packagesToPack, despite not providing it doing the right thing - custom: 'pack' - arguments: '-c $(buildConfiguration) -p:XunitVersion="$(xunitVer)" -p:VersionSuffix="daily.$(Build.BuildNumber)" -p:ContinuousIntegrationBuild=true' - nobuild: true - configuration: '$(buildConfiguration)' - versioningScheme: 'byBuildNumber' - #packagesToPack: 'MonoMod@(|.Common|.Utils|.RuntimeDetour|.RuntimeDetour.HookGen)/*.csproj' - #packDirectory: '$(Build.ArtifactStagingDirectory)/nupkgs' -- task: PublishBuildArtifacts@1 - displayName: 'Artifacts: Publish: nupkgs' - continueOnError: true - inputs: - pathtoPublish: 'artifacts/package/${{lower(variables.buildConfiguration)}}' - artifactName: '$(artifactPrefix)nupkgs$(artifactSuffix)' - publishLocation: 'Container' -- task: NuGetCommand@2 - condition: and(always(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables.pushNuGet, 'true')) - displayName: 'Artifacts: Push: nupkgs' - inputs: - command: push - packagesToPush: 'artifacts/package/${{lower(variables.buildConfiguration)}}/*.nupkg' - publishVstsFeed: '572c97eb-dbaa-4a55-90e5-1d05431535bd/72ad568d-c548-4599-8b0a-9ea52b45bbbd' -#- task: PowerShell@2 -# condition: and(always(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables.pushNuGet, 'true')) -# displayName: 'Artifacts: Prepare: MonoMod.Common to nuget.org' -# continueOnError: true -# inputs: -# filePath: 'azure-pipelines-prepushcommon.ps1' -# arguments: '$(Build.ArtifactStagingDirectory)/nupkgs/MonoMod.Common.*.nupkg' -#- task: NuGetCommand@2 -# condition: and(always(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables.pushNuGet, 'true')) -# displayName: 'Artifacts: Push: MonoMod.Common to nuget.org' -# inputs: -# command: 'push' -# packagesToPush: '$(Build.ArtifactStagingDirectory)/nupkgs/MonoMod.Common.*.nupkg' -# nuGetFeedType: 'external' -# publishFeedCredentials: 'NuGet MonoMod' - -# Run tests. -# I've tried to simplify this mess. It's impossible. --ade -- task: CmdLine@2 - condition: and(always(), contains(variables.testers, ';fx;')) - displayName: 'Test: fx: net452' - inputs: - script: ${{format(variables.xunit, '', '%userprofile%', 'net452', 'exe', 'fx', '', 'net46')}} -- task: PublishTestResults@2 - condition: and(always(), contains(variables.testers, ';fx;')) - displayName: 'Test: Publish: fx: net46' - inputs: - testResultsFormat: 'xUnit' - testResultsFiles: '**/testresults.fx.net46.xml' - testRunTitle: 'Tests @ $(jobArchName) fx net46' - -- task: CmdLine@2 - condition: and(always(), contains(variables.testers, ';mono;')) - displayName: 'Test: mono: net46' - inputs: - script: ${{format(variables.xunit, 'mono --debug ', '~', 'net452', 'exe', 'mono', '', 'net46')}} -- task: PublishTestResults@2 - condition: and(always(), contains(variables.testers, ';mono;')) - displayName: 'Test: Publish: mono: net46' - inputs: - testResultsFormat: 'xUnit' - testResultsFiles: '**/testresults.mono.net46.xml' - testRunTitle: 'Tests @ $(jobArchName) mono net46' - -- task: CmdLine@2 - condition: and(always(), contains(variables.testers, ';monoslow;')) - displayName: 'Test: monoslow: net46' - inputs: - script: ${{format(variables.xunit, 'mono --debug ', '~', 'net452', 'exe', 'monoslow', '-parallel none -appdomains denied -verbose', 'net46')}} -- task: PublishTestResults@2 - condition: and(always(), contains(variables.testers, ';monoslow;')) - displayName: 'Test: Publish: monoslow: net46' - inputs: - testResultsFormat: 'xUnit' - testResultsFiles: '**/testresults.monoslow.net46.xml' - testRunTitle: 'Tests @ $(jobArchName) monoslow net46' - -# All of the .NET Core tests run via azure-pipelines-coretest.yml, and have an optional 'arch' parameter to allow specific arch selection -- template: 'azure-pipelines-coretest.yml' - parameters: - condition: and(true, contains(variables.testers, ';core2.1;')) - targetFramework: 'netcoreapp2.1' - -- template: 'azure-pipelines-coretest.yml' - parameters: - condition: and(true, contains(variables.testers, ';core3;')) - targetFramework: 'netcoreapp3.0' - arch: 'x64' -- template: 'azure-pipelines-coretest.yml' - parameters: - condition: and(contains(variables.testers, ';core3;'), eq(variables.testx86, 'true')) - targetFramework: 'netcoreapp3.0' - arch: 'x86' - -- template: 'azure-pipelines-coretest.yml' - parameters: - condition: and(true, contains(variables.testers, ';core3.1;')) - targetFramework: 'netcoreapp3.1' - arch: 'x64' -- template: 'azure-pipelines-coretest.yml' - parameters: - condition: and(contains(variables.testers, ';core3.1;'), eq(variables.testx86, 'true')) - targetFramework: 'netcoreapp3.1' - arch: 'x86' - -- template: 'azure-pipelines-coretest.yml' - parameters: - condition: and(true, contains(variables.testers, ';core5;')) - targetFramework: 'net5.0' - arch: 'x64' -- template: 'azure-pipelines-coretest.yml' - parameters: - condition: and(contains(variables.testers, ';core5;'), eq(variables.testx86, 'true')) - targetFramework: 'net5.0' - arch: 'x86' - -- template: 'azure-pipelines-coretest.yml' - parameters: - condition: and(true, contains(variables.testers, ';core6;')) - targetFramework: 'net6.0' - arch: 'x64' - usePgo: 'true' -- template: 'azure-pipelines-coretest.yml' - parameters: - condition: and(contains(variables.testers, ';core6;'), eq(variables.testx86, 'true')) - targetFramework: 'net6.0' - arch: 'x86' - usePgo: 'true' - -- template: 'azure-pipelines-coretest.yml' - parameters: - condition: and(true, contains(variables.testers, ';core7;')) - targetFramework: 'net7.0' - arch: 'x64' - usePgo: 'true' -- template: 'azure-pipelines-coretest.yml' - parameters: - condition: and(contains(variables.testers, ';core7;'), eq(variables.testx86, 'true')) - targetFramework: 'net7.0' - arch: 'x86' - usePgo: 'true' - -- template: 'azure-pipelines-coretest.yml' - parameters: - condition: and(true, contains(variables.testers, ';core8;')) - targetFramework: 'net8.0' - arch: 'x64' - usePgo: 'true' -- template: 'azure-pipelines-coretest.yml' - parameters: - condition: and(contains(variables.testers, ';core8;'), eq(variables.testx86, 'true')) - targetFramework: 'net8.0' - arch: 'x86' - usePgo: 'true' - -# Rebuild and publish the DocFX page. -- task: PowerShell@2 - condition: and(always(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables.generateDocFX, 'true')) - displayName: 'DocFX' - continueOnError: true - inputs: - filePath: 'azure-pipelines-docfx.ps1' - arguments: '$(GitHubBotName) $(GitHubBotEmail) $(GitHubBotToken)' From df275ffc35d8469737a39bbba66864287794ec31 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 31 May 2024 05:58:54 -0500 Subject: [PATCH 099/133] Initial pass to setup DocFX --- .config/dotnet-tools.json | 20 +++++ .gitignore | 1 - docfx/.gitignore | 9 +++ docfx/api/.gitignore | 5 ++ docfx/api/index.md | 2 + docfx/articles/intro.md | 1 + docfx/articles/toc.yml | 2 + docfx/docfx.json | 85 ++++++++++++++++++++++ docfx/globalmeta.json | 9 +++ docfx/index.md | 1 + docfx/toc.yml | 8 ++ docs/Core/index.md | 5 ++ docs/Core/toc.yml | 10 +++ docs/RuntimeDetour.HookGen/index.md | 3 + docs/RuntimeDetour.HookGen/toc.yml | 6 ++ docs/RuntimeDetour/implementation/index.md | 3 + docs/RuntimeDetour/implementation/toc.yml | 6 ++ docs/RuntimeDetour/index.md | 4 + docs/RuntimeDetour/toc.yml | 8 ++ docs/Utils/index.md | 3 + docs/Utils/toc.yml | 6 ++ docs/index.md | 14 ++++ docs/toc.yml | 28 +++++++ 23 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 .config/dotnet-tools.json create mode 100644 docfx/.gitignore create mode 100644 docfx/api/.gitignore create mode 100644 docfx/api/index.md create mode 100644 docfx/articles/intro.md create mode 100644 docfx/articles/toc.yml create mode 100644 docfx/docfx.json create mode 100644 docfx/globalmeta.json create mode 100644 docfx/index.md create mode 100644 docfx/toc.yml create mode 100644 docs/Core/index.md create mode 100644 docs/Core/toc.yml create mode 100644 docs/RuntimeDetour.HookGen/index.md create mode 100644 docs/RuntimeDetour.HookGen/toc.yml create mode 100644 docs/RuntimeDetour/implementation/index.md create mode 100644 docs/RuntimeDetour/implementation/toc.yml create mode 100644 docs/RuntimeDetour/index.md create mode 100644 docs/RuntimeDetour/toc.yml create mode 100644 docs/Utils/index.md create mode 100644 docs/Utils/toc.yml create mode 100644 docs/index.md create mode 100644 docs/toc.yml diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 00000000..895e0bf3 --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,20 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "docfx": { + "version": "2.76.0", + "commands": [ + "docfx" + ], + "rollForward": false + }, + "docfxtocgenerator": { + "version": "1.19.0", + "commands": [ + "DocFxTocGenerator" + ], + "rollForward": false + } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 91f9c77d..fedc461e 100644 --- a/.gitignore +++ b/.gitignore @@ -176,7 +176,6 @@ ClientBin/ *.publishsettings node_modules/ orleans.codegen.cs -docfx/ # RIA/Silverlight projects Generated_Code/ diff --git a/docfx/.gitignore b/docfx/.gitignore new file mode 100644 index 00000000..2781f6d5 --- /dev/null +++ b/docfx/.gitignore @@ -0,0 +1,9 @@ +############### +# folder # +############### +/**/DROP/ +/**/TEMP/ +/**/packages/ +/**/bin/ +/**/obj/ +_site diff --git a/docfx/api/.gitignore b/docfx/api/.gitignore new file mode 100644 index 00000000..f798527e --- /dev/null +++ b/docfx/api/.gitignore @@ -0,0 +1,5 @@ +############### +# temp file # +############### +*.yml +.manifest diff --git a/docfx/api/index.md b/docfx/api/index.md new file mode 100644 index 00000000..1cb68311 --- /dev/null +++ b/docfx/api/index.md @@ -0,0 +1,2 @@ +# PLACEHOLDER +TODO: Add .NET projects to the *src* folder and run `docfx` to generate **REAL** *API Documentation*! diff --git a/docfx/articles/intro.md b/docfx/articles/intro.md new file mode 100644 index 00000000..901a16a4 --- /dev/null +++ b/docfx/articles/intro.md @@ -0,0 +1 @@ +# Add your introductions here! diff --git a/docfx/articles/toc.yml b/docfx/articles/toc.yml new file mode 100644 index 00000000..f64352ce --- /dev/null +++ b/docfx/articles/toc.yml @@ -0,0 +1,2 @@ +- name: Introduction + href: intro.md diff --git a/docfx/docfx.json b/docfx/docfx.json new file mode 100644 index 00000000..d10433c5 --- /dev/null +++ b/docfx/docfx.json @@ -0,0 +1,85 @@ +{ + "metadata": [ + { + "src": [ + { + "src": "..", + "files": [ + "artifacts/bin/*/release_*/MonoMod*.dll" + ], + "exclude": [ + "**/MonoMod.FrameworkTests.dll", + "**/MonoMod.SourceGen.*.dll", + "**/MonoMod.UnitTest.dll", + "**/MonoMod.Backports.Tasks.dll", + "**/MonoMod.ILHelpers.Patcher.dll", + "**/MonoMod.DebugIL.dll", + "**/MonoMod.RuntimeDetour.HookGen.dll" + ] + } + ], + "dest": "api", + "disableGitFeatures": false, + "disableDefaultFilter": false, + "namespaceLayout": "nested", + "memberLayout": "separatePages", + "enumSortOrder": "declaringOrder" + } + ], + "build": { + "content": [ + { + "files": [ + "api/**.yml", + "api/index.md" + ] + }, + { + "files": [ + "articles/**.md", + "articles/**/toc.yml", + "toc.yml", + "*.md" + ] + }, + { + "src": "../docs", + "dest": "docs", + "files": [ + "**.md", + "**/toc.yml" + ] + } + ], + "resource": [ + { + "files": [ + "images/**" + ] + } + ], + "overwrite": [ + { + "files": [ + "apidoc/**.md" + ], + "exclude": [ + "obj/**", + "_site/**" + ] + } + ], + "dest": "_site", + "globalMetadataFiles": ["globalmeta.json"], + "fileMetadataFiles": [], + "template": [ + "default", "modern" + ], + "postProcessors": ["ExtractSearchIndex"], + "markdownEngineName": "markdig", + "noLangKeyword": false, + "keepFileLink": false, + "cleanupCacheHistory": false, + "disableGitFeatures": false + } +} \ No newline at end of file diff --git a/docfx/globalmeta.json b/docfx/globalmeta.json new file mode 100644 index 00000000..fc9586ee --- /dev/null +++ b/docfx/globalmeta.json @@ -0,0 +1,9 @@ +{ + "_appTitle": "MonoMod", + "_appName": "MonoMod", + "_enableSearch": true, + "_disableSideFilter": false, + "_enableNewTab": true, + "_disableContribution": false, + "_disableBreadcrumb": false +} \ No newline at end of file diff --git a/docfx/index.md b/docfx/index.md new file mode 100644 index 00000000..53c77a07 --- /dev/null +++ b/docfx/index.md @@ -0,0 +1 @@ +[!INCLUDE [readme](../README.md)] \ No newline at end of file diff --git a/docfx/toc.yml b/docfx/toc.yml new file mode 100644 index 00000000..36fb6887 --- /dev/null +++ b/docfx/toc.yml @@ -0,0 +1,8 @@ +- name: API + href: api/ + homepage: api/index.md +- name: "|" +- name: GitHub + href: https://github.com/MonoMod +- name: Discord + href: https://discord.gg/jm7GCZB \ No newline at end of file diff --git a/docs/Core/index.md b/docs/Core/index.md new file mode 100644 index 00000000..8ebad316 --- /dev/null +++ b/docs/Core/index.md @@ -0,0 +1,5 @@ +# Core + +* [Discussion about the CoreCLR JIT hook design](./CoreCLRJitHooks.md) +* [INativeExceptionHelper](./NativeExceptionHandler.md) +* [MonoMod.Core's Architecture](./Architecture.md) diff --git a/docs/Core/toc.yml b/docs/Core/toc.yml new file mode 100644 index 00000000..fe251a85 --- /dev/null +++ b/docs/Core/toc.yml @@ -0,0 +1,10 @@ +# This is an automatically generated file +items: +- name: Core + href: index.md +- name: Discussion about the CoreCLR JIT hook design + href: CoreCLRJitHooks.md +- name: INativeExceptionHelper + href: NativeExceptionHandler.md +- name: MonoMod.Core's Architecture + href: Architecture.md diff --git a/docs/RuntimeDetour.HookGen/index.md b/docs/RuntimeDetour.HookGen/index.md new file mode 100644 index 00000000..b2aabd41 --- /dev/null +++ b/docs/RuntimeDetour.HookGen/index.md @@ -0,0 +1,3 @@ +# RuntimeDetour.HookGen + +* [Using HookGen](./Usage.md) diff --git a/docs/RuntimeDetour.HookGen/toc.yml b/docs/RuntimeDetour.HookGen/toc.yml new file mode 100644 index 00000000..0e96a2d4 --- /dev/null +++ b/docs/RuntimeDetour.HookGen/toc.yml @@ -0,0 +1,6 @@ +# This is an automatically generated file +items: +- name: RuntimeDetour.HookGen + href: index.md +- name: Using HookGen + href: Usage.md diff --git a/docs/RuntimeDetour/implementation/index.md b/docs/RuntimeDetour/implementation/index.md new file mode 100644 index 00000000..986ac223 --- /dev/null +++ b/docs/RuntimeDetour/implementation/index.md @@ -0,0 +1,3 @@ +# Implementation + +* [ChainHotPatching](./ChainHotPatching.md) diff --git a/docs/RuntimeDetour/implementation/toc.yml b/docs/RuntimeDetour/implementation/toc.yml new file mode 100644 index 00000000..f1b6b7b8 --- /dev/null +++ b/docs/RuntimeDetour/implementation/toc.yml @@ -0,0 +1,6 @@ +# This is an automatically generated file +items: +- name: Implementation + href: index.md +- name: ChainHotPatching + href: ChainHotPatching.md diff --git a/docs/RuntimeDetour/index.md b/docs/RuntimeDetour/index.md new file mode 100644 index 00000000..fc79e9b4 --- /dev/null +++ b/docs/RuntimeDetour/index.md @@ -0,0 +1,4 @@ +# RuntimeDetour + +* [Implementation](./implementation) +* [Using RuntimeDetour](./Usage.md) diff --git a/docs/RuntimeDetour/toc.yml b/docs/RuntimeDetour/toc.yml new file mode 100644 index 00000000..5b7653de --- /dev/null +++ b/docs/RuntimeDetour/toc.yml @@ -0,0 +1,8 @@ +# This is an automatically generated file +items: +- name: RuntimeDetour + href: index.md +- name: Implementation + href: implementation +- name: Using RuntimeDetour + href: Usage.md diff --git a/docs/Utils/index.md b/docs/Utils/index.md new file mode 100644 index 00000000..fc6bfb66 --- /dev/null +++ b/docs/Utils/index.md @@ -0,0 +1,3 @@ +# Utils + +* [Using ModInterop](./ModInterop.md) diff --git a/docs/Utils/toc.yml b/docs/Utils/toc.yml new file mode 100644 index 00000000..a625ef51 --- /dev/null +++ b/docs/Utils/toc.yml @@ -0,0 +1,6 @@ +# This is an automatically generated file +items: +- name: Utils + href: index.md +- name: Using ModInterop + href: ModInterop.md diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..5032a773 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,14 @@ +# Docs + +* [Core](./Core) +* [Debugging With MonoMod](./Debugging.md) +* [MonoMod.Backports](./README.Backports.md) +* [MonoMod.ILHelpers](./README.ILHelpers.md) +* [MonoMod.Utils](./README.Utils.md) +* [README.Core](./README.Core.md) +* [README.RuntimeDetour](./README.RuntimeDetour.md) +* [RuntimeDetour](./RuntimeDetour) +* [RuntimeDetour.HookGen](./RuntimeDetour.HookGen) +* [Switches](./Switches.md) +* [Using MonoMod.Patcher](./README.Patcher.md) +* [Utils](./Utils) diff --git a/docs/toc.yml b/docs/toc.yml new file mode 100644 index 00000000..3b11b37c --- /dev/null +++ b/docs/toc.yml @@ -0,0 +1,28 @@ +# This is an automatically generated file +items: +- name: Docs + href: index.md +- name: Core + href: Core +- name: Debugging With MonoMod + href: Debugging.md +- name: MonoMod.Backports + href: README.Backports.md +- name: MonoMod.ILHelpers + href: README.ILHelpers.md +- name: MonoMod.RuntimeDetour + href: README.RuntimeDetour.md +- name: MonoMod.Utils + href: README.Utils.md +- name: README.Core + href: README.Core.md +- name: RuntimeDetour + href: RuntimeDetour +- name: RuntimeDetour.HookGen + href: RuntimeDetour.HookGen +- name: Switches + href: Switches.md +- name: Using MonoMod.Patcher + href: README.Patcher.md +- name: Utils + href: Utils From 0d616f3aecfa27c6876201594da5d1bde559abc6 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 31 May 2024 06:09:43 -0500 Subject: [PATCH 100/133] Copy some more config from old setup --- docfx/api/index.md | 2 -- docfx/docfx.json | 2 ++ docfx/filterConfig.yml | 7 +++++++ docfx/globalmeta.json | 13 ++++++++----- docfx/images/favicon.png | Bin 0 -> 188575 bytes docfx/images/logo-small.png | Bin 0 -> 6514 bytes 6 files changed, 17 insertions(+), 7 deletions(-) delete mode 100644 docfx/api/index.md create mode 100644 docfx/filterConfig.yml create mode 100644 docfx/images/favicon.png create mode 100644 docfx/images/logo-small.png diff --git a/docfx/api/index.md b/docfx/api/index.md deleted file mode 100644 index 1cb68311..00000000 --- a/docfx/api/index.md +++ /dev/null @@ -1,2 +0,0 @@ -# PLACEHOLDER -TODO: Add .NET projects to the *src* folder and run `docfx` to generate **REAL** *API Documentation*! diff --git a/docfx/docfx.json b/docfx/docfx.json index d10433c5..48015b93 100644 --- a/docfx/docfx.json +++ b/docfx/docfx.json @@ -21,6 +21,8 @@ "dest": "api", "disableGitFeatures": false, "disableDefaultFilter": false, + "outputFormat": "apiPage", + "filter": "filterConfig.yml", "namespaceLayout": "nested", "memberLayout": "separatePages", "enumSortOrder": "declaringOrder" diff --git a/docfx/filterConfig.yml b/docfx/filterConfig.yml new file mode 100644 index 00000000..cad31780 --- /dev/null +++ b/docfx/filterConfig.yml @@ -0,0 +1,7 @@ +apiRules: +- exclude: + uidRegex: ^System.* + type: Namespace +- exclude: + uidRegex: ^Iced.* + type: Namespace \ No newline at end of file diff --git a/docfx/globalmeta.json b/docfx/globalmeta.json index fc9586ee..89a3adb5 100644 --- a/docfx/globalmeta.json +++ b/docfx/globalmeta.json @@ -1,9 +1,12 @@ { "_appTitle": "MonoMod", - "_appName": "MonoMod", + "_appFooter": "Yet another C# modding swiss army knife.", + "_description": "A C# modding swiss army knife, powered by cecil.", + "author": "0x0ade and contributors", + "url": "https://monomod.github.io/", + "image": "https://monomod.github.io/favicon.png", + "_appLogoPath": "images/logo-small.png", + "_appFaviconPath": "images/favicon.png", "_enableSearch": true, - "_disableSideFilter": false, - "_enableNewTab": true, - "_disableContribution": false, - "_disableBreadcrumb": false + "_enableNewTab": true } \ No newline at end of file diff --git a/docfx/images/favicon.png b/docfx/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..5aadb2ad0d2a1d49cd1e3d5ad97531232628ffb5 GIT binary patch literal 188575 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

Gs!p|8a)1 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℞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 Y7tD7g?D!g>&;S4c07*qoM6N<$g4+;jr~m)} literal 0 HcmV?d00001 From f0cf656a71d441ef481473c6e21233e9bd0ec2b1 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 31 May 2024 06:34:41 -0500 Subject: [PATCH 101/133] More tweaks to docfx generation --- docfx/api/index.md | 0 docfx/docfx.json | 20 +++++++++++--------- docfx/toc.yml | 5 +++++ 3 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 docfx/api/index.md diff --git a/docfx/api/index.md b/docfx/api/index.md new file mode 100644 index 00000000..e69de29b diff --git a/docfx/docfx.json b/docfx/docfx.json index 48015b93..c74b23f1 100644 --- a/docfx/docfx.json +++ b/docfx/docfx.json @@ -5,16 +5,17 @@ { "src": "..", "files": [ - "artifacts/bin/*/release_*/MonoMod*.dll" + "src/**/MonoMod*.csproj" ], "exclude": [ - "**/MonoMod.FrameworkTests.dll", - "**/MonoMod.SourceGen.*.dll", - "**/MonoMod.UnitTest.dll", - "**/MonoMod.Backports.Tasks.dll", - "**/MonoMod.ILHelpers.Patcher.dll", - "**/MonoMod.DebugIL.dll", - "**/MonoMod.RuntimeDetour.HookGen.dll" + "**/MonoMod.FrameworkTests.*", + "**/MonoMod.SourceGen.*.*", + "**/MonoMod.UnitTest.*", + "**/MonoMod.Backports.Tasks.*", + "**/MonoMod.Patcher.*", + "**/MonoMod.ILHelpers.Patcher.*", + "**/MonoMod.DebugIL.*", + "**/MonoMod.RuntimeDetour.HookGen.*" ] } ], @@ -23,7 +24,8 @@ "disableDefaultFilter": false, "outputFormat": "apiPage", "filter": "filterConfig.yml", - "namespaceLayout": "nested", + "categoryLayout": "nested", + "namespaceLayout": "flattened", "memberLayout": "separatePages", "enumSortOrder": "declaringOrder" } diff --git a/docfx/toc.yml b/docfx/toc.yml index 36fb6887..1c7b4452 100644 --- a/docfx/toc.yml +++ b/docfx/toc.yml @@ -1,6 +1,11 @@ +- name: Home + href: / + homepage: index.md - name: API href: api/ homepage: api/index.md +- name: DevDocs + href: docs/ - name: "|" - name: GitHub href: https://github.com/MonoMod From 97dc61c5065632bd3f90f5dffb0b2a4d8f2bb533 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 31 May 2024 06:56:30 -0500 Subject: [PATCH 102/133] More tweaks to DocFX stuff --- docfx/globalmeta.json | 1 + docfx/toc.yml | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/docfx/globalmeta.json b/docfx/globalmeta.json index 89a3adb5..928b553e 100644 --- a/docfx/globalmeta.json +++ b/docfx/globalmeta.json @@ -1,5 +1,6 @@ { "_appTitle": "MonoMod", + "_appName": "MonoMod", "_appFooter": "Yet another C# modding swiss army knife.", "_description": "A C# modding swiss army knife, powered by cecil.", "author": "0x0ade and contributors", diff --git a/docfx/toc.yml b/docfx/toc.yml index 1c7b4452..3298f5e6 100644 --- a/docfx/toc.yml +++ b/docfx/toc.yml @@ -1,12 +1,9 @@ - name: Home href: / - homepage: index.md - name: API href: api/ - homepage: api/index.md - name: DevDocs href: docs/ -- name: "|" - name: GitHub href: https://github.com/MonoMod - name: Discord From aa539609d5d22839c467d8dfdbeb7e846ad45146 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 31 May 2024 07:29:54 -0500 Subject: [PATCH 103/133] Use mref output so that xrefs work properly I'd like to use apiPage, but xrefs... --- docfx/docfx.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docfx/docfx.json b/docfx/docfx.json index c74b23f1..1f965fb9 100644 --- a/docfx/docfx.json +++ b/docfx/docfx.json @@ -22,7 +22,8 @@ "dest": "api", "disableGitFeatures": false, "disableDefaultFilter": false, - "outputFormat": "apiPage", + "shouldSkipMarkup": true, + "outputFormat": "mref", "filter": "filterConfig.yml", "categoryLayout": "nested", "namespaceLayout": "flattened", From c6c4eae0c5607c7457cb64fd49fc2de84aa8de5c Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 1 Jun 2024 03:42:02 -0500 Subject: [PATCH 104/133] Clean up docs folder for DocFX website --- MonoMod.sln | 22 ++++++++ docfx/docfx.csproj | 10 ++++ docs/Core/index.md | 8 +-- docs/Core/toc.yml | 4 +- docs/RuntimeDetour.HookGen/index.md | 4 +- docs/RuntimeDetour.HookGen/toc.yml | 4 +- docs/RuntimeDetour/implementation/index.md | 2 +- docs/RuntimeDetour/implementation/toc.yml | 4 +- docs/RuntimeDetour/index.md | 6 +-- docs/RuntimeDetour/toc.yml | 5 +- docs/Utils/index.md | 4 +- docs/Utils/toc.yml | 4 +- docs/docs.csproj | 2 + docs/index.md | 32 +++++++----- docs/toc.yml | 60 ++++++++++++---------- 15 files changed, 103 insertions(+), 68 deletions(-) create mode 100644 docfx/docfx.csproj create mode 100644 docs/docs.csproj diff --git a/MonoMod.sln b/MonoMod.sln index 7f9366e7..d7b48365 100644 --- a/MonoMod.sln +++ b/MonoMod.sln @@ -49,6 +49,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoMod.Backports.Tasks", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = ".github", ".github\.github.csproj", "{179EC228-CED4-429E-934F-422C96273F74}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "docs", "docs\docs.csproj", "{4A65448D-466F-4E87-9797-41F43787EFF3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "docfx", "docfx\docfx.csproj", "{0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -193,6 +197,22 @@ Global {179EC228-CED4-429E-934F-422C96273F74}.Release|Any CPU.Build.0 = Release|Any CPU {179EC228-CED4-429E-934F-422C96273F74}.ReleaseTrace|Any CPU.ActiveCfg = ReleaseTrace|Any CPU {179EC228-CED4-429E-934F-422C96273F74}.ReleaseTrace|Any CPU.Build.0 = ReleaseTrace|Any CPU + {4A65448D-466F-4E87-9797-41F43787EFF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4A65448D-466F-4E87-9797-41F43787EFF3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4A65448D-466F-4E87-9797-41F43787EFF3}.DebugTrace|Any CPU.ActiveCfg = DebugTrace|Any CPU + {4A65448D-466F-4E87-9797-41F43787EFF3}.DebugTrace|Any CPU.Build.0 = DebugTrace|Any CPU + {4A65448D-466F-4E87-9797-41F43787EFF3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4A65448D-466F-4E87-9797-41F43787EFF3}.Release|Any CPU.Build.0 = Release|Any CPU + {4A65448D-466F-4E87-9797-41F43787EFF3}.ReleaseTrace|Any CPU.ActiveCfg = ReleaseTrace|Any CPU + {4A65448D-466F-4E87-9797-41F43787EFF3}.ReleaseTrace|Any CPU.Build.0 = ReleaseTrace|Any CPU + {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.DebugTrace|Any CPU.ActiveCfg = DebugTrace|Any CPU + {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.DebugTrace|Any CPU.Build.0 = DebugTrace|Any CPU + {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.Release|Any CPU.Build.0 = Release|Any CPU + {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.ReleaseTrace|Any CPU.ActiveCfg = ReleaseTrace|Any CPU + {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.ReleaseTrace|Any CPU.Build.0 = ReleaseTrace|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -201,6 +221,8 @@ Global {7B682BC4-241C-4B74-8EDC-2D7677A79337} = {BCABFBF3-BB48-4C21-9A52-E140015570A9} {119A01EE-2238-423C-BE5B-48C68D7D14A7} = {69977055-8E22-4EB0-850D-6EE7D592BC04} {179EC228-CED4-429E-934F-422C96273F74} = {BCABFBF3-BB48-4C21-9A52-E140015570A9} + {4A65448D-466F-4E87-9797-41F43787EFF3} = {BCABFBF3-BB48-4C21-9A52-E140015570A9} + {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698} = {BCABFBF3-BB48-4C21-9A52-E140015570A9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E10A3D07-6D9A-4898-B95F-268636312A67} diff --git a/docfx/docfx.csproj b/docfx/docfx.csproj new file mode 100644 index 00000000..26a0a95a --- /dev/null +++ b/docfx/docfx.csproj @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/docs/Core/index.md b/docs/Core/index.md index 8ebad316..3b67f455 100644 --- a/docs/Core/index.md +++ b/docs/Core/index.md @@ -1,5 +1,5 @@ -# Core +# MonoMod.Core -* [Discussion about the CoreCLR JIT hook design](./CoreCLRJitHooks.md) -* [INativeExceptionHelper](./NativeExceptionHandler.md) -* [MonoMod.Core's Architecture](./Architecture.md) +- [Discussion about the CoreCLR JIT hook design](CoreCLRJitHooks.md) +- [`INativeExceptionHelper`](NativeExceptionHandler.md) +- [MonoMod.Core's Architecture](Architecture.md) diff --git a/docs/Core/toc.yml b/docs/Core/toc.yml index fe251a85..543d8301 100644 --- a/docs/Core/toc.yml +++ b/docs/Core/toc.yml @@ -1,7 +1,5 @@ -# This is an automatically generated file +href: index.md items: -- name: Core - href: index.md - name: Discussion about the CoreCLR JIT hook design href: CoreCLRJitHooks.md - name: INativeExceptionHelper diff --git a/docs/RuntimeDetour.HookGen/index.md b/docs/RuntimeDetour.HookGen/index.md index b2aabd41..1a64eb10 100644 --- a/docs/RuntimeDetour.HookGen/index.md +++ b/docs/RuntimeDetour.HookGen/index.md @@ -1,3 +1,3 @@ -# RuntimeDetour.HookGen +# MonoMod.RuntimeDetour.HookGen -* [Using HookGen](./Usage.md) +* [Using HookGen](Usage.md) diff --git a/docs/RuntimeDetour.HookGen/toc.yml b/docs/RuntimeDetour.HookGen/toc.yml index 0e96a2d4..f97f6084 100644 --- a/docs/RuntimeDetour.HookGen/toc.yml +++ b/docs/RuntimeDetour.HookGen/toc.yml @@ -1,6 +1,4 @@ -# This is an automatically generated file +href: index.md items: -- name: RuntimeDetour.HookGen - href: index.md - name: Using HookGen href: Usage.md diff --git a/docs/RuntimeDetour/implementation/index.md b/docs/RuntimeDetour/implementation/index.md index 986ac223..14c856b7 100644 --- a/docs/RuntimeDetour/implementation/index.md +++ b/docs/RuntimeDetour/implementation/index.md @@ -1,3 +1,3 @@ # Implementation -* [ChainHotPatching](./ChainHotPatching.md) +* [ChainHotPatching](ChainHotPatching.md) diff --git a/docs/RuntimeDetour/implementation/toc.yml b/docs/RuntimeDetour/implementation/toc.yml index f1b6b7b8..4fb1fe92 100644 --- a/docs/RuntimeDetour/implementation/toc.yml +++ b/docs/RuntimeDetour/implementation/toc.yml @@ -1,6 +1,4 @@ -# This is an automatically generated file +href: index.md items: -- name: Implementation - href: index.md - name: ChainHotPatching href: ChainHotPatching.md diff --git a/docs/RuntimeDetour/index.md b/docs/RuntimeDetour/index.md index fc79e9b4..d21d5a3f 100644 --- a/docs/RuntimeDetour/index.md +++ b/docs/RuntimeDetour/index.md @@ -1,4 +1,4 @@ -# RuntimeDetour +# MonoMod.RuntimeDetour -* [Implementation](./implementation) -* [Using RuntimeDetour](./Usage.md) +- [Implementation](implementation/index.md) +- [Using RuntimeDetour](Usage.md) diff --git a/docs/RuntimeDetour/toc.yml b/docs/RuntimeDetour/toc.yml index 5b7653de..e75c196c 100644 --- a/docs/RuntimeDetour/toc.yml +++ b/docs/RuntimeDetour/toc.yml @@ -1,8 +1,7 @@ # This is an automatically generated file +href: index.md items: -- name: RuntimeDetour - href: index.md - name: Implementation - href: implementation + href: implementation/toc.yml - name: Using RuntimeDetour href: Usage.md diff --git a/docs/Utils/index.md b/docs/Utils/index.md index fc6bfb66..4aaea02d 100644 --- a/docs/Utils/index.md +++ b/docs/Utils/index.md @@ -1,3 +1,3 @@ -# Utils +# MonoMod.Utils -* [Using ModInterop](./ModInterop.md) +* [Using ModInterop](ModInterop.md) diff --git a/docs/Utils/toc.yml b/docs/Utils/toc.yml index a625ef51..d299175f 100644 --- a/docs/Utils/toc.yml +++ b/docs/Utils/toc.yml @@ -1,6 +1,4 @@ -# This is an automatically generated file +href: index.md items: -- name: Utils - href: index.md - name: Using ModInterop href: ModInterop.md diff --git a/docs/docs.csproj b/docs/docs.csproj new file mode 100644 index 00000000..4d7b3159 --- /dev/null +++ b/docs/docs.csproj @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 5032a773..cd2614cf 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,14 +1,20 @@ -# Docs +# Conceptual Documentation Index -* [Core](./Core) -* [Debugging With MonoMod](./Debugging.md) -* [MonoMod.Backports](./README.Backports.md) -* [MonoMod.ILHelpers](./README.ILHelpers.md) -* [MonoMod.Utils](./README.Utils.md) -* [README.Core](./README.Core.md) -* [README.RuntimeDetour](./README.RuntimeDetour.md) -* [RuntimeDetour](./RuntimeDetour) -* [RuntimeDetour.HookGen](./RuntimeDetour.HookGen) -* [Switches](./Switches.md) -* [Using MonoMod.Patcher](./README.Patcher.md) -* [Utils](./Utils) +- [Debugging With MonoMod](Debugging.md) +- [MonoMod Configuration Switches](Switches.md) + +## Per-package + +- [MonoMod.Core](Core/index.md) +- [MonoMod.RuntimeDetour](RuntimeDetour/index.md) +- [MonoMod.RuntimeDetour.HookGen](RuntimeDetour.HookGen/index.md) +- [MonoMod.Utils](Utils/index.md) + +## Package READMEs + +- [MonoMod.Backports](README.Backports.md) +- [MonoMod.ILHelpers](README.ILHelpers.md) +- [MonoMod.Core](README.Core.md) +- [MonoMod.Patcher](README.Patcher.md) +- [MonoMod.RuntimeDetour](README.RuntimeDetour.md) +- [MonoMod.Utils](README.Utils.md) diff --git a/docs/toc.yml b/docs/toc.yml index 3b11b37c..42f9d788 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -1,28 +1,32 @@ -# This is an automatically generated file -items: -- name: Docs - href: index.md -- name: Core - href: Core -- name: Debugging With MonoMod - href: Debugging.md -- name: MonoMod.Backports - href: README.Backports.md -- name: MonoMod.ILHelpers - href: README.ILHelpers.md -- name: MonoMod.RuntimeDetour - href: README.RuntimeDetour.md -- name: MonoMod.Utils - href: README.Utils.md -- name: README.Core - href: README.Core.md -- name: RuntimeDetour - href: RuntimeDetour -- name: RuntimeDetour.HookGen - href: RuntimeDetour.HookGen -- name: Switches - href: Switches.md -- name: Using MonoMod.Patcher - href: README.Patcher.md -- name: Utils - href: Utils +name: Conceptual Documentation +href: index.md +items: +- name: Debugging With MonoMod + href: Debugging.md + items: + - name: Configuration Switches + href: Switches.md +- name: Per-package + items: + - name: MonoMod.Core + href: Core/toc.yml + - name: MonoMod.RuntimeDetour + href: RuntimeDetour/toc.yml + - name: MonoMod.RuntimeDetour.HookGen + href: RuntimeDetour.HookGen/toc.yml + - name: MonoMod.Utils + href: Utils/toc.yml +- name: Package READMEs + items: + - name: MonoMod.Backports + href: README.Backports.md + - name: MonoMod.ILHelpers + href: README.ILHelpers.md + - name: MonoMod.Core + href: README.Core.md + - name: MonoMod.Patcher + href: README.Patcher.md + - name: MonoMod.RuntimeDetour + href: README.RuntimeDetour.md + - name: MonoMod.Utils + href: README.Utils.md \ No newline at end of file From 11dc05063b2af284faa602dcd605c42e36444714 Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 1 Jun 2024 04:01:19 -0500 Subject: [PATCH 105/133] Minor cleanups to main README Among other things, fixes #82 --- README.md | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index a2f96376..56c8264b 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ General purpose .NET assembly modding "basework", powered by [cecil](https://git *MIT-licensed.* -[![Build status](https://img.shields.io/azure-devops/build/MonoMod/MonoMod/1.svg?style=flat-square)](https://dev.azure.com/MonoMod/MonoMod/_build/latest?definitionId=1) ![Deployment status](https://img.shields.io/azure-devops/release/MonoMod/572c97eb-dbaa-4a55-90e5-1d05431535bd/1/1.svg?style=flat-square) +[![Build and Test](https://github.com/MonoMod/MonoMod/actions/workflows/ci.yml/badge.svg)](https://github.com/MonoMod/MonoMod/actions/workflows/ci.yml) | GitHub: All | NuGet: Patcher | NuGet: Utils | NuGet: RuntimeDetour | NuGet: HookGen | |--|--|--|--|--| @@ -16,24 +16,7 @@ General purpose .NET assembly modding "basework", powered by [cecil](https://git ## Sections - [Introduction](#introduction) -- [Using MonoMod](#using-monomod) -- [Using ModInterop (ext)](/README-ModInterop.md) -- [Using RuntimeDetour & HookGen (ext)](/README-RuntimeDetour.md) -- [FAQ](#faq) - -### Special thanks to my [patrons on Patreon](https://www.patreon.com/0x0ade): -- [Chad Yates](https://twitter.com/ChadCYates) -- [Sc2ad](https://github.com/sc2ad) -- Raegous -- Chaser6 -- [Harrison Clarke](https://twitter.com/hay_guise) -- [KyleTheScientist](https://www.twitch.tv/kylethescientist) -- [Renaud Bédard](https://twitter.com/renaudbedard) -- [leo60228](https://leo60228.space) -- [Rubydragon](https://www.twitch.tv/rubydrag0n) -- Holly Magala -- [Jimmy Londo (iamdadbod)](https://www.youtube.com/iamdadbod) -- [Artus Elias Meyer-Toms](https://twitter.com/artuselias) +- [Using RuntimeDetour](docs/RuntimeDetour/Usage.md) ---- @@ -45,7 +28,7 @@ Mods / mod loaders for the following games are already using it in one way or an - Celeste: [Everest](https://everestapi.github.io/) - Risk of Rain 2: [BepInExPack (BepInEx + MonoMod + R2API)](https://thunderstore.io/package/bbepis/BepInExPack/) - Enter the Gungeon: [Mod the Gungeon](https://modthegungeon.github.io/) -- Rain World: [RainDB via Partiality](http://www.raindb.net/) +- Rain World: [RainDB via BepInEx](http://www.raindb.net/) - Totally Accurate Battle Simulator: [TABS-Multiplayer](https://github.com/Ceiridge/TABS-Multiplayer) - Salt and Sanctuary: [Salt.Modding](https://github.com/seanpr96/Salt.Modding) - Nimbatus: [Nimbatus-Mods via Partiality](https://github.com/OmegaRogue/Nimbatus-Mods) @@ -75,6 +58,16 @@ It consists of the following **modular components**: --- -### Debugging mods that use MonoMod - -See [Debugging](docs/Debugging.md). \ No newline at end of file +### Special thanks to my [patrons on Patreon](https://www.patreon.com/0x0ade): +- [Chad Yates](https://twitter.com/ChadCYates) +- [Sc2ad](https://github.com/sc2ad) +- Raegous +- Chaser6 +- [Harrison Clarke](https://twitter.com/hay_guise) +- [KyleTheScientist](https://www.twitch.tv/kylethescientist) +- [Renaud Bédard](https://twitter.com/renaudbedard) +- [leo60228](https://leo60228.space) +- [Rubydragon](https://www.twitch.tv/rubydrag0n) +- Holly Magala +- [Jimmy Londo (iamdadbod)](https://www.youtube.com/iamdadbod) +- [Artus Elias Meyer-Toms](https://twitter.com/artuselias) From 6643ea8a8808af25fd2f9a9a2beebb56f20b9dd5 Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 1 Jun 2024 04:02:42 -0500 Subject: [PATCH 106/133] Tweak inclusion rules in docfx.csproj --- docfx/docfx.csproj | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/docfx/docfx.csproj b/docfx/docfx.csproj index 26a0a95a..e41366b7 100644 --- a/docfx/docfx.csproj +++ b/docfx/docfx.csproj @@ -1,10 +1,12 @@ - - - + + + - - - + + + + + \ No newline at end of file From 449f9eb2cd7e533ed94099424f91fe34e4fbabec Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 1 Jun 2024 05:07:42 -0500 Subject: [PATCH 107/133] Add docs build + CNAME --- .github/workflows/build.yml | 1 + .github/workflows/docs.yml | 73 +++++++++++++++++++++++++++++++++++++ docfx/CNAME | 1 + docfx/docfx.json | 5 ++- 4 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/docs.yml create mode 100644 docfx/CNAME diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f208cd79..11bc83c8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,6 +64,7 @@ jobs: path: ${{ env.NUGET_PACKAGES }} key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }} restore-keys: ${{ runner.os }}-nuget-v1- + - name: Restore run: dotnet restore -bl:restore.binlog -noAutoRsp diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..e2e089f6 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,73 @@ +on: + push: + branches: + - reorganize + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + actions: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +defaults: + run: + shell: pwsh + +env: + DOTNET_TELEMETRY_OPTOUT: true + DOTNET_NOLOGO: true + NUGET_PACKAGES: ${{github.workspace}}/artifacts/pkg + +jobs: + publish-docs: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Configure git + run: | + git config --global core.autocrlf input + + - name: Checkout + uses: actions/checkout@v4 + with: + lfs: true + submodules: recursive + + - name: Install .NET SDK + uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 + with: + global-json: global.json + + # NOTE: manual package caching + - name: Cache restored NuGet packages + uses: actions/cache@v4 + with: + path: ${{ env.NUGET_PACKAGES }} + key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }} + restore-keys: ${{ runner.os }}-nuget-v1- + + - name: Restore + run: dotnet restore -bl:restore.binlog -noAutoRsp + + - name: Restore .NET tools + run: dotnet tool restore + + - name: Build DocFX project + run: dotnet docfx docfx/docfx.json + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload entire repository + path: 'docfx/_site' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/docfx/CNAME b/docfx/CNAME new file mode 100644 index 00000000..0fa7e435 --- /dev/null +++ b/docfx/CNAME @@ -0,0 +1 @@ +monomod.dev diff --git a/docfx/docfx.json b/docfx/docfx.json index 1f965fb9..eb2cf141 100644 --- a/docfx/docfx.json +++ b/docfx/docfx.json @@ -12,7 +12,7 @@ "**/MonoMod.SourceGen.*.*", "**/MonoMod.UnitTest.*", "**/MonoMod.Backports.Tasks.*", - "**/MonoMod.Patcher.*", + "**/MonoMod.Patcher.*", "**/MonoMod.ILHelpers.Patcher.*", "**/MonoMod.DebugIL.*", "**/MonoMod.RuntimeDetour.HookGen.*" @@ -59,7 +59,8 @@ "resource": [ { "files": [ - "images/**" + "images/**", + "CNAME" ] } ], From b73e33e747561d62a747255c510de229cf2e3551 Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 1 Jun 2024 05:11:46 -0500 Subject: [PATCH 108/133] Add name to docs workflow --- .github/workflows/docs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e2e089f6..56b974b6 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,3 +1,4 @@ +name: Build and push docs on: push: branches: From 62ca4038ee0283ab941475c6c07b78798215c29f Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 1 Jun 2024 05:16:47 -0500 Subject: [PATCH 109/133] Build source generators before generating DocFX site --- .github/workflows/docs.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 56b974b6..c545537a 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -61,6 +61,9 @@ jobs: - name: Restore .NET tools run: dotnet tool restore + - name: Build Source Generator projects + run: dotnet build src/MonoMod.SourceGen.Internal/MonoMod.SourceGen.Internal.csproj -c Release -noAutoRsp + - name: Build DocFX project run: dotnet docfx docfx/docfx.json From 0c09219096952c5ffe3c8cf11429fe0e619611b2 Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 1 Jun 2024 05:24:15 -0500 Subject: [PATCH 110/133] Run full (though no-analyzer) build before DocFX --- .github/workflows/docs.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c545537a..65023f8d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -61,17 +61,26 @@ jobs: - name: Restore .NET tools run: dotnet tool restore - - name: Build Source Generator projects - run: dotnet build src/MonoMod.SourceGen.Internal/MonoMod.SourceGen.Internal.csproj -c Release -noAutoRsp + - name: Build all projects + run: dotnet build -c Release -noAutoRsp -bl:build.binlog -p:RunAnalyzers=false # don't run any analyzers to speed up this build - name: Build DocFX project run: dotnet docfx docfx/docfx.json + - name: Upload binlogs + uses: actions/upload-artifact@v4 + if: ${{ always() }} + with: + name: binlogs + path: '*.binlog' + retention-days: 7 + - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: # Upload entire repository path: 'docfx/_site' + - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 From 65e06816c0667c13fdcb673c50bb57ab7d5a1b05 Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 1 Jun 2024 05:40:14 -0500 Subject: [PATCH 111/133] Fix Home link on unusual site layout --- .github/workflows/docs.yml | 2 +- docfx/toc.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 65023f8d..6aa3a5de 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,4 +1,4 @@ -name: Build and push docs +name: Docs on: push: branches: diff --git a/docfx/toc.yml b/docfx/toc.yml index 3298f5e6..3bcca430 100644 --- a/docfx/toc.yml +++ b/docfx/toc.yml @@ -1,5 +1,5 @@ - name: Home - href: / + href: index.md - name: API href: api/ - name: DevDocs From ee7d1b69a017c2f4066c77ca1cafbc63222121b2 Mon Sep 17 00:00:00 2001 From: DaNike Date: Sun, 2 Jun 2024 04:48:51 -0500 Subject: [PATCH 112/133] Tweak Backports's Span implementation to behave when Mono doesn't let you pin strings --- .github/workflows/docs.yml | 74 +++++++------- docs/RuntimeIssueNotes.md | 99 +++++++++++++++++++ docs/toc.yml | 3 +- .../Memory.cs | 2 +- .../MemoryExtensions.Portable.cs | 24 +++-- .../ReadOnlyMemory.cs | 2 +- .../ReadOnlySpan.Portable.cs | 2 +- src/MonoMod.ILHelpers/MonoMod.ILHelpers.il | 31 +++++- 8 files changed, 186 insertions(+), 51 deletions(-) create mode 100644 docs/RuntimeIssueNotes.md diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 6aa3a5de..8a344835 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -14,15 +14,15 @@ permissions: # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. concurrency: group: "pages" - cancel-in-progress: false - -defaults: - run: - shell: pwsh - -env: - DOTNET_TELEMETRY_OPTOUT: true - DOTNET_NOLOGO: true + cancel-in-progress: false + +defaults: + run: + shell: pwsh + +env: + DOTNET_TELEMETRY_OPTOUT: true + DOTNET_NOLOGO: true NUGET_PACKAGES: ${{github.workspace}}/artifacts/pkg jobs: @@ -31,40 +31,40 @@ jobs: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest - steps: - - name: Configure git - run: | - git config --global core.autocrlf input - - - name: Checkout - uses: actions/checkout@v4 - with: - lfs: true - submodules: recursive - - - name: Install .NET SDK - uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 - with: - global-json: global.json - - # NOTE: manual package caching - - name: Cache restored NuGet packages - uses: actions/cache@v4 - with: - path: ${{ env.NUGET_PACKAGES }} - key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }} - restore-keys: ${{ runner.os }}-nuget-v1- - - - name: Restore + steps: + - name: Configure git + run: | + git config --global core.autocrlf input + + - name: Checkout + uses: actions/checkout@v4 + with: + lfs: true + submodules: recursive + + - name: Install .NET SDK + uses: nike4613/install-dotnet@54b402247e474b39b84891b9093d8025892c8b47 + with: + global-json: global.json + + # NOTE: manual package caching + - name: Cache restored NuGet packages + uses: actions/cache@v4 + with: + path: ${{ env.NUGET_PACKAGES }} + key: ${{ runner.os }}-nuget-v1-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', 'nuget.config', 'global.json') }} + restore-keys: ${{ runner.os }}-nuget-v1- + + - name: Restore run: dotnet restore -bl:restore.binlog -noAutoRsp - - name: Restore .NET tools - run: dotnet tool restore + - name: Restore .NET tools + run: dotnet tool restore - name: Build all projects run: dotnet build -c Release -noAutoRsp -bl:build.binlog -p:RunAnalyzers=false # don't run any analyzers to speed up this build - - name: Build DocFX project + - name: Build DocFX project run: dotnet docfx docfx/docfx.json - name: Upload binlogs diff --git a/docs/RuntimeIssueNotes.md b/docs/RuntimeIssueNotes.md new file mode 100644 index 00000000..435e9bba --- /dev/null +++ b/docs/RuntimeIssueNotes.md @@ -0,0 +1,99 @@ +# Notes on issues in various runtime versions + +## `fixed` on strings in old Mono + +Some old versions of Mono have broken `conv.u` instruction handling. + +The following code will crash those old versions with an assert in the JIT's local propagation routine: + +```cs +fixed (char* pStr = "some string") +{ + // ... +} +``` + +This is because the sequence that Roslyn emits for `fixed` over a string is this: + +``` + .locals ( + string pinned stringLocalMarkedPinned, + char* ptrLocal + ) + + // ... + + // load string object onto the stack... + stloc stringLocalMarkedPinned + ldloc stringLocalMarkedPinned + conv.u + stloc ptrLocal + ldloc ptrLocal + brfalse.s PTR_NULL + + ldloc ptrLocal + call int32 [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::get_OffsetToStringData() + add + stloc ptrLocal + +PTR_NULL: + // code using the pointer in ptrLocal +``` + +Importantly, this sequence uses `conv.u` on a string object to convert a value of type `O` to a value of type `U` +(a.k.a. `nuint` or `UIntPtr`), giving the address of the object, then uses `RuntimeHelpers.OffsetToStringData` to +offset that to the start of the string data. + +New runtimes expose `GetPinnableReference()` on `string`, and if it's present, Roslyn will use that instead. However, +the versions of Mono that this is a problem with (such as the version Unity 5.x uses) expose the .NET 3.5 API surface +which does *not* include `GetPinnableReference()`. + +This fails because the JIT's importer has an incomplete implementation of `conv.u`. + +The relevant code is in `mini/method-to-ir.c`, in the `type_from_op` function: + +```c +// ... +switch (ins->opcode) { +// ... +case CEE_CONV_U: + ins->type = STACK_PTR; + switch (src1->type) { + case STACK_I4: + ins->opcode = OP_ICONV_TO_U; + break; + case STACK_PTR: + case STACK_MP: +#if SIZEOF_REGISTER == 8 + ins->opcode = OP_LCONV_TO_U; +#else + ins->opcode = OP_MOVE; +#endif + break; + case STACK_I8: + ins->opcode = OP_LCONV_TO_U; + break; + case STACK_R8: + ins->opcode = OP_FCONV_TO_U; + break; + } + break; +// ... +} +// ... +``` + +In the problematic case, `src1->type` is `STACK_OBJ`, which is not handled, and so `ins->opcode` remains the IL +opcode for `conv.u`, as opposed to a Mono IR opcode. This then causes an assertion failure in `mini/local-propagation.c` +in `mono_local_cprop`: + +```c +g_assert (ins->opcode > MONO_CEE_LAST); +``` + +Which, of course, fails, because `ins->opcode` is still `CEE_CONV_U`, which is less than `MONO_CEE_LAST`. + +### Workarounds + +1. Use an array, like `new char[] { /* ... */ }`. +2. Convert the string to a `ReadOnlySpan` first, like `str.AsSpan()`. diff --git a/docs/toc.yml b/docs/toc.yml index 42f9d788..671fd9d6 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -29,4 +29,5 @@ items: - name: MonoMod.RuntimeDetour href: README.RuntimeDetour.md - name: MonoMod.Utils - href: README.Utils.md \ No newline at end of file + href: README.Utils.md +- href: RuntimeIssueNotes.md \ No newline at end of file diff --git a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Memory.cs b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Memory.cs index f307c323..b6b92ed4 100644 --- a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Memory.cs +++ b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Memory.cs @@ -279,7 +279,7 @@ public Span Span // and then cast to a Memory. Such a cast can only be done with unsafe or marshaling code, // in which case that's the dangerous operation performed by the dev, and we're just following // suit here to make it work as best as possible. - return new Span(Unsafe.As>(s), MemoryExtensions.StringAdjustment, s.Length).Slice(_index, _length); + return new Span(Unsafe.As>(s), MemoryExtensions.StringAdjustmentHolder.StringAdjustment, s.Length).Slice(_index, _length); } else if (_object != null) { diff --git a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/MemoryExtensions.Portable.cs b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/MemoryExtensions.Portable.cs index 9c065649..a0bd5a7a 100644 --- a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/MemoryExtensions.Portable.cs +++ b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/MemoryExtensions.Portable.cs @@ -278,7 +278,7 @@ public static ReadOnlySpan AsSpan(this string? text) if (text == null) return default; - return new ReadOnlySpan(Unsafe.As>(text), StringAdjustment, text.Length); + return new ReadOnlySpan(Unsafe.As>(text), StringAdjustmentHolder.StringAdjustment, text.Length); } ///

@@ -302,7 +302,7 @@ public static ReadOnlySpan AsSpan(this string? text, int start) if ((uint)start > (uint)text.Length) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); - return new ReadOnlySpan(Unsafe.As>(text), StringAdjustment + start * sizeof(char), text.Length - start); + return new ReadOnlySpan(Unsafe.As>(text), StringAdjustmentHolder.StringAdjustment + start * sizeof(char), text.Length - start); } /// @@ -327,7 +327,7 @@ public static ReadOnlySpan AsSpan(this string? text, int start, int length if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start)) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); - return new ReadOnlySpan(Unsafe.As>(text), StringAdjustment + start * sizeof(char), length); + return new ReadOnlySpan(Unsafe.As>(text), StringAdjustmentHolder.StringAdjustment + start * sizeof(char), length); } /// Creates a new over the portion of the target string. @@ -384,16 +384,22 @@ public static ReadOnlyMemory AsMemory(this string? text, int start, int le return new ReadOnlyMemory(text, start, length); } - internal static readonly nint StringAdjustment = MeasureStringAdjustment(); - private static nint MeasureStringAdjustment() + internal static class StringAdjustmentHolder { - string sampleString = "a"; - unsafe + internal static readonly nint StringAdjustment = MeasureStringAdjustment(); + + private static nint MeasureStringAdjustment() { - fixed (char* pSampleString = sampleString) + string sampleString = "a"; + unsafe { - return Unsafe.ByteOffset(ref Unsafe.As>(sampleString).Data, ref Unsafe.AsRef(pSampleString)); + // NOTE: On some old versions of Mono, pinning strings doesn't work, and hard crashes the runtime with an assert. (See docs/RuntimeIssueNodes.md) + // We therefore use a somewhat unusual method of working out the right string adjustment: we have an IL-implemented method for measuring the difference + // between the start of the object and the start of the object data, as measured relative to Pinnable.Data. We can then subtract this amount from + // the normal RuntimeHelpers.OffsetToStringData to get the correct string adjustment. + var offset = (nint)ILHelpers.GetRawDataOffset(sampleString, ref Unsafe.As>(sampleString).Data); + return RuntimeHelpers.OffsetToStringData - offset; } } } diff --git a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlyMemory.cs b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlyMemory.cs index 10178db3..5fbed302 100644 --- a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlyMemory.cs +++ b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlyMemory.cs @@ -196,7 +196,7 @@ public ReadOnlySpan Span else if (typeof(T) == typeof(char) && _object is string s) { Debug.Assert(_length >= 0); - return new ReadOnlySpan(Unsafe.As>(s), MemoryExtensions.StringAdjustment, s.Length).Slice(_index, _length); + return new ReadOnlySpan(Unsafe.As>(s), MemoryExtensions.StringAdjustmentHolder.StringAdjustment, s.Length).Slice(_index, _length); } else if (_object != null) { diff --git a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlySpan.Portable.cs b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlySpan.Portable.cs index 50b15cb3..449171c8 100644 --- a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlySpan.Portable.cs +++ b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlySpan.Portable.cs @@ -205,7 +205,7 @@ public override string ToString() if (typeof(T) == typeof(char)) { // If this wraps a string and represents the full length of the string, just return the wrapped string. - if (_byteOffset == MemoryExtensions.StringAdjustment) + if (_byteOffset == MemoryExtensions.StringAdjustmentHolder.StringAdjustment) { object? obj = Unsafe.As(_pinnable); // minimize chances the compilers will optimize away the 'is' check if (obj is string str && _length == str.Length) diff --git a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il index 8a76abc2..55508397 100644 --- a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il +++ b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il @@ -32,10 +32,39 @@ ret } + // This exists to work around the fact that on old Mono, the normal string pinning sequence is broken. + // See docs/RuntimeIssueNodes.md + .method public hidebysig static native int GetRawDataOffset(object obj, uint8& rawData) cil managed aggressiveinlining + { + .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 01 00 00 + ) + + .maxstack 8 + .locals init ( + object pinned str, + void*& ptrRef + ) + + ldarg.1 + conv.u + + ldarg.0 + stloc str + ldloca str + conv.u + stloc ptrRef + ldloc ptrRef + ldind.i + sub + + ret + } + .method public hidebysig static !!T& UnboxAnyUnsafe(object& 'box') cil managed aggressiveinlining { .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( - 01 00 02 00 00 + 01 00 02 00 00 ) .maxstack 8 From 238c9479552d76d6d8f77732dbdac1b3405ee77a Mon Sep 17 00:00:00 2001 From: DaNike Date: Sun, 2 Jun 2024 17:44:37 -0500 Subject: [PATCH 113/133] Add analyzer for string pinning issue --- external/iced.props | 2 +- .../AnalyzerReleases.Unshipped.md | 3 +- .../Analyzers/DoNotPinStrings.cs | 66 +++++++++++++++++++ tools/Common.CS.targets | 6 +- 4 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 src/MonoMod.SourceGen.Internal/Analyzers/DoNotPinStrings.cs diff --git a/external/iced.props b/external/iced.props index 73226c4f..b6f7645d 100644 --- a/external/iced.props +++ b/external/iced.props @@ -10,7 +10,7 @@ true false true - false + <_MMTargetFrameworks>$(TargetFrameworks) <_MMDefineConsts>$(DefineConstants) diff --git a/src/MonoMod.SourceGen.Internal/AnalyzerReleases.Unshipped.md b/src/MonoMod.SourceGen.Internal/AnalyzerReleases.Unshipped.md index d9dd9bf2..41c50d0f 100644 --- a/src/MonoMod.SourceGen.Internal/AnalyzerReleases.Unshipped.md +++ b/src/MonoMod.SourceGen.Internal/AnalyzerReleases.Unshipped.md @@ -8,4 +8,5 @@ Rule ID | Category | Severity | Notes MM.ILOverload.BadKind | | Error | ILOverloadGenerator MM.ILOverload.NoFile | | Error | ILOverloadGenerator MM0001 | Build | Warning | AssemblyInfoGenerator -MM0002 | Build | Warning | AssemblyInfoGenerator \ No newline at end of file +MM0002 | Build | Warning | AssemblyInfoGenerator +MMA001 | Stability | Error | DoNotPinStrings \ No newline at end of file diff --git a/src/MonoMod.SourceGen.Internal/Analyzers/DoNotPinStrings.cs b/src/MonoMod.SourceGen.Internal/Analyzers/DoNotPinStrings.cs new file mode 100644 index 00000000..516a0f82 --- /dev/null +++ b/src/MonoMod.SourceGen.Internal/Analyzers/DoNotPinStrings.cs @@ -0,0 +1,66 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; +using System.Collections.Immutable; +using System.Linq; + +namespace MonoMod.SourceGen.Internal.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class DoNotPinStrings : DiagnosticAnalyzer + { + private static readonly DiagnosticDescriptor PinningStringsIsDangerous = new( + "MMA001", + "Do not pin strings, as it may crash some older Mono runtimes", + "Do not pin strings, as it may crash some older Mono runtimes (see docs/RuntimeIssueNotes.md). Pin a span instead.", + "Stability", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(PinningStringsIsDangerous); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", + Justification = "Roslyn always passes a non-null context")] + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics); // if generated code pins strings, we still want to report it + + context.RegisterOperationAction(ctx => + { + var op = (IVariableDeclaratorOperation)ctx.Operation; + + if (!op.Symbol.IsFixed) + { + // we only care about fixed variables + return; + } + + var initializer = op.GetVariableInitializer()?.Value; + if (initializer is null) + { + // no initializer, nothing to do + return; + } + + if (initializer.IsImplicit) + { + initializer = initializer.ChildOperations.Any() ? initializer.ChildOperations.First() : null; + } + + if (initializer is null || initializer.Type is null) + { + // no initializer, nothing to do + return; + } + + if (initializer.Type.SpecialType is SpecialType.System_String) + { + // the initializer of the fixed variable is a string, report it + ctx.ReportDiagnostic(Diagnostic.Create(PinningStringsIsDangerous, initializer.Syntax.GetLocation())); + } + + }, OperationKind.VariableDeclarator); + } + } +} diff --git a/tools/Common.CS.targets b/tools/Common.CS.targets index 1b51c87a..4eccb1bc 100644 --- a/tools/Common.CS.targets +++ b/tools/Common.CS.targets @@ -48,8 +48,12 @@ Shared/%(Filename)%(Extension) + + + - + false false From c3d34ef2681b82839f2acac35eeadc6db5d511ec Mon Sep 17 00:00:00 2001 From: DaNike Date: Sun, 2 Jun 2024 17:59:14 -0500 Subject: [PATCH 114/133] Fix string pins --- src/MonoMod.SourceGen.Internal/Analyzers/DoNotPinStrings.cs | 1 - src/MonoMod.Utils/DynDll.Backend.cs | 2 +- src/MonoMod.Utils/PlatformDetection.cs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/MonoMod.SourceGen.Internal/Analyzers/DoNotPinStrings.cs b/src/MonoMod.SourceGen.Internal/Analyzers/DoNotPinStrings.cs index 516a0f82..df9fdb9f 100644 --- a/src/MonoMod.SourceGen.Internal/Analyzers/DoNotPinStrings.cs +++ b/src/MonoMod.SourceGen.Internal/Analyzers/DoNotPinStrings.cs @@ -2,7 +2,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; using System.Collections.Immutable; -using System.Linq; namespace MonoMod.SourceGen.Internal.Analyzers { diff --git a/src/MonoMod.Utils/DynDll.Backend.cs b/src/MonoMod.Utils/DynDll.Backend.cs index d47bd478..c8a3f548 100644 --- a/src/MonoMod.Utils/DynDll.Backend.cs +++ b/src/MonoMod.Utils/DynDll.Backend.cs @@ -181,7 +181,7 @@ protected override unsafe bool TryOpenLibraryCore(string? name, Assembly assembl } else { - fixed (char* pName = name) + fixed (char* pName = name.AsSpan()) { handle = result = Interop.Windows.LoadLibraryW((ushort*)pName); } diff --git a/src/MonoMod.Utils/PlatformDetection.cs b/src/MonoMod.Utils/PlatformDetection.cs index fc3e5a9f..d7eff378 100644 --- a/src/MonoMod.Utils/PlatformDetection.cs +++ b/src/MonoMod.Utils/PlatformDetection.cs @@ -398,7 +398,7 @@ private static unsafe bool CheckWine() if (env == "FALSE") return false; - fixed (char* pNtdll = "ntdll.dll") + fixed (char* pNtdll = "ntdll.dll".AsSpan()) { var ntdll = Interop.Windows.GetModuleHandleW((ushort*)pNtdll); if (ntdll != Interop.Windows.HMODULE.NULL && ntdll != Interop.Windows.HMODULE.INVALID_VALUE) From 2621383ce3a01a4bc6a47432e631234ddc47cb1c Mon Sep 17 00:00:00 2001 From: DaNike Date: Sun, 2 Jun 2024 18:00:21 -0500 Subject: [PATCH 115/133] Bump versions --- src/MonoMod.Backports/MonoMod.Backports.csproj | 2 +- src/MonoMod.Core/MonoMod.Core.csproj | 2 +- src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj | 2 +- src/MonoMod.RuntimeDetour/MonoMod.RuntimeDetour.csproj | 2 +- src/MonoMod.Utils/MonoMod.Utils.csproj | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/MonoMod.Backports/MonoMod.Backports.csproj b/src/MonoMod.Backports/MonoMod.Backports.csproj index 37f385b9..12048f68 100644 --- a/src/MonoMod.Backports/MonoMod.Backports.csproj +++ b/src/MonoMod.Backports/MonoMod.Backports.csproj @@ -10,7 +10,7 @@ false - 1.1.1 + 1.1.2 1.1.0 diff --git a/src/MonoMod.Core/MonoMod.Core.csproj b/src/MonoMod.Core/MonoMod.Core.csproj index 4709cf27..3235adbb 100644 --- a/src/MonoMod.Core/MonoMod.Core.csproj +++ b/src/MonoMod.Core/MonoMod.Core.csproj @@ -12,7 +12,7 @@ RuntimeDetour;detour;detours;$(PackageTags) - 1.1.1 + 1.1.2 1.0.0 true diff --git a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj index fa892d36..d7f6bf36 100644 --- a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj +++ b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj @@ -4,7 +4,7 @@ A collection of IL helpers for MonoMod, including a backport of System.Runtime.CompilerServices.Unsafe. - 1.0.1 + 1.1.0 1.0.0 diff --git a/src/MonoMod.RuntimeDetour/MonoMod.RuntimeDetour.csproj b/src/MonoMod.RuntimeDetour/MonoMod.RuntimeDetour.csproj index ad47ec12..6caf5eb9 100644 --- a/src/MonoMod.RuntimeDetour/MonoMod.RuntimeDetour.csproj +++ b/src/MonoMod.RuntimeDetour/MonoMod.RuntimeDetour.csproj @@ -13,7 +13,7 @@ true - 25.1.1 + 25.1.2 25.0.0 diff --git a/src/MonoMod.Utils/MonoMod.Utils.csproj b/src/MonoMod.Utils/MonoMod.Utils.csproj index 8df1074a..84c5a57d 100644 --- a/src/MonoMod.Utils/MonoMod.Utils.csproj +++ b/src/MonoMod.Utils/MonoMod.Utils.csproj @@ -9,7 +9,7 @@ enable false - 25.0.5 + 25.0.6 25.0.0 From a328cf86fd7dc5c56cf8d2ea65f7b3eeff22baea Mon Sep 17 00:00:00 2001 From: DaNike Date: Sun, 2 Jun 2024 18:31:15 -0500 Subject: [PATCH 116/133] Allow RuntimeType to not be present --- src/MonoMod.Utils/ReflectionHelper.FixReflectionCache.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/MonoMod.Utils/ReflectionHelper.FixReflectionCache.cs b/src/MonoMod.Utils/ReflectionHelper.FixReflectionCache.cs index b887663b..772dd288 100644 --- a/src/MonoMod.Utils/ReflectionHelper.FixReflectionCache.cs +++ b/src/MonoMod.Utils/ReflectionHelper.FixReflectionCache.cs @@ -7,18 +7,17 @@ namespace MonoMod.Utils { public static partial class ReflectionHelper { - // .NET Framework can break member ordering if using Module.Resolve* on certain members. private static readonly object?[] _CacheGetterArgs = { /* MemberListType.All */ 0, /* name apparently always null? */ null }; - private static Type t_RuntimeType = + // Note: on older Mono, RuntimeType doesn't exist. That's fine; it means there's no cache to fix. + private static Type? t_RuntimeType = typeof(Type).Assembly - .GetType("System.RuntimeType") - ?? throw new InvalidOperationException("Could not find RuntimeType"); + .GetType("System.RuntimeType"); private static Type? t_RuntimeTypeCache = - t_RuntimeType.GetNestedType("RuntimeTypeCache", BindingFlags.Public | BindingFlags.NonPublic); + t_RuntimeType?.GetNestedType("RuntimeTypeCache", BindingFlags.Public | BindingFlags.NonPublic); private static PropertyInfo? p_RuntimeType_Cache = t_RuntimeTypeCache == null ? null : From b68646ab99a02d19bfb06db5dd1e0a359e7b5d83 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 3 Jun 2024 22:02:33 -0500 Subject: [PATCH 117/133] Remove Unsafe.As UB in {,ReadOnly}Span backports --- .../Memory.cs | 2 +- .../MemoryExtensions.Portable.cs | 26 ++------- .../Pinnable.cs | 19 ------- .../ReadOnlyMemory.cs | 2 +- .../ReadOnlySpan.Portable.cs | 34 ++++-------- .../InteropServices/MemoryMarshal.Portable.cs | 24 +++------ .../Span.Portable.cs | 53 ++++++------------- .../SpanHelpers.Portable.cs | 2 +- src/MonoMod.ILHelpers/MonoMod.ILHelpers.il | 38 +++++-------- 9 files changed, 51 insertions(+), 149 deletions(-) delete mode 100644 src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Pinnable.cs diff --git a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Memory.cs b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Memory.cs index b6b92ed4..114b4f07 100644 --- a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Memory.cs +++ b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Memory.cs @@ -279,7 +279,7 @@ public Span Span // and then cast to a Memory. Such a cast can only be done with unsafe or marshaling code, // in which case that's the dangerous operation performed by the dev, and we're just following // suit here to make it work as best as possible. - return new Span(Unsafe.As>(s), MemoryExtensions.StringAdjustmentHolder.StringAdjustment, s.Length).Slice(_index, _length); + return new Span(s, (nint)RuntimeHelpers.OffsetToStringData, s.Length).Slice(_index, _length); } else if (_object != null) { diff --git a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/MemoryExtensions.Portable.cs b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/MemoryExtensions.Portable.cs index a0bd5a7a..94ea033b 100644 --- a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/MemoryExtensions.Portable.cs +++ b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/MemoryExtensions.Portable.cs @@ -278,7 +278,7 @@ public static ReadOnlySpan AsSpan(this string? text) if (text == null) return default; - return new ReadOnlySpan(Unsafe.As>(text), StringAdjustmentHolder.StringAdjustment, text.Length); + return new ReadOnlySpan(text, (nint)RuntimeHelpers.OffsetToStringData, text.Length); } /// @@ -302,7 +302,7 @@ public static ReadOnlySpan AsSpan(this string? text, int start) if ((uint)start > (uint)text.Length) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); - return new ReadOnlySpan(Unsafe.As>(text), StringAdjustmentHolder.StringAdjustment + start * sizeof(char), text.Length - start); + return new ReadOnlySpan(text, (nint)RuntimeHelpers.OffsetToStringData + start * sizeof(char), text.Length - start); } /// @@ -327,7 +327,7 @@ public static ReadOnlySpan AsSpan(this string? text, int start, int length if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start)) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); - return new ReadOnlySpan(Unsafe.As>(text), StringAdjustmentHolder.StringAdjustment + start * sizeof(char), length); + return new ReadOnlySpan(text, (nint)RuntimeHelpers.OffsetToStringData + start * sizeof(char), length); } /// Creates a new over the portion of the target string. @@ -383,25 +383,5 @@ public static ReadOnlyMemory AsMemory(this string? text, int start, int le return new ReadOnlyMemory(text, start, length); } - - - internal static class StringAdjustmentHolder - { - internal static readonly nint StringAdjustment = MeasureStringAdjustment(); - - private static nint MeasureStringAdjustment() - { - string sampleString = "a"; - unsafe - { - // NOTE: On some old versions of Mono, pinning strings doesn't work, and hard crashes the runtime with an assert. (See docs/RuntimeIssueNodes.md) - // We therefore use a somewhat unusual method of working out the right string adjustment: we have an IL-implemented method for measuring the difference - // between the start of the object and the start of the object data, as measured relative to Pinnable.Data. We can then subtract this amount from - // the normal RuntimeHelpers.OffsetToStringData to get the correct string adjustment. - var offset = (nint)ILHelpers.GetRawDataOffset(sampleString, ref Unsafe.As>(sampleString).Data); - return RuntimeHelpers.OffsetToStringData - offset; - } - } - } } } \ No newline at end of file diff --git a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Pinnable.cs b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Pinnable.cs deleted file mode 100644 index 84e7e1b5..00000000 --- a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Pinnable.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - -namespace System -{ - // - // This class exists solely so that arbitrary objects can be Unsafe-casted to it to get a ref to the start of the user data. - // - [StructLayout(LayoutKind.Sequential)] - [SuppressMessage("Performance", "CA1812", Justification = "Objects are unsafe-casted to this to be stored in Memory and Span")] - internal sealed class Pinnable - { - public T Data = default!; - } -} \ No newline at end of file diff --git a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlyMemory.cs b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlyMemory.cs index 5fbed302..c4801fee 100644 --- a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlyMemory.cs +++ b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlyMemory.cs @@ -196,7 +196,7 @@ public ReadOnlySpan Span else if (typeof(T) == typeof(char) && _object is string s) { Debug.Assert(_length >= 0); - return new ReadOnlySpan(Unsafe.As>(s), MemoryExtensions.StringAdjustmentHolder.StringAdjustment, s.Length).Slice(_index, _length); + return new ReadOnlySpan(s, (nint)RuntimeHelpers.OffsetToStringData, s.Length).Slice(_index, _length); } else if (_object != null) { diff --git a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlySpan.Portable.cs b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlySpan.Portable.cs index 449171c8..282f9c4c 100644 --- a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlySpan.Portable.cs +++ b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlySpan.Portable.cs @@ -33,7 +33,7 @@ public ReadOnlySpan(T[]? array) } _length = array.Length; - _pinnable = Unsafe.As>(array); + _pinnable = array; _byteOffset = SpanHelpers.PerTypeValues.ArrayAdjustment; } @@ -63,7 +63,7 @@ public ReadOnlySpan(T[]? array, int start, int length) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); _length = length; - _pinnable = Unsafe.As>(array); + _pinnable = array; _byteOffset = SpanHelpers.PerTypeValues.ArrayAdjustment.Add(start); } @@ -97,7 +97,7 @@ public unsafe ReadOnlySpan(void* pointer, int length) // Constructor for internal use only. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ReadOnlySpan(Pinnable? pinnable, IntPtr byteOffset, int length) + internal ReadOnlySpan(object? pinnable, IntPtr byteOffset, int length) { Debug.Assert(length >= 0); @@ -122,10 +122,7 @@ public ref readonly T this[int index] if ((uint)index >= ((uint)_length)) ThrowHelper.ThrowIndexOutOfRangeException(); - if (_pinnable == null) - unsafe { return ref Unsafe.Add(ref Unsafe.AsRef(_byteOffset.ToPointer()), index); } - else - return ref Unsafe.Add(ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset), index); + return ref Unsafe.Add(ref DangerousGetPinnableReference(), index); } } @@ -138,11 +135,7 @@ public unsafe ref readonly T GetPinnableReference() { if (_length != 0) { - if (_pinnable == null) - { - return ref Unsafe.AsRef(_byteOffset.ToPointer()); - } - return ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset); + return ref DangerousGetPinnableReference(); } return ref Unsafe.AsRef(null); } @@ -205,7 +198,7 @@ public override string ToString() if (typeof(T) == typeof(char)) { // If this wraps a string and represents the full length of the string, just return the wrapped string. - if (_byteOffset == MemoryExtensions.StringAdjustmentHolder.StringAdjustment) + if (_byteOffset == (nint)RuntimeHelpers.OffsetToStringData) { object? obj = Unsafe.As(_pinnable); // minimize chances the compilers will optimize away the 'is' check if (obj is string str && _length == str.Length) @@ -276,22 +269,15 @@ public T[] ToArray() } /// - /// This method is obsolete, use System.Runtime.InteropServices.MemoryMarshal.GetReference instead. /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [EditorBrowsable(EditorBrowsableState.Never)] - internal ref T DangerousGetPinnableReference() - { - if (_pinnable == null) - unsafe { return ref Unsafe.AsRef(_byteOffset.ToPointer()); } - else - return ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset); - } + internal ref T DangerousGetPinnableReference() => + ref Unsafe.Add(ref ILHelpers.ObjectAsRef(_pinnable), _byteOffset); // These expose the internal representation for Span-related apis use only. - internal Pinnable? Pinnable => _pinnable; + internal object? Pinnable => _pinnable; internal IntPtr ByteOffset => _byteOffset; // @@ -305,7 +291,7 @@ internal ref T DangerousGetPinnableReference() // _pinnable = null // _byteOffset = the pointer // - private readonly Pinnable? _pinnable; + private readonly object? _pinnable; private readonly IntPtr _byteOffset; private readonly int _length; } diff --git a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Runtime/InteropServices/MemoryMarshal.Portable.cs b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Runtime/InteropServices/MemoryMarshal.Portable.cs index 738a70ab..55757ca7 100644 --- a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Runtime/InteropServices/MemoryMarshal.Portable.cs +++ b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Runtime/InteropServices/MemoryMarshal.Portable.cs @@ -31,7 +31,7 @@ public static Span AsBytes(Span span) ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T)); int newLength = checked(span.Length * Unsafe.SizeOf()); - return new Span(Unsafe.As>(span.Pinnable), span.ByteOffset, newLength); + return new Span(span.Pinnable, span.ByteOffset, newLength); } /// @@ -53,7 +53,7 @@ public static ReadOnlySpan AsBytes(ReadOnlySpan span) ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T)); int newLength = checked(span.Length * Unsafe.SizeOf()); - return new ReadOnlySpan(Unsafe.As>(span.Pinnable), span.ByteOffset, newLength); + return new ReadOnlySpan(span.Pinnable, span.ByteOffset, newLength); } /// Creates a from a . @@ -72,25 +72,13 @@ public static Memory AsMemory(ReadOnlyMemory memory) => /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. /// - public static ref T GetReference(Span span) - { - if (span.Pinnable == null) - unsafe { return ref Unsafe.AsRef(span.ByteOffset.ToPointer()); } - else - return ref Unsafe.AddByteOffset(ref span.Pinnable.Data, span.ByteOffset); - } + public static ref T GetReference(Span span) => ref span.DangerousGetPinnableReference(); /// /// Returns a reference to the 0th element of the ReadOnlySpan. If the Span is empty, returns a reference to the location where the 0th element /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. /// - public static ref T GetReference(ReadOnlySpan span) - { - if (span.Pinnable == null) - unsafe { return ref Unsafe.AsRef(span.ByteOffset.ToPointer()); } - else - return ref Unsafe.AddByteOffset(ref span.Pinnable.Data, span.ByteOffset); - } + public static ref T GetReference(ReadOnlySpan span) => ref Unsafe.AsRef(in span.GetPinnableReference()); /// /// Casts a Span of one primitive type to another primitive type . @@ -114,7 +102,7 @@ public static Span Cast(Span span) ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(TTo)); int newLength = checked((int)((long)span.Length * Unsafe.SizeOf() / Unsafe.SizeOf())); - return new Span(Unsafe.As>(span.Pinnable), span.ByteOffset, newLength); + return new Span(span.Pinnable, span.ByteOffset, newLength); } /// @@ -139,7 +127,7 @@ public static ReadOnlySpan Cast(ReadOnlySpan span) ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(TTo)); int newLength = checked((int)((long)span.Length * Unsafe.SizeOf() / Unsafe.SizeOf())); - return new ReadOnlySpan(Unsafe.As>(span.Pinnable), span.ByteOffset, newLength); + return new ReadOnlySpan(span.Pinnable, span.ByteOffset, newLength); } } } \ No newline at end of file diff --git a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Span.Portable.cs b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Span.Portable.cs index a9451b22..c7eb29c9 100644 --- a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Span.Portable.cs +++ b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/Span.Portable.cs @@ -35,7 +35,7 @@ public Span(T[]? array) ThrowHelper.ThrowArrayTypeMismatchException(); _length = array.Length; - _pinnable = Unsafe.As>(array); + _pinnable = array; _byteOffset = SpanHelpers.PerTypeValues.ArrayAdjustment; } @@ -58,7 +58,7 @@ internal static Span Create(T[]? array, int start) IntPtr byteOffset = SpanHelpers.PerTypeValues.ArrayAdjustment.Add(start); int length = array.Length - start; - return new Span(pinnable: Unsafe.As>(array), byteOffset: byteOffset, length: length); + return new Span(pinnable: array, byteOffset: byteOffset, length: length); } /// @@ -89,7 +89,7 @@ public Span(T[]? array, int start, int length) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); _length = length; - _pinnable = Unsafe.As>(array); + _pinnable = array; _byteOffset = SpanHelpers.PerTypeValues.ArrayAdjustment.Add(start); } @@ -123,7 +123,7 @@ public unsafe Span(void* pointer, int length) // Constructor for internal use only. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Span(Pinnable? pinnable, IntPtr byteOffset, int length) + internal Span(object? pinnable, IntPtr byteOffset, int length) { Debug.Assert(length >= 0); @@ -148,10 +148,7 @@ public ref T this[int index] if ((uint)index >= ((uint)_length)) ThrowHelper.ThrowIndexOutOfRangeException(); - if (_pinnable == null) - unsafe { return ref Unsafe.Add(ref Unsafe.AsRef(_byteOffset.ToPointer()), index); } - else - return ref Unsafe.Add(ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset), index); + return ref Unsafe.Add(ref DangerousGetPinnableReference(), index); } } @@ -160,17 +157,14 @@ public ref T this[int index] /// It can be used for pinning and is required to support the use of span within a fixed statement. /// [EditorBrowsable(EditorBrowsableState.Never)] - public unsafe ref T GetPinnableReference() + public ref T GetPinnableReference() { if (_length != 0) { - if (_pinnable == null) - { - return ref Unsafe.AsRef(_byteOffset.ToPointer()); - } - return ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset); + return ref DangerousGetPinnableReference(); } - return ref Unsafe.AsRef(null); + + return ref Unsafe.NullRef(); } /// @@ -195,9 +189,7 @@ public unsafe void Clear() } else { - ref byte b = ref Unsafe.As(ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset)); - - SpanHelpers.ClearLessThanPointerSized(ref b, byteLength); + SpanHelpers.ClearLessThanPointerSized(ref Unsafe.As(ref DangerousGetPinnableReference()), byteLength); } } else @@ -232,15 +224,7 @@ public unsafe void Fill(T value) if (Unsafe.SizeOf() == 1) { byte fill = Unsafe.As(ref value); - if (_pinnable == null) - { - Unsafe.InitBlockUnaligned(_byteOffset.ToPointer(), fill, (uint)length); - } - else - { - ref byte r = ref Unsafe.As(ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset)); - Unsafe.InitBlockUnaligned(ref r, fill, (uint)length); - } + Unsafe.InitBlockUnaligned(ref Unsafe.As(ref DangerousGetPinnableReference()), fill, (uint)length); } else { @@ -401,22 +385,15 @@ public T[] ToArray() } /// - /// This method is obsolete, use System.Runtime.InteropServices.MemoryMarshal.GetReference instead. /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [EditorBrowsable(EditorBrowsableState.Never)] - internal ref T DangerousGetPinnableReference() - { - if (_pinnable == null) - unsafe { return ref Unsafe.AsRef(_byteOffset.ToPointer()); } - else - return ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset); - } + internal ref T DangerousGetPinnableReference() => + ref Unsafe.AddByteOffset(ref ILHelpers.ObjectAsRef(_pinnable), _byteOffset); // These expose the internal representation for Span-related apis use only. - internal Pinnable? Pinnable => _pinnable; + internal object? Pinnable => _pinnable; internal IntPtr ByteOffset => _byteOffset; // @@ -430,7 +407,7 @@ internal ref T DangerousGetPinnableReference() // _pinnable = null // _byteOffset = the pointer // - private readonly Pinnable? _pinnable; + private readonly object? _pinnable; private readonly IntPtr _byteOffset; private readonly int _length; } diff --git a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/SpanHelpers.Portable.cs b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/SpanHelpers.Portable.cs index 662d8e41..82921870 100644 --- a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/SpanHelpers.Portable.cs +++ b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/SpanHelpers.Portable.cs @@ -310,7 +310,7 @@ public static class PerTypeValues private static IntPtr MeasureArrayAdjustment() { T[] sampleArray = new T[1]; - return Unsafe.ByteOffset(ref Unsafe.As>(sampleArray).Data, ref sampleArray[0]); + return Unsafe.ByteOffset(ref ILHelpers.ObjectAsRef(sampleArray), ref sampleArray[0]); } } } diff --git a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il index 55508397..dc331d4f 100644 --- a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il +++ b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il @@ -31,33 +31,23 @@ callvirt instance !0 class [FUNC_ASSEMBLY]System.Func`1::Invoke() ret } - - // This exists to work around the fact that on old Mono, the normal string pinning sequence is broken. - // See docs/RuntimeIssueNodes.md - .method public hidebysig static native int GetRawDataOffset(object obj, uint8& rawData) cil managed aggressiveinlining + + .method public hidebysig static !!T& ObjectAsRef(object obj) cil managed aggressiveinlining { - .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( - 01 00 01 00 00 - ) - - .maxstack 8 - .locals init ( - object pinned str, - void*& ptrRef + .locals ( + object pinned pin, + !!T*& ref ) - - ldarg.1 - conv.u - - ldarg.0 - stloc str - ldloca str - conv.u - stloc ptrRef - ldloc ptrRef + + ldarg obj + stloc pin + + ldloca pin ldind.i - sub - + // i wanted to just use AsRef, but for some reason i cant? will figure out later + // call uint8& System.Runtime.CompilerServices.Unsafe::AsRef(void*) + stloc ref + ldloc ref ret } From f14025482bd93474d3266a720fecbdbfa24110a6 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 3 Jun 2024 22:14:53 -0500 Subject: [PATCH 118/133] Fix AsRef in ObjectAsRef --- src/MonoMod.ILHelpers/MonoMod.ILHelpers.il | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il index dc331d4f..e5c993e3 100644 --- a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il +++ b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il @@ -35,8 +35,10 @@ .method public hidebysig static !!T& ObjectAsRef(object obj) cil managed aggressiveinlining { .locals ( - object pinned pin, - !!T*& ref + object pinned pin +#ifndef netcoreapp + , !!T& ref +#endif ) ldarg obj @@ -44,10 +46,11 @@ ldloca pin ldind.i - // i wanted to just use AsRef, but for some reason i cant? will figure out later - // call uint8& System.Runtime.CompilerServices.Unsafe::AsRef(void*) + // call !0& System.Runtime.CompilerServices.Unsafe::AsRef(void*) +#ifndef netcoreapp stloc ref ldloc ref +#endif ret } From d0825f72c5c602dce0eb4ebe8d449ba94108dc81 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 3 Jun 2024 23:20:00 -0500 Subject: [PATCH 119/133] Fix dumb issues --- .../ReadOnlySpan.Portable.cs | 2 +- src/MonoMod.ILHelpers/MonoMod.ILHelpers.il | 28 ++++++++++++++----- src/MonoMod.UnitTest/Temp.cs | 17 +++++++++++ 3 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 src/MonoMod.UnitTest/Temp.cs diff --git a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlySpan.Portable.cs b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlySpan.Portable.cs index 282f9c4c..49525c36 100644 --- a/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlySpan.Portable.cs +++ b/src/MonoMod.Backports/System/Memory,is_fx,lt_core_2.1,lt_std_2.1/ReadOnlySpan.Portable.cs @@ -274,7 +274,7 @@ public T[] ToArray() /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ref T DangerousGetPinnableReference() => - ref Unsafe.Add(ref ILHelpers.ObjectAsRef(_pinnable), _byteOffset); + ref Unsafe.AddByteOffset(ref ILHelpers.ObjectAsRef(_pinnable), _byteOffset); // These expose the internal representation for Span-related apis use only. internal object? Pinnable => _pinnable; diff --git a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il index e5c993e3..0e2acea4 100644 --- a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il +++ b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il @@ -34,24 +34,38 @@ .method public hidebysig static !!T& ObjectAsRef(object obj) cil managed aggressiveinlining { +#ifdef netcoreapp + ldarg obj + conv.u + ret +#else .locals ( - object pinned pin -#ifndef netcoreapp - , !!T& ref + object pinned pin, + !!T& finalRef +#ifdef NET35 + , !!T** refPtr #endif ) ldarg obj stloc pin + // see https://nike4613.github.io/MonoMod/docs/RuntimeIssueNotes.html#fixed-on-strings-in-old-mono +#ifdef NET35 ldloca pin + conv.u + stloc refPtr + ldloc refPtr ldind.i - // call !0& System.Runtime.CompilerServices.Unsafe::AsRef(void*) -#ifndef netcoreapp - stloc ref - ldloc ref +#else + ldloc pin + conv.u #endif + // call !0& System.Runtime.CompilerServices.Unsafe::AsRef(void*) + stloc finalRef + ldloc finalRef ret +#endif } .method public hidebysig static !!T& UnboxAnyUnsafe(object& 'box') cil managed aggressiveinlining diff --git a/src/MonoMod.UnitTest/Temp.cs b/src/MonoMod.UnitTest/Temp.cs new file mode 100644 index 00000000..c58b907c --- /dev/null +++ b/src/MonoMod.UnitTest/Temp.cs @@ -0,0 +1,17 @@ +using System; +using Xunit; +using Xunit.Abstractions; + +namespace MonoMod.UnitTest +{ + public class Temp : TestBase + { + public Temp(ITestOutputHelper helper) : base(helper) { } + + [Fact] + public void MyThing() + { + Assert.Equal("abc", "abc".AsSpan().ToArray()); + } + } +} \ No newline at end of file From 1147b11d359bca3e19769a02529fecac8bc382eb Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 3 Jun 2024 23:53:23 -0500 Subject: [PATCH 120/133] Final IL touches --- src/MonoMod.ILHelpers/MonoMod.ILHelpers.il | 44 +++++++++++----------- src/MonoMod.UnitTest/Temp.cs | 17 --------- 2 files changed, 22 insertions(+), 39 deletions(-) delete mode 100644 src/MonoMod.UnitTest/Temp.cs diff --git a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il index 0e2acea4..93f31ab6 100644 --- a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il +++ b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il @@ -33,39 +33,39 @@ } .method public hidebysig static !!T& ObjectAsRef(object obj) cil managed aggressiveinlining - { -#ifdef netcoreapp - ldarg obj - conv.u - ret -#else + { + .maxstack 1 .locals ( - object pinned pin, - !!T& finalRef -#ifdef NET35 + object pinned pin +#ifndef netcoreapp , !!T** refPtr + , !!T& finalRef #endif - ) - - ldarg obj - stloc pin - - // see https://nike4613.github.io/MonoMod/docs/RuntimeIssueNotes.html#fixed-on-strings-in-old-mono -#ifdef NET35 + ) + + // pin obj + ldarg obj + stloc pin + +#ifdef netcoreapp + // return ref *Unsafe.BitCast(pin); + ldloc pin + conv.u +#else + // see docs/RuntimeIssueNotes.md - "`fixed` on strings in old Mono" for why this is necessary + // T* ptr = *(T**)(&pin); ldloca pin conv.u stloc refPtr ldloc refPtr ldind.i -#else - ldloc pin - conv.u -#endif - // call !0& System.Runtime.CompilerServices.Unsafe::AsRef(void*) + // return Unsafe.AsRef(ptr); + // see the comments inside that function for why don't just immediately ret stloc finalRef ldloc finalRef +#endif + ret -#endif } .method public hidebysig static !!T& UnboxAnyUnsafe(object& 'box') cil managed aggressiveinlining diff --git a/src/MonoMod.UnitTest/Temp.cs b/src/MonoMod.UnitTest/Temp.cs deleted file mode 100644 index c58b907c..00000000 --- a/src/MonoMod.UnitTest/Temp.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using Xunit; -using Xunit.Abstractions; - -namespace MonoMod.UnitTest -{ - public class Temp : TestBase - { - public Temp(ITestOutputHelper helper) : base(helper) { } - - [Fact] - public void MyThing() - { - Assert.Equal("abc", "abc".AsSpan().ToArray()); - } - } -} \ No newline at end of file From a97697711a7eecae7d13187f61bc6c70234e6863 Mon Sep 17 00:00:00 2001 From: DaNike Date: Tue, 4 Jun 2024 02:08:36 -0500 Subject: [PATCH 121/133] Avoid using function pointers --- src/MonoMod.Core/IDetourFactory.cs | 3 +- .../Platforms/Architectures/x86Arch.cs | 3 +- .../Platforms/Architectures/x86_64Arch.cs | 3 +- src/MonoMod.Core/Platforms/PlatformTriple.cs | 3 +- .../Platforms/Runtimes/Core21Runtime.cs | 3 +- src/MonoMod.Utils/Helpers.cs | 62 +++++++++++++++++-- 6 files changed, 68 insertions(+), 9 deletions(-) diff --git a/src/MonoMod.Core/IDetourFactory.cs b/src/MonoMod.Core/IDetourFactory.cs index 2d8170b3..463c9744 100644 --- a/src/MonoMod.Core/IDetourFactory.cs +++ b/src/MonoMod.Core/IDetourFactory.cs @@ -79,9 +79,10 @@ public static class DetourFactory public static unsafe IDetourFactory Current { [MethodImpl(MethodImplOptionsEx.AggressiveInlining)] - get => Helpers.GetOrInit(ref lazyCurrent, &CreateDefaultFactory); + get => Helpers.GetOrInit(ref lazyCurrent, createDefaultFactoryFunc); } + private static readonly Func createDefaultFactoryFunc = CreateDefaultFactory; private static PlatformTripleDetourFactory CreateDefaultFactory() => new(PlatformTriple.Current); diff --git a/src/MonoMod.Core/Platforms/Architectures/x86Arch.cs b/src/MonoMod.Core/Platforms/Architectures/x86Arch.cs index 81284ad8..60999060 100644 --- a/src/MonoMod.Core/Platforms/Architectures/x86Arch.cs +++ b/src/MonoMod.Core/Platforms/Architectures/x86Arch.cs @@ -11,10 +11,11 @@ internal sealed class x86Arch : IArchitecture public ArchitectureFeature Features => ArchitectureFeature.CreateAltEntryPoint; private BytePatternCollection? lazyKnownMethodThunks; - public unsafe BytePatternCollection KnownMethodThunks => Helpers.GetOrInit(ref lazyKnownMethodThunks, &CreateKnownMethodThunks); + public unsafe BytePatternCollection KnownMethodThunks => Helpers.GetOrInit(ref lazyKnownMethodThunks, createKnownMethodThunksFunc); public IAltEntryFactory AltEntryFactory { get; } + private static readonly Func createKnownMethodThunksFunc = CreateKnownMethodThunks; private static BytePatternCollection CreateKnownMethodThunks() { const ushort An = BytePattern.SAnyValue; diff --git a/src/MonoMod.Core/Platforms/Architectures/x86_64Arch.cs b/src/MonoMod.Core/Platforms/Architectures/x86_64Arch.cs index 19e7400a..2de03bf3 100644 --- a/src/MonoMod.Core/Platforms/Architectures/x86_64Arch.cs +++ b/src/MonoMod.Core/Platforms/Architectures/x86_64Arch.cs @@ -13,10 +13,11 @@ internal sealed class x86_64Arch : IArchitecture public ArchitectureFeature Features => ArchitectureFeature.Immediate64 | ArchitectureFeature.CreateAltEntryPoint; private BytePatternCollection? lazyKnownMethodThunks; - public unsafe BytePatternCollection KnownMethodThunks => Helpers.GetOrInit(ref lazyKnownMethodThunks, &CreateKnownMethodThunks); + public unsafe BytePatternCollection KnownMethodThunks => Helpers.GetOrInit(ref lazyKnownMethodThunks, createKnownMethodThunksFunc); public IAltEntryFactory AltEntryFactory { get; } + private static readonly Func createKnownMethodThunksFunc = CreateKnownMethodThunks; private static BytePatternCollection CreateKnownMethodThunks() { const ushort An = BytePattern.SAnyValue; diff --git a/src/MonoMod.Core/Platforms/PlatformTriple.cs b/src/MonoMod.Core/Platforms/PlatformTriple.cs index eb75f20a..2fea542d 100644 --- a/src/MonoMod.Core/Platforms/PlatformTriple.cs +++ b/src/MonoMod.Core/Platforms/PlatformTriple.cs @@ -117,8 +117,9 @@ public static ISystem CreateCurrentSystem() /// /// This is automatically constructed on first access, according to the values returned by . /// - public static unsafe PlatformTriple Current => Helpers.GetOrInitWithLock(ref lazyCurrent, lazyCurrentLock, &CreateCurrent); + public static unsafe PlatformTriple Current => Helpers.GetOrInitWithLock(ref lazyCurrent, lazyCurrentLock, createCurrentFunc); + private static readonly Func createCurrentFunc = CreateCurrent; private static PlatformTriple CreateCurrent() { var sys = CreateCurrentSystem(); diff --git a/src/MonoMod.Core/Platforms/Runtimes/Core21Runtime.cs b/src/MonoMod.Core/Platforms/Runtimes/Core21Runtime.cs index 7b37451f..46b9b890 100644 --- a/src/MonoMod.Core/Platforms/Runtimes/Core21Runtime.cs +++ b/src/MonoMod.Core/Platforms/Runtimes/Core21Runtime.cs @@ -39,11 +39,12 @@ public unsafe override void DisableInlining(MethodBase method) { } */ + private static readonly Func createJitHookHelpersFunc = CreateJitHookHelpers; private static JitHookHelpersHolder CreateJitHookHelpers(Core21Runtime self) => new(self); private readonly object sync = new(); private JitHookHelpersHolder? lazyJitHookHelpers; - protected unsafe JitHookHelpersHolder JitHookHelpers => Helpers.GetOrInitWithLock(ref lazyJitHookHelpers, sync, &CreateJitHookHelpers, this); + protected unsafe JitHookHelpersHolder JitHookHelpers => Helpers.GetOrInitWithLock(ref lazyJitHookHelpers, sync, createJitHookHelpersFunc, this); // src/inc/corinfo.h line 216 // 0ba106c8-81a0-407f-99a1-928448c1eb62 diff --git a/src/MonoMod.Utils/Helpers.cs b/src/MonoMod.Utils/Helpers.cs index 96e736fc..464e6e9a 100644 --- a/src/MonoMod.Utils/Helpers.cs +++ b/src/MonoMod.Utils/Helpers.cs @@ -157,22 +157,48 @@ private static void ThrowAssertionFailed(ref AssertionInterpolatedStringHandler } #region GetOrInit* + private static class FuncInvokeHolder + { + public static readonly Func, T> InvokeFunc = static f => f(); + } + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining)] + public static T GetOrInit(ref T? location, Func init) where T : class + { + if (location is not null) + return location; + return InitializeValue(ref location, FuncInvokeHolder.InvokeFunc, init); + } + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining)] + public static T GetOrInitWithLock(ref T? location, object @lock, Func init) where T : class + { + if (location is not null) + return location; + return InitializeValueWithLock(ref location, @lock, FuncInvokeHolder.InvokeFunc, init); + } + [MethodImpl(MethodImplOptionsEx.AggressiveInlining)] - public unsafe static T GetOrInit(ref T? location, Func init) where T : class + public static T GetOrInit(ref T? location, Func init, TParam param) where T : class { + ThrowIfArgumentNull(init); if (location is not null) return location; - return InitializeValue(ref location, &ILHelpers.TailCallFunc, init); + return InitializeValue(ref location, init, param); } [MethodImpl(MethodImplOptionsEx.AggressiveInlining)] - public unsafe static T GetOrInitWithLock(ref T? location, object @lock, Func init) where T : class + public static T GetOrInitWithLock(ref T? location, object @lock, Func init, TParam param) where T : class { + ThrowIfArgumentNull(init); if (location is not null) return location; - return InitializeValueWithLock(ref location, @lock, &ILHelpers.TailCallFunc, init); + return InitializeValueWithLock(ref location, @lock, init, param); } + /// + /// This overload may not work on some older Mono implementations, which do not have good function pointer support. + /// [MethodImpl(MethodImplOptionsEx.AggressiveInlining)] public unsafe static T GetOrInit(ref T? location, delegate* init) where T : class { @@ -181,6 +207,10 @@ public unsafe static T GetOrInit(ref T? location, delegate* init) where T return InitializeValue(ref location, &ILHelpers.TailCallDelegatePtr, (IntPtr)init); } + + /// + /// This overload may not work on some older Mono implementations, which do not have good function pointer support. + /// [MethodImpl(MethodImplOptionsEx.AggressiveInlining)] public unsafe static T GetOrInitWithLock(ref T? location, object @lock, delegate* init) where T : class { @@ -189,6 +219,9 @@ public unsafe static T GetOrInitWithLock(ref T? location, object @lock, deleg return InitializeValueWithLock(ref location, @lock, &ILHelpers.TailCallDelegatePtr, (IntPtr)init); } + /// + /// This overload may not work on some older Mono implementations, which do not have good function pointer support. + /// [MethodImpl(MethodImplOptionsEx.AggressiveInlining)] public unsafe static T GetOrInit(ref T? location, delegate* init, TParam obj) where T : class { @@ -197,6 +230,9 @@ public unsafe static T GetOrInit(ref T? location, delegate* + /// This overload may not work on some older Mono implementations, which do not have good function pointer support. + /// [MethodImpl(MethodImplOptionsEx.AggressiveInlining)] public unsafe static T GetOrInitWithLock(ref T? location, object @lock, delegate* init, TParam obj) where T : class { @@ -212,6 +248,13 @@ private unsafe static T InitializeValue(ref T? location, delegate*(ref T? location, Func init, TParam obj) where T : class + { + _ = Interlocked.CompareExchange(ref location, init(obj), null); + return location!; + } + [MethodImpl(MethodImplOptionsEx.NoInlining)] private unsafe static T InitializeValueWithLock(ref T? location, object @lock, delegate* init, TParam obj) where T : class { @@ -222,6 +265,17 @@ private unsafe static T InitializeValueWithLock(ref T? location, obje return location = init(obj); } } + + [MethodImpl(MethodImplOptionsEx.NoInlining)] + private unsafe static T InitializeValueWithLock(ref T? location, object @lock, Func init, TParam obj) where T : class + { + lock (@lock) + { + if (location is not null) + return location; + return location = init(obj); + } + } #endregion [MethodImpl(MethodImplOptionsEx.AggressiveInlining)] From 0611a4f5cb17304139e223a5b8914b3835a4a9be Mon Sep 17 00:00:00 2001 From: DaNike Date: Tue, 4 Jun 2024 02:17:43 -0500 Subject: [PATCH 122/133] Fix some incredibly unsafe code --- src/MonoMod.ILHelpers/MonoMod.ILHelpers.il | 32 -------------------- src/MonoMod.Utils/DynamicReferenceManager.cs | 6 ++-- 2 files changed, 3 insertions(+), 35 deletions(-) diff --git a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il index 93f31ab6..b2b9be67 100644 --- a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il +++ b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il @@ -67,36 +67,4 @@ ret } - - .method public hidebysig static !!T& UnboxAnyUnsafe(object& 'box') cil managed aggressiveinlining - { - .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( - 01 00 02 00 00 - ) - - .maxstack 8 - .locals init ( - [0] !!T - ) - - // if (default(T) == null) - ldloca.s 0 - initobj !!T - ldloc.0 - box !!T - ldnull - ceq - brfalse ValType - - // it's a reference type, so we just return the ref like Unsafe.As - ldarg.0 - ret - - ValType: - // it's a value type, so we want to unbox and return the ref - ldarg.0 - ldind.ref - unbox !!T - ret - } } \ No newline at end of file diff --git a/src/MonoMod.Utils/DynamicReferenceManager.cs b/src/MonoMod.Utils/DynamicReferenceManager.cs index ce08140c..9396bfbd 100644 --- a/src/MonoMod.Utils/DynamicReferenceManager.cs +++ b/src/MonoMod.Utils/DynamicReferenceManager.cs @@ -114,7 +114,7 @@ private static unsafe DataScope AllocReferenceStruct(in [MethodImpl(MethodImplOptionsEx.AggressiveOptimization)] public static DataScope AllocReference(in T? value, out DynamicReferenceCell cellRef) { - if (default(T) == null) + if (!typeof(T).IsValueType) { return AllocReferenceClass(Unsafe.As(ref Unsafe.AsRef(in value)), out cellRef); } @@ -197,14 +197,14 @@ private static Cell GetCell(DynamicReferenceCell cellRef) { case RefValueCell: { - Helpers.Assert(default(T) == null); + Helpers.Assert(!typeof(T).IsValueType); var c = Unsafe.As(cell); Helpers.Assert(c.Value is null or T); return ref Unsafe.As(ref c.Value!); } case ValueTypeCell: { - Helpers.Assert(default(T) != null); + Helpers.Assert(typeof(T).IsValueType); var c = (ValueCell)cell; return ref c.Value; } From 18a742a1ae4514b354ee2d2280eb9bd6fe5d18a0 Mon Sep 17 00:00:00 2001 From: DaNike Date: Tue, 4 Jun 2024 02:53:24 -0500 Subject: [PATCH 123/133] Add ApiCompat suppressions for UnboxAnyUnsafe --- .../CompatibilitySuppressions.xml | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/MonoMod.ILHelpers/CompatibilitySuppressions.xml b/src/MonoMod.ILHelpers/CompatibilitySuppressions.xml index 3656039a..b096c469 100644 --- a/src/MonoMod.ILHelpers/CompatibilitySuppressions.xml +++ b/src/MonoMod.ILHelpers/CompatibilitySuppressions.xml @@ -1,13 +1,54 @@  - CP0001 T:System.Runtime.CompilerServices.Unsafe lib/net5.0/MonoMod.ILHelpers.dll lib/net6.0/MonoMod.ILHelpers.dll + + CP0002 + M:MonoMod.ILHelpers.UnboxAnyUnsafe``1(System.Object@) + lib/net35/MonoMod.ILHelpers.dll + lib/net35/MonoMod.ILHelpers.dll + true + + + CP0002 + M:MonoMod.ILHelpers.UnboxAnyUnsafe``1(System.Object@) + lib/net452/MonoMod.ILHelpers.dll + lib/net452/MonoMod.ILHelpers.dll + true + + + CP0002 + M:MonoMod.ILHelpers.UnboxAnyUnsafe``1(System.Object@) + lib/net5.0/MonoMod.ILHelpers.dll + lib/net5.0/MonoMod.ILHelpers.dll + true + + + CP0002 + M:MonoMod.ILHelpers.UnboxAnyUnsafe``1(System.Object@) + lib/net6.0/MonoMod.ILHelpers.dll + lib/net6.0/MonoMod.ILHelpers.dll + true + + + CP0002 + M:MonoMod.ILHelpers.UnboxAnyUnsafe``1(System.Object@) + lib/net7.0/MonoMod.ILHelpers.dll + lib/net7.0/MonoMod.ILHelpers.dll + true + + + CP0002 + M:MonoMod.ILHelpers.UnboxAnyUnsafe``1(System.Object@) + lib/netstandard2.0/MonoMod.ILHelpers.dll + lib/netstandard2.0/MonoMod.ILHelpers.dll + true + CP1002 System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null From 948e1d687d73eee137924d78c4b1267e0025d043 Mon Sep 17 00:00:00 2001 From: DaNike Date: Tue, 4 Jun 2024 02:53:36 -0500 Subject: [PATCH 124/133] Adjust global.json settings --- global.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/global.json b/global.json index a2065e21..980041e3 100644 --- a/global.json +++ b/global.json @@ -1,8 +1,8 @@ { "sdk": { "allowPrerelease": true, - "rollForward": "latestMinor", - "version": "8.0.203" + "rollForward": "latestPatch", + "version": "8.0.301" }, "msbuild-sdks": { "Microsoft.Build.NoTargets": "3.7.56", From 72d1f570019b1eb74ba1185583cb0388ad1c4a8c Mon Sep 17 00:00:00 2001 From: DaNike Date: Tue, 4 Jun 2024 16:49:25 -0500 Subject: [PATCH 125/133] Document Mono sizeof issue --- docs/RuntimeIssueNotes.md | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/docs/RuntimeIssueNotes.md b/docs/RuntimeIssueNotes.md index 435e9bba..0bdbb3f2 100644 --- a/docs/RuntimeIssueNotes.md +++ b/docs/RuntimeIssueNotes.md @@ -1,12 +1,41 @@ # Notes on issues in various runtime versions +Martin, this is wrong. + +## `sizeof` IL opcode does not work with generic parameters on old Mono + +The title says it all. `sizeof` works fine with all other type-specs, but with generic parameters specifically, +it always returns the system pointer size. + +The relevant code is in `metadata/metadata.c`, in `mono_type_size` (which `sizeof` correctly embeds as a constant): + +```c +int +mono_type_size (MonoType *t, int *align) +{ + // ... + + switch (t->type){ + // ... + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + /* FIXME: Martin, this is wrong. */ + *align = __alignof__(gpointer); + return sizeof (gpointer); + // ... + } + + // ... +} +``` + ## `fixed` on strings in old Mono Some old versions of Mono have broken `conv.u` instruction handling. The following code will crash those old versions with an assert in the JIT's local propagation routine: -```cs +```csharp fixed (char* pStr = "some string") { // ... @@ -15,7 +44,7 @@ fixed (char* pStr = "some string") This is because the sequence that Roslyn emits for `fixed` over a string is this: -``` +```il .locals ( string pinned stringLocalMarkedPinned, char* ptrLocal From dccfe2baf7b2e994143c2445fa0e0b5702d639d0 Mon Sep 17 00:00:00 2001 From: DaNike Date: Tue, 4 Jun 2024 18:23:34 -0500 Subject: [PATCH 126/133] Add analyzer for sizeof(T) with generic param --- .../AnalyzerReleases.Unshipped.md | 4 +- .../Analyzers/DoNotPinStrings.cs | 2 +- .../Analyzers/DoNotSizeofGenerics.cs | 50 +++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 src/MonoMod.SourceGen.Internal/Analyzers/DoNotSizeofGenerics.cs diff --git a/src/MonoMod.SourceGen.Internal/AnalyzerReleases.Unshipped.md b/src/MonoMod.SourceGen.Internal/AnalyzerReleases.Unshipped.md index 41c50d0f..2a4c8bb7 100644 --- a/src/MonoMod.SourceGen.Internal/AnalyzerReleases.Unshipped.md +++ b/src/MonoMod.SourceGen.Internal/AnalyzerReleases.Unshipped.md @@ -9,4 +9,6 @@ MM.ILOverload.BadKind | | Error | ILOverloadGenerator MM.ILOverload.NoFile | | Error | ILOverloadGenerator MM0001 | Build | Warning | AssemblyInfoGenerator MM0002 | Build | Warning | AssemblyInfoGenerator -MMA001 | Stability | Error | DoNotPinStrings \ No newline at end of file +MMA001 | RuntimeIssues | Error | DoNotPinStrings +MMA002 | RuntimeIssues | Warning | DoNotSizeofGenerics +MMA003 | RuntimeIssues | Warning | DoNotSizeofGenerics \ No newline at end of file diff --git a/src/MonoMod.SourceGen.Internal/Analyzers/DoNotPinStrings.cs b/src/MonoMod.SourceGen.Internal/Analyzers/DoNotPinStrings.cs index df9fdb9f..62a05fda 100644 --- a/src/MonoMod.SourceGen.Internal/Analyzers/DoNotPinStrings.cs +++ b/src/MonoMod.SourceGen.Internal/Analyzers/DoNotPinStrings.cs @@ -12,7 +12,7 @@ public sealed class DoNotPinStrings : DiagnosticAnalyzer "MMA001", "Do not pin strings, as it may crash some older Mono runtimes", "Do not pin strings, as it may crash some older Mono runtimes (see docs/RuntimeIssueNotes.md). Pin a span instead.", - "Stability", + "RuntimeIssues", DiagnosticSeverity.Error, isEnabledByDefault: true); diff --git a/src/MonoMod.SourceGen.Internal/Analyzers/DoNotSizeofGenerics.cs b/src/MonoMod.SourceGen.Internal/Analyzers/DoNotSizeofGenerics.cs new file mode 100644 index 00000000..3ac96f5f --- /dev/null +++ b/src/MonoMod.SourceGen.Internal/Analyzers/DoNotSizeofGenerics.cs @@ -0,0 +1,50 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; +using System.Collections.Immutable; + +namespace MonoMod.SourceGen.Internal.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class DoNotSizeofGenerics : DiagnosticAnalyzer + { + private static readonly DiagnosticDescriptor DoNotSizeofGeneric = new( + "MMA002", + "Do not use the sizeof() operator on a generic parameter", + "On some old Mono runtimes, sizeof(T) always returns sizeof(IntPtr). See docs/RuntimeIssueNotes.md.", + "RuntimeIssues", + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor DoNotUseUnsafeSizeOf = new( + "MMA003", + "Do not use Unsafe.SizeOf()", + "On some old Mono runtimes, the sizeof opcode always returns sizeof(IntPtr) on generic parameters, " + + "which Unsafe.SizeOf() always has.. See docs/RuntimeIssueNotes.md.", + "RuntimeIssues", + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics + => ImmutableArray.Create(DoNotSizeofGeneric, DoNotUseUnsafeSizeOf); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", + Justification = "Roslyn always passes a non-null context")] + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics); + + // normal sizeof operator + context.RegisterOperationAction(ctx => + { + var sizeofOp = (ISizeOfOperation)ctx.Operation; + + if (sizeofOp.TypeOperand.TypeKind is TypeKind.TypeParameter) + { + ctx.ReportDiagnostic(Diagnostic.Create(DoNotSizeofGeneric, sizeofOp.Syntax.GetLocation())); + } + }, OperationKind.SizeOf); + } + } +} From f2cd192251aafe78d23b0f537205631889a2f8cb Mon Sep 17 00:00:00 2001 From: DaNike Date: Tue, 4 Jun 2024 19:56:32 -0500 Subject: [PATCH 127/133] Implement type aliasing and forwarding to enable customization of Unsafe impls in C# --- src/Common/UnsafeAlias.cs | 4 +- src/MonoMod.Backports/Directory.Build.targets | 2 +- .../MonoMod.Backports.csproj | 11 +- .../MonoMod.Backports/SRCS.Unsafe.cs | 158 ++++++++++++++++++ .../System/NonVersionableAttribute.cs | 7 + src/MonoMod.ILHelpers/MonoMod.ILHelpers.il | 112 ++++++------- .../MonoMod.ILHelpers.ilproj | 3 + .../System.Runtime.CompilerServices.Unsafe.il | 55 +++++- 8 files changed, 288 insertions(+), 64 deletions(-) create mode 100644 src/MonoMod.Backports/MonoMod.Backports/SRCS.Unsafe.cs create mode 100644 src/MonoMod.Backports/System/NonVersionableAttribute.cs diff --git a/src/Common/UnsafeAlias.cs b/src/Common/UnsafeAlias.cs index 7d283c3c..97f13457 100644 --- a/src/Common/UnsafeAlias.cs +++ b/src/Common/UnsafeAlias.cs @@ -4,8 +4,8 @@ global using ilhelpers::MonoMod; -#if !NET6_0_OR_GREATER -// Any time we want to use Unsafe, we want ours, not the BCL's +#if !NET6_0_OR_GREATER && (NET40_OR_GREATER || NETSTANDARD1_0_OR_GREATER || NETCOREAPP || NET) +// Any time we want to use Unsafe, we want ours, not the BCL's. Note that we need these funky defs because the location of Unsafe moves between versions. // I would actually rather move the BCL assembly defining it into an alias, but that doesn't seem to be particularly viable global using Unsafe = ilhelpers::System.Runtime.CompilerServices.Unsafe; #else diff --git a/src/MonoMod.Backports/Directory.Build.targets b/src/MonoMod.Backports/Directory.Build.targets index 668c6160..c9b9a8cf 100644 --- a/src/MonoMod.Backports/Directory.Build.targets +++ b/src/MonoMod.Backports/Directory.Build.targets @@ -2,7 +2,7 @@ - + true diff --git a/src/MonoMod.Backports/MonoMod.Backports.csproj b/src/MonoMod.Backports/MonoMod.Backports.csproj index 12048f68..374f89b3 100644 --- a/src/MonoMod.Backports/MonoMod.Backports.csproj +++ b/src/MonoMod.Backports/MonoMod.Backports.csproj @@ -5,7 +5,8 @@ false - true + false + false false false @@ -27,6 +28,14 @@ or ('$(MMTFKind)' == '.NETCoreApp' and $([MSBuild]::VersionGreaterThanOrEquals('$(MMTFVersion)','2.1')))">true + + + + ilhelpers + all + + + diff --git a/src/MonoMod.Backports/MonoMod.Backports/SRCS.Unsafe.cs b/src/MonoMod.Backports/MonoMod.Backports/SRCS.Unsafe.cs new file mode 100644 index 00000000..f5e9f33a --- /dev/null +++ b/src/MonoMod.Backports/MonoMod.Backports/SRCS.Unsafe.cs @@ -0,0 +1,158 @@ +#if NET40_OR_GREATER || NETSTANDARD1_0_OR_GREATER || NETCOREAPP || NET +#define UNSAFE_IN_ILHELPERS +#endif + +extern alias ilhelpers; + +// Sometimes these global usings are unused. That's fine. +#pragma warning disable IDE0005 + +// Global usings +global using ilhelpers::MonoMod; + +#if UNSAFE_IN_ILHELPERS && !NET6_0_OR_GREATER +global using Unsafe = ilhelpers::System.Runtime.CompilerServices.Unsafe; +#else +global using Unsafe = System.Runtime.CompilerServices.Unsafe; +#endif + +#pragma warning restore IDE0005 + +#if UNSAFE_IN_ILHELPERS +// SRCS.Unsafe is defined in ILHelpers, so we want to define UnsafeRaw + a type-forwarder + +#if NET6_0_OR_GREATER +using ILImpl = System.Runtime.CompilerServices.Unsafe; +#else +using ILImpl = ilhelpers::System.Runtime.CompilerServices.Unsafe; +#endif + +using System; +using System.Runtime.CompilerServices; + +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(ILImpl))] + +namespace MonoMod.Backports.ILHelpers; + +[CLSCompliant(false)] +public static unsafe class UnsafeRaw +#else +// SRCS.Unsafe is defined here, so we want to define Unsafe + +using MonoMod.Backports; + +using ILImpl = ilhelpers::MonoMod.Backports.ILHelpers.UnsafeRaw; + +namespace System.Runtime.CompilerServices; + +[CLSCompliant(false)] +public static unsafe class Unsafe +#endif +{ + #region Direct forwarders +#nullable disable + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static T Read(void* source) => ILImpl.Read(source); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static T ReadUnaligned(void* source) => ILImpl.ReadUnaligned(source); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static T ReadUnaligned(ref byte source) => ILImpl.ReadUnaligned(ref source); + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void Write(void* destination, T value) => ILImpl.Write(destination, value); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void WriteUnaligned(void* destination, T value) => ILImpl.WriteUnaligned(destination, value); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void WriteUnaligned(ref byte destination, T value) => ILImpl.WriteUnaligned(ref destination, value); + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void Copy(void* destination, ref T source) => ILImpl.Copy(destination, ref source); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void Copy(ref T destination, void* source) => ILImpl.Copy(ref destination, source); + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void* AsPointer(ref T value) => ILImpl.AsPointer(ref value); + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void SkipInit(out T value) => ILImpl.SkipInit(out value); + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void CopyBlock(void* destination, void* source, uint byteCount) => ILImpl.CopyBlock(destination, source, byteCount); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void CopyBlock(ref byte destination, ref byte source, uint byteCount) => ILImpl.CopyBlock(ref destination, ref source, byteCount); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void CopyBlockUnaligned(void* destination, void* source, uint byteCount) => ILImpl.CopyBlockUnaligned(destination, source, byteCount); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void CopyBlockUnaligned(ref byte destination, ref byte source, uint byteCount) => ILImpl.CopyBlockUnaligned(ref destination, ref source, byteCount); + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void InitBlock(void* startAddress, byte value, uint byteCount) => ILImpl.InitBlock(startAddress, value, byteCount); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void InitBlock(ref byte startAddress, byte value, uint byteCount) => ILImpl.InitBlock(ref startAddress, value, byteCount); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void InitBlockUnaligned(void* startAddress, byte value, uint byteCount) => ILImpl.InitBlockUnaligned(startAddress, value, byteCount); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void InitBlockUnaligned(ref byte startAddress, byte value, uint byteCount) => ILImpl.InitBlockUnaligned(ref startAddress, value, byteCount); + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static T As(object o) where T : class => ILImpl.As(o); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T AsRef(void* source) => ref ILImpl.AsRef(source); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T AsRef(in T source) => ref ILImpl.AsRef(in source); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref TTo As(ref TFrom source) => ref ILImpl.As(ref source); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T Unbox(object box) where T : struct => ref ILImpl.Unbox(box); + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T AddByteOffset(ref T source, nint byteOffset) => ref ILImpl.AddByteOffset(ref source, byteOffset); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T AddByteOffset(ref T source, nuint byteOffset) => ref ILImpl.AddByteOffset(ref source, byteOffset); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T SubtractByteOffset(ref T source, nint byteOffset) => ref ILImpl.SubtractByteOffset(ref source, byteOffset); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T SubtractByteOffset(ref T source, nuint byteOffset) => ref ILImpl.SubtractByteOffset(ref source, byteOffset); + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static nint ByteOffset(ref T origin, ref T target) => ILImpl.ByteOffset(ref origin, ref target); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static bool AreSame(ref T left, ref T right) => ILImpl.AreSame(ref left, ref right); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static bool IsAddressGreaterThan(ref T left, ref T right) => ILImpl.IsAddressGreaterThan(ref left, ref right); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static bool IsAddressLessThan(ref T left, ref T right) => ILImpl.IsAddressLessThan(ref left, ref right); + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static bool IsNullRef(ref T source) => ILImpl.IsNullRef(ref source); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T NullRef() => ref ILImpl.NullRef(); +#nullable enable + #endregion + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static int SizeOf() + // TODO: specialize impl on net35 for mono workaround + => ILImpl.SizeOf(); + + // TODO: fix Add and Subtract on net35 for Mono workaround + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T Add(ref T source, int elementOffset) => ref ILImpl.Add(ref source, elementOffset); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void* Add(void* source, int elementOffset) => ILImpl.Add(source, elementOffset); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T Add(ref T source, nint elementOffset) => ref ILImpl.Add(ref source, elementOffset); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T Add(ref T source, nuint elementOffset) => ref ILImpl.Add(ref source, elementOffset); + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T Subtract(ref T source, int elementOffset) => ref ILImpl.Subtract(ref source, elementOffset); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void* Subtract(void* source, int elementOffset) => ILImpl.Subtract(source, elementOffset); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T Subtract(ref T source, nint elementOffset) => ref ILImpl.Subtract(ref source, elementOffset); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T Subtract(ref T source, nuint elementOffset) => ref ILImpl.Subtract(ref source, elementOffset); + +} \ No newline at end of file diff --git a/src/MonoMod.Backports/System/NonVersionableAttribute.cs b/src/MonoMod.Backports/System/NonVersionableAttribute.cs new file mode 100644 index 00000000..7834361d --- /dev/null +++ b/src/MonoMod.Backports/System/NonVersionableAttribute.cs @@ -0,0 +1,7 @@ +namespace System +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + internal sealed class NonVersionableAttribute : Attribute + { + } +} diff --git a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il index b2b9be67..8b76173e 100644 --- a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il +++ b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.il @@ -1,70 +1,70 @@ -#include "version.h" - -#ifdef NET35 -#define FUNC_ASSEMBLY "System.Core" -.assembly extern FUNC_ASSEMBLY -{ - .publickeytoken = CORE_PUBKEY_TOKEN - .ver 3:5:0:0 -} -#else -#define FUNC_ASSEMBLY "CORE_ASSEMBLY" -#endif - -.class public abstract auto ansi sealed beforefieldinit MonoMod.ILHelpers - extends [CORE_ASSEMBLY]System.Object -{ - .method public hidebysig static !!T TailCallDelegatePtr(native int source) cil managed aggressiveinlining - { - .maxstack 8 - ldarg.0 - tail. - calli !!T() - ret - } // end of method Unsafe::Read - - .method public hidebysig static !!T TailCallFunc(class [FUNC_ASSEMBLY]System.Func`1 func) cil managed aggressiveinlining - { - .maxstack 8 - ldarg.0 - tail. - callvirt instance !0 class [FUNC_ASSEMBLY]System.Func`1::Invoke() - ret - } - - .method public hidebysig static !!T& ObjectAsRef(object obj) cil managed aggressiveinlining +#include "version.h" + +#ifdef NET35 +#define FUNC_ASSEMBLY "System.Core" +.assembly extern FUNC_ASSEMBLY +{ + .publickeytoken = CORE_PUBKEY_TOKEN + .ver 3:5:0:0 +} +#else +#define FUNC_ASSEMBLY "CORE_ASSEMBLY" +#endif + +.class public abstract auto ansi sealed beforefieldinit MonoMod.ILHelpers + extends [CORE_ASSEMBLY]System.Object +{ + .method public hidebysig static !!T TailCallDelegatePtr(native int source) cil managed aggressiveinlining + { + .maxstack 8 + ldarg.0 + //tail. // The tail prefix on calli seems to be considered by many tools to be invalid. + calli !!T() + ret + } // end of method Unsafe::Read + + .method public hidebysig static !!T TailCallFunc(class [FUNC_ASSEMBLY]System.Func`1 func) cil managed aggressiveinlining + { + .maxstack 8 + ldarg.0 + tail. + callvirt instance !0 class [FUNC_ASSEMBLY]System.Func`1::Invoke() + ret + } + + .method public hidebysig static !!T& ObjectAsRef(object obj) cil managed aggressiveinlining { - .maxstack 1 - .locals ( + .maxstack 1 + .locals ( object pinned pin -#ifndef netcoreapp - , !!T** refPtr +#ifndef netcoreapp + , !!T** refPtr , !!T& finalRef -#endif +#endif ) - // pin obj + // pin obj ldarg obj stloc pin - + #ifdef netcoreapp // return ref *Unsafe.BitCast(pin); - ldloc pin - conv.u -#else + ldloc pin + conv.u +#else // see docs/RuntimeIssueNotes.md - "`fixed` on strings in old Mono" for why this is necessary - // T* ptr = *(T**)(&pin); - ldloca pin - conv.u - stloc refPtr - ldloc refPtr - ldind.i + // T* ptr = *(T**)(&pin); + ldloca pin + conv.u + stloc refPtr + ldloc refPtr + ldind.i // return Unsafe.AsRef(ptr); - // see the comments inside that function for why don't just immediately ret - stloc finalRef - ldloc finalRef + // see the comments inside that function for why don't just immediately ret + stloc finalRef + ldloc finalRef #endif - ret - } + ret + } } \ No newline at end of file diff --git a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj index d7f6bf36..e5922313 100644 --- a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj +++ b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj @@ -6,6 +6,9 @@ 1.1.0 1.0.0 + + + false \ No newline at end of file diff --git a/src/MonoMod.ILHelpers/System.Runtime.CompilerServices.Unsafe.il b/src/MonoMod.ILHelpers/System.Runtime.CompilerServices.Unsafe.il index e087c714..1d041ef4 100644 --- a/src/MonoMod.ILHelpers/System.Runtime.CompilerServices.Unsafe.il +++ b/src/MonoMod.ILHelpers/System.Runtime.CompilerServices.Unsafe.il @@ -7,6 +7,22 @@ // =============== CLASS MEMBERS DECLARATION =================== +.assembly extern MonoMod.Backports { } + +#ifdef NET40_OR_GREATER + #define UNSAFE_ILHELPERS +#endif +#ifdef NETSTANDARD1_0_OR_GREATER + #define UNSAFE_ILHELPERS +#endif +#ifdef NETCOREAPP1_0_OR_GREATER + #define UNSAFE_ILHELPERS +#endif + +#ifndef UNSAFE_ILHELPERS + #define UNSAFE_BACKPORTS +#endif + #ifdef NET6_0_OR_GREATER #ifdef NET6_0 @@ -24,10 +40,21 @@ #endif } +.class extern forwarder MonoMod.Backports.ILHelpers.UnsafeRaw { .assembly extern MonoMod.Backports } + #else // TODO: nullable annotations (ugh) +#ifdef UNSAFE_ILHELPERS +// we define SRCS.Unsafe in ILHelpers, and forward UnsafeRaw to Backports, which forwards back here. +.class extern forwarder MonoMod.Backports.ILHelpers.UnsafeRaw { .assembly extern MonoMod.Backports } .class public abstract auto ansi sealed beforefieldinit System.Runtime.CompilerServices.Unsafe +#else +// we define SRCS.Unsafe in Backports to handle some runtime issues, forward to it here, +// and Backports uses some of UnsafeRaw to actually implement it +.class extern forwarder System.Runtime.CompilerServices.Unsafe { .assembly extern MonoMod.Backports } +.class public abstract auto ansi sealed beforefieldinit MonoMod.Backports.ILHelpers.UnsafeRaw +#endif extends [CORE_ASSEMBLY]System.Object { .method public hidebysig static !!T Read(void* source) cil managed aggressiveinlining @@ -324,6 +351,10 @@ .method public hidebysig static !!T& Add(!!T& source, native int elementOffset) cil managed aggressiveinlining { .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 ) + .param [2] + .custom instance void System.Runtime.CompilerServices.NativeIntegerAttribute::.ctor() = ( + 01 00 00 00 + ) .maxstack 8 ldarg.0 ldarg.1 @@ -335,11 +366,11 @@ .method public hidebysig static !!T& Add(!!T& source, native uint elementOffset) cil managed aggressiveinlining { + .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 ) .param [2] .custom instance void System.Runtime.CompilerServices.NativeIntegerAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 ldarg.0 ldarg.1 @@ -352,6 +383,10 @@ .method public hidebysig static !!T& AddByteOffset(!!T& source, native int byteOffset) cil managed aggressiveinlining { .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 ) + .param [2] + .custom instance void System.Runtime.CompilerServices.NativeIntegerAttribute::.ctor() = ( + 01 00 00 00 + ) .maxstack 8 ldarg.0 ldarg.1 @@ -361,11 +396,11 @@ .method public hidebysig static !!T& AddByteOffset(!!T& source, native uint byteOffset) cil managed aggressiveinlining { + .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 ) .param [2] .custom instance void System.Runtime.CompilerServices.NativeIntegerAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 ldarg.0 ldarg.1 @@ -402,6 +437,10 @@ .method public hidebysig static !!T& Subtract(!!T& source, native int elementOffset) cil managed aggressiveinlining { .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 ) + .param [2] + .custom instance void System.Runtime.CompilerServices.NativeIntegerAttribute::.ctor() = ( + 01 00 00 00 + ) .maxstack 8 ldarg.0 ldarg.1 @@ -413,11 +452,11 @@ .method public hidebysig static !!T& Subtract(!!T& source, native uint elementOffset) cil managed aggressiveinlining { + .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 ) .param [2] .custom instance void System.Runtime.CompilerServices.NativeIntegerAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 ldarg.0 ldarg.1 @@ -430,6 +469,10 @@ .method public hidebysig static !!T& SubtractByteOffset(!!T& source, native int byteOffset) cil managed aggressiveinlining { .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 ) + .param [2] + .custom instance void System.Runtime.CompilerServices.NativeIntegerAttribute::.ctor() = ( + 01 00 00 00 + ) .maxstack 8 ldarg.0 ldarg.1 @@ -439,11 +482,11 @@ .method public hidebysig static !!T& SubtractByteOffset(!!T& source, native uint byteOffset) cil managed aggressiveinlining { + .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 ) .param [2] .custom instance void System.Runtime.CompilerServices.NativeIntegerAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 ldarg.0 ldarg.1 @@ -454,6 +497,10 @@ .method public hidebysig static native int ByteOffset(!!T& origin, !!T& target) cil managed aggressiveinlining { .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 ) + .param [0] + .custom instance void System.Runtime.CompilerServices.NativeIntegerAttribute::.ctor() = ( + 01 00 00 00 + ) .maxstack 8 ldarg.1 ldarg.0 From 449dfd9b3919eef8e4b597331b32e93448114727 Mon Sep 17 00:00:00 2001 From: DaNike Date: Tue, 4 Jun 2024 20:24:36 -0500 Subject: [PATCH 128/133] Fix package build --- .../CompatibilitySuppressions.xml | 25 +++++++++++++++++++ .../MonoMod.ILHelpers.ilproj | 2 +- tools/NuGet.props | 4 --- tools/NuGet.targets | 7 +++++- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/MonoMod.Backports/CompatibilitySuppressions.xml b/src/MonoMod.Backports/CompatibilitySuppressions.xml index 8674ead1..48161047 100644 --- a/src/MonoMod.Backports/CompatibilitySuppressions.xml +++ b/src/MonoMod.Backports/CompatibilitySuppressions.xml @@ -1,6 +1,12 @@  + + CP0001 + T:System.Runtime.CompilerServices.Unsafe + lib/net5.0/MonoMod.ILHelpers.dll + lib/net6.0/MonoMod.ILHelpers.dll + CP0002 M:System.ValueTuple.#ctor @@ -73,6 +79,19 @@ lib/net35/MonoMod.Backports.dll lib/net452/MonoMod.Backports.dll + + CP1002 + System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null + left + lib/net6.0/MonoMod.ILHelpers.dll + + + CP1002 + System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null + left + lib/net6.0/MonoMod.ILHelpers.dll + true + CP1002 mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes @@ -80,6 +99,12 @@ lib/net452/MonoMod.Backports.dll true + + CP1002 + System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null + lib/net6.0/MonoMod.ILHelpers.dll + right + CP1002 mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes diff --git a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj index e5922313..48b46831 100644 --- a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj +++ b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj @@ -8,7 +8,7 @@ 1.0.0 - false + false \ No newline at end of file diff --git a/tools/NuGet.props b/tools/NuGet.props index 07acb596..2642f90e 100644 --- a/tools/NuGet.props +++ b/tools/NuGet.props @@ -22,10 +22,6 @@ false - - - - diff --git a/tools/NuGet.targets b/tools/NuGet.targets index d0e73270..353e39e1 100644 --- a/tools/NuGet.targets +++ b/tools/NuGet.targets @@ -30,7 +30,7 @@ - + README.md @@ -40,6 +40,11 @@ + + + + + From 76f46e6b2f8d902108f9c02b539224b2a02d110a Mon Sep 17 00:00:00 2001 From: DaNike Date: Wed, 5 Jun 2024 03:33:18 -0500 Subject: [PATCH 129/133] Implement old Mono sizeof fix --- src/Common/UnsafeAlias.cs | 2 +- .../MonoMod.Backports.csproj | 1 + .../MonoMod.Backports/SRCS.Unsafe.cs | 44 ++++++++++++++++--- .../MonoMod.ILHelpers.ilproj | 3 ++ .../System.Runtime.CompilerServices.Unsafe.il | 9 +--- src/MonoMod.Utils/Helpers.cs | 8 ++-- 6 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/Common/UnsafeAlias.cs b/src/Common/UnsafeAlias.cs index 97f13457..29216bec 100644 --- a/src/Common/UnsafeAlias.cs +++ b/src/Common/UnsafeAlias.cs @@ -4,7 +4,7 @@ global using ilhelpers::MonoMod; -#if !NET6_0_OR_GREATER && (NET40_OR_GREATER || NETSTANDARD1_0_OR_GREATER || NETCOREAPP || NET) +#if !NET6_0_OR_GREATER && (NETSTANDARD2_1_OR_GREATER || NETCOREAPP || NET) // Any time we want to use Unsafe, we want ours, not the BCL's. Note that we need these funky defs because the location of Unsafe moves between versions. // I would actually rather move the BCL assembly defining it into an alias, but that doesn't seem to be particularly viable global using Unsafe = ilhelpers::System.Runtime.CompilerServices.Unsafe; diff --git a/src/MonoMod.Backports/MonoMod.Backports.csproj b/src/MonoMod.Backports/MonoMod.Backports.csproj index 374f89b3..da9e82cb 100644 --- a/src/MonoMod.Backports/MonoMod.Backports.csproj +++ b/src/MonoMod.Backports/MonoMod.Backports.csproj @@ -1,6 +1,7 @@  + $(TargetFrameworks);netstandard2.1;netcoreapp2.1;netcoreapp3.0;netcoreapp3.1 diff --git a/src/MonoMod.Backports/MonoMod.Backports/SRCS.Unsafe.cs b/src/MonoMod.Backports/MonoMod.Backports/SRCS.Unsafe.cs index f5e9f33a..44cb4915 100644 --- a/src/MonoMod.Backports/MonoMod.Backports/SRCS.Unsafe.cs +++ b/src/MonoMod.Backports/MonoMod.Backports/SRCS.Unsafe.cs @@ -1,4 +1,4 @@ -#if NET40_OR_GREATER || NETSTANDARD1_0_OR_GREATER || NETCOREAPP || NET +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP || NET #define UNSAFE_IN_ILHELPERS #endif @@ -130,12 +130,45 @@ public static unsafe class Unsafe #nullable enable #endregion +#if !UNSAFE_IN_ILHELPERS + // See docs/RuntimeIssueNotes.md. Until 2015, Mono returned incorrect values for the sizeof opcode when applied to a type parameter. + // To deal with this, we need to compute type size in another way, and return it as appropriate, specializing all of the below accordingly. + private static class PerTypeValues + { + public static readonly nint TypeSize = ComputeTypeSize(); + + private static nint ComputeTypeSize() + { + var array = new T[2]; + return ILImpl.ByteOffset(ref array[0], ref array[1]); + } + } + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static int SizeOf() => (int)PerTypeValues.TypeSize; + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T Add(ref T source, int elementOffset) => ref ILImpl.AddByteOffset(ref source, (nint)elementOffset * PerTypeValues.TypeSize); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void* Add(void* source, int elementOffset) => (byte*)source + (elementOffset * PerTypeValues.TypeSize); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T Add(ref T source, nint elementOffset) => ref ILImpl.AddByteOffset(ref source, elementOffset * PerTypeValues.TypeSize); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T Add(ref T source, nuint elementOffset) => ref ILImpl.AddByteOffset(ref source, elementOffset * (nuint)PerTypeValues.TypeSize); + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T Subtract(ref T source, int elementOffset) => ref ILImpl.SubtractByteOffset(ref source, (nint)elementOffset * PerTypeValues.TypeSize); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static void* Subtract(void* source, int elementOffset) => (byte*)source - (elementOffset * PerTypeValues.TypeSize); + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static ref T Subtract(ref T source, nint elementOffset) => ref ILImpl.SubtractByteOffset(ref source, elementOffset * PerTypeValues.TypeSize); [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] - public static int SizeOf() - // TODO: specialize impl on net35 for mono workaround - => ILImpl.SizeOf(); + public static ref T Subtract(ref T source, nuint elementOffset) => ref ILImpl.SubtractByteOffset(ref source, elementOffset * (nuint)PerTypeValues.TypeSize); - // TODO: fix Add and Subtract on net35 for Mono workaround +#else + + [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] + public static int SizeOf() => ILImpl.SizeOf(); [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] public static ref T Add(ref T source, int elementOffset) => ref ILImpl.Add(ref source, elementOffset); @@ -155,4 +188,5 @@ public static int SizeOf() [MethodImpl(MethodImplOptionsEx.AggressiveInlining), NonVersionable] public static ref T Subtract(ref T source, nuint elementOffset) => ref ILImpl.Subtract(ref source, elementOffset); +#endif } \ No newline at end of file diff --git a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj index 48b46831..cc5f7d54 100644 --- a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj +++ b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj @@ -3,6 +3,9 @@ A collection of IL helpers for MonoMod, including a backport of System.Runtime.CompilerServices.Unsafe. + + + $(TargetFrameworks);netstandard2.1;netcoreapp2.1;netcoreapp3.0;netcoreapp3.1 1.1.0 1.0.0 diff --git a/src/MonoMod.ILHelpers/System.Runtime.CompilerServices.Unsafe.il b/src/MonoMod.ILHelpers/System.Runtime.CompilerServices.Unsafe.il index 1d041ef4..fd0fadb7 100644 --- a/src/MonoMod.ILHelpers/System.Runtime.CompilerServices.Unsafe.il +++ b/src/MonoMod.ILHelpers/System.Runtime.CompilerServices.Unsafe.il @@ -9,20 +9,13 @@ .assembly extern MonoMod.Backports { } -#ifdef NET40_OR_GREATER - #define UNSAFE_ILHELPERS -#endif -#ifdef NETSTANDARD1_0_OR_GREATER +#ifdef NETSTANDARD2_1_OR_GREATER #define UNSAFE_ILHELPERS #endif #ifdef NETCOREAPP1_0_OR_GREATER #define UNSAFE_ILHELPERS #endif -#ifndef UNSAFE_ILHELPERS - #define UNSAFE_BACKPORTS -#endif - #ifdef NET6_0_OR_GREATER #ifdef NET6_0 diff --git a/src/MonoMod.Utils/Helpers.cs b/src/MonoMod.Utils/Helpers.cs index 464e6e9a..5f4fb919 100644 --- a/src/MonoMod.Utils/Helpers.cs +++ b/src/MonoMod.Utils/Helpers.cs @@ -24,22 +24,22 @@ public static void Swap(ref T a, ref T b) [MethodImpl(MethodImplOptionsEx.AggressiveInlining)] public static unsafe bool Has(this T value, T flag) where T : struct, Enum { - if (sizeof(T) == sizeof(long)) + if (Unsafe.SizeOf() == sizeof(long)) { var flagVal = Unsafe.As(ref flag); return (Unsafe.As(ref value) & flagVal) == flagVal; } - else if (sizeof(T) == sizeof(int)) + else if (Unsafe.SizeOf() == sizeof(int)) { var flagVal = Unsafe.As(ref flag); return (Unsafe.As(ref value) & flagVal) == flagVal; } - else if (sizeof(T) == sizeof(short)) + else if (Unsafe.SizeOf() == sizeof(short)) { var flagVal = Unsafe.As(ref flag); return (Unsafe.As(ref value) & flagVal) == flagVal; } - else if (sizeof(T) == sizeof(byte)) + else if (Unsafe.SizeOf() == sizeof(byte)) { var flagVal = Unsafe.As(ref flag); return (Unsafe.As(ref value) & flagVal) == flagVal; From a4fdf98d7885cb0a670e7f880f8b700adf425041 Mon Sep 17 00:00:00 2001 From: DaNike Date: Fri, 7 Jun 2024 03:00:11 -0500 Subject: [PATCH 130/133] Make ILHelpers package depend on Backports package I don't know if this can actually be used; still need to test that. Package validation also doesn't recognize it. --- docs/README.ILHelpers.md | 14 +- .../CompatibilitySuppressions.xml | 25 --- .../MonoMod.Backports.csproj | 5 +- .../CompatibilitySuppressions.xml | 177 ++++++++++++++++++ .../MonoMod.ILHelpers.ilproj | 27 ++- tools/Common.props | 1 + tools/Common.targets | 15 ++ 7 files changed, 217 insertions(+), 47 deletions(-) diff --git a/docs/README.ILHelpers.md b/docs/README.ILHelpers.md index b2033350..cf52b15a 100644 --- a/docs/README.ILHelpers.md +++ b/docs/README.ILHelpers.md @@ -1,13 +1 @@ -# `MonoMod.ILHelpers` - -`MonoMod.ILHelpers` is a collection of helpers manually implemented in IL. - -Notably, this contains a backport of `System.Runtime.CompilerServices.Unsafe`, as it exists in .NET 6, to all older -runtimes. This means that any environment which *also* provides that class which is older than .NET 6 will require -an `extern alias` to be able to use properly. - -## Notable APIs - -- `System.Runtime.CompilerServices.Unsafe` -- `MonoMod.ILHelpers` -- \ No newline at end of file +# DO NOT REFERENCE THIS PACKAGE DIRECTLY! Reference MonoMod.Backports instead. \ No newline at end of file diff --git a/src/MonoMod.Backports/CompatibilitySuppressions.xml b/src/MonoMod.Backports/CompatibilitySuppressions.xml index 48161047..8674ead1 100644 --- a/src/MonoMod.Backports/CompatibilitySuppressions.xml +++ b/src/MonoMod.Backports/CompatibilitySuppressions.xml @@ -1,12 +1,6 @@  - - CP0001 - T:System.Runtime.CompilerServices.Unsafe - lib/net5.0/MonoMod.ILHelpers.dll - lib/net6.0/MonoMod.ILHelpers.dll - CP0002 M:System.ValueTuple.#ctor @@ -79,19 +73,6 @@ lib/net35/MonoMod.Backports.dll lib/net452/MonoMod.Backports.dll - - CP1002 - System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/net6.0/MonoMod.ILHelpers.dll - - - CP1002 - System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/net6.0/MonoMod.ILHelpers.dll - true - CP1002 mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes @@ -99,12 +80,6 @@ lib/net452/MonoMod.Backports.dll true - - CP1002 - System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null - lib/net6.0/MonoMod.ILHelpers.dll - right - CP1002 mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes diff --git a/src/MonoMod.Backports/MonoMod.Backports.csproj b/src/MonoMod.Backports/MonoMod.Backports.csproj index da9e82cb..2de1a934 100644 --- a/src/MonoMod.Backports/MonoMod.Backports.csproj +++ b/src/MonoMod.Backports/MonoMod.Backports.csproj @@ -1,8 +1,8 @@  - - $(TargetFrameworks);netstandard2.1;netcoreapp2.1;netcoreapp3.0;netcoreapp3.1 + + $(BackportsTargetFrameworks) false @@ -33,7 +33,6 @@ ilhelpers - all diff --git a/src/MonoMod.ILHelpers/CompatibilitySuppressions.xml b/src/MonoMod.ILHelpers/CompatibilitySuppressions.xml index b096c469..8d9cb1dc 100644 --- a/src/MonoMod.ILHelpers/CompatibilitySuppressions.xml +++ b/src/MonoMod.ILHelpers/CompatibilitySuppressions.xml @@ -1,12 +1,45 @@  + + CP0001 + T:System.Runtime.CompilerServices.Unsafe + lib/net35/MonoMod.ILHelpers.dll + lib/net35/MonoMod.ILHelpers.dll + true + + + CP0001 + T:System.Runtime.CompilerServices.Unsafe + lib/net452/MonoMod.ILHelpers.dll + lib/net452/MonoMod.ILHelpers.dll + true + CP0001 T:System.Runtime.CompilerServices.Unsafe lib/net5.0/MonoMod.ILHelpers.dll lib/net6.0/MonoMod.ILHelpers.dll + + CP0001 + T:MonoMod.Backports.ILHelpers.UnsafeRaw + lib/netstandard2.0/MonoMod.ILHelpers.dll + lib/netcoreapp2.1/MonoMod.ILHelpers.dll + + + CP0001 + T:System.Runtime.CompilerServices.Unsafe + lib/netstandard2.0/MonoMod.ILHelpers.dll + lib/netstandard2.0/MonoMod.ILHelpers.dll + true + + + CP0001 + T:MonoMod.Backports.ILHelpers.UnsafeRaw + lib/netstandard2.0/MonoMod.ILHelpers.dll + lib/netstandard2.1/MonoMod.ILHelpers.dll + CP0002 M:MonoMod.ILHelpers.UnboxAnyUnsafe``1(System.Object@) @@ -49,6 +82,52 @@ lib/netstandard2.0/MonoMod.ILHelpers.dll true + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + left + lib/net35/MonoMod.ILHelpers.dll + true + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + left + lib/net452/MonoMod.ILHelpers.dll + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + left + lib/net452/MonoMod.ILHelpers.dll + true + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + left + lib/net5.0/MonoMod.ILHelpers.dll + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + left + lib/net5.0/MonoMod.ILHelpers.dll + true + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + left + lib/net6.0/MonoMod.ILHelpers.dll + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + left + lib/net6.0/MonoMod.ILHelpers.dll + true + CP1002 System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null @@ -62,6 +141,74 @@ lib/net6.0/MonoMod.ILHelpers.dll true + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + left + lib/net7.0/MonoMod.ILHelpers.dll + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + left + lib/net7.0/MonoMod.ILHelpers.dll + true + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + left + lib/net8.0/MonoMod.ILHelpers.dll + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + left + lib/netcoreapp2.1/MonoMod.ILHelpers.dll + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + left + lib/netcoreapp3.0/MonoMod.ILHelpers.dll + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + left + lib/netcoreapp3.1/MonoMod.ILHelpers.dll + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + left + lib/netstandard2.0/MonoMod.ILHelpers.dll + true + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + left + lib/netstandard2.1/MonoMod.ILHelpers.dll + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + lib/net35/MonoMod.ILHelpers.dll + right + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + lib/net5.0/MonoMod.ILHelpers.dll + right + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + lib/net6.0/MonoMod.ILHelpers.dll + right + CP1002 System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null @@ -75,4 +222,34 @@ right true + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + lib/net7.0/MonoMod.ILHelpers.dll + right + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + lib/netcoreapp2.1/MonoMod.ILHelpers.dll + right + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + lib/netcoreapp3.0/MonoMod.ILHelpers.dll + right + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + lib/netcoreapp3.1/MonoMod.ILHelpers.dll + right + + + CP1002 + MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + lib/netstandard2.0/MonoMod.ILHelpers.dll + right + \ No newline at end of file diff --git a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj index cc5f7d54..16da1804 100644 --- a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj +++ b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj @@ -2,16 +2,31 @@ - A collection of IL helpers for MonoMod, including a backport of System.Runtime.CompilerServices.Unsafe. + # DO NOT REFERENCE THIS PACKAGE DIRECTLY! Reference MonoMod.Backports instead. - - $(TargetFrameworks);netstandard2.1;netcoreapp2.1;netcoreapp3.0;netcoreapp3.1 + + $(BackportsTargetFrameworks) 1.1.0 1.0.0 - - - false + + + + + + + + + %(Value) + %(Value) + + + + + + \ No newline at end of file diff --git a/tools/Common.props b/tools/Common.props index fd6f5fb1..42a70888 100644 --- a/tools/Common.props +++ b/tools/Common.props @@ -12,6 +12,7 @@ net6.0;net5.0;net7.0;net8.0;netstandard2.0;net35;net452 + $(TargetFrameworks);netstandard2.1;netcoreapp2.1;netcoreapp3.0;netcoreapp3.1 diff --git a/tools/Common.targets b/tools/Common.targets index 91dbe439..d69f5ce5 100644 --- a/tools/Common.targets +++ b/tools/Common.targets @@ -7,5 +7,20 @@ + + + + <_PropertiesToGet Remove="@(_PropertiesToGet)" /> + + + + + <_PropertiesToGet Include="$(PropertyNames)" /> + + + + + + From 8543f031bd0a867b1a843b7410c777d56f844887 Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 8 Jun 2024 03:05:12 -0500 Subject: [PATCH 131/133] Remove the Backports-related compat suppressions in ILHelpers --- .../CompatibilitySuppressions.xml | 177 ------------------ .../MonoMod.ILHelpers.ilproj | 11 +- 2 files changed, 9 insertions(+), 179 deletions(-) diff --git a/src/MonoMod.ILHelpers/CompatibilitySuppressions.xml b/src/MonoMod.ILHelpers/CompatibilitySuppressions.xml index 8d9cb1dc..b096c469 100644 --- a/src/MonoMod.ILHelpers/CompatibilitySuppressions.xml +++ b/src/MonoMod.ILHelpers/CompatibilitySuppressions.xml @@ -1,45 +1,12 @@  - - CP0001 - T:System.Runtime.CompilerServices.Unsafe - lib/net35/MonoMod.ILHelpers.dll - lib/net35/MonoMod.ILHelpers.dll - true - - - CP0001 - T:System.Runtime.CompilerServices.Unsafe - lib/net452/MonoMod.ILHelpers.dll - lib/net452/MonoMod.ILHelpers.dll - true - CP0001 T:System.Runtime.CompilerServices.Unsafe lib/net5.0/MonoMod.ILHelpers.dll lib/net6.0/MonoMod.ILHelpers.dll - - CP0001 - T:MonoMod.Backports.ILHelpers.UnsafeRaw - lib/netstandard2.0/MonoMod.ILHelpers.dll - lib/netcoreapp2.1/MonoMod.ILHelpers.dll - - - CP0001 - T:System.Runtime.CompilerServices.Unsafe - lib/netstandard2.0/MonoMod.ILHelpers.dll - lib/netstandard2.0/MonoMod.ILHelpers.dll - true - - - CP0001 - T:MonoMod.Backports.ILHelpers.UnsafeRaw - lib/netstandard2.0/MonoMod.ILHelpers.dll - lib/netstandard2.1/MonoMod.ILHelpers.dll - CP0002 M:MonoMod.ILHelpers.UnboxAnyUnsafe``1(System.Object@) @@ -82,52 +49,6 @@ lib/netstandard2.0/MonoMod.ILHelpers.dll true - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/net35/MonoMod.ILHelpers.dll - true - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/net452/MonoMod.ILHelpers.dll - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/net452/MonoMod.ILHelpers.dll - true - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/net5.0/MonoMod.ILHelpers.dll - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/net5.0/MonoMod.ILHelpers.dll - true - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/net6.0/MonoMod.ILHelpers.dll - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/net6.0/MonoMod.ILHelpers.dll - true - CP1002 System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null @@ -141,74 +62,6 @@ lib/net6.0/MonoMod.ILHelpers.dll true - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/net7.0/MonoMod.ILHelpers.dll - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/net7.0/MonoMod.ILHelpers.dll - true - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/net8.0/MonoMod.ILHelpers.dll - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/netcoreapp2.1/MonoMod.ILHelpers.dll - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/netcoreapp3.0/MonoMod.ILHelpers.dll - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/netcoreapp3.1/MonoMod.ILHelpers.dll - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/netstandard2.0/MonoMod.ILHelpers.dll - true - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/netstandard2.1/MonoMod.ILHelpers.dll - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - lib/net35/MonoMod.ILHelpers.dll - right - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - lib/net5.0/MonoMod.ILHelpers.dll - right - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - lib/net6.0/MonoMod.ILHelpers.dll - right - CP1002 System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null @@ -222,34 +75,4 @@ right true - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - lib/net7.0/MonoMod.ILHelpers.dll - right - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - lib/netcoreapp2.1/MonoMod.ILHelpers.dll - right - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - lib/netcoreapp3.0/MonoMod.ILHelpers.dll - right - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - lib/netcoreapp3.1/MonoMod.ILHelpers.dll - right - - - CP1002 - MonoMod.Backports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - lib/netstandard2.0/MonoMod.ILHelpers.dll - right - \ No newline at end of file diff --git a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj index 16da1804..3e790781 100644 --- a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj +++ b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj @@ -1,5 +1,4 @@  - # DO NOT REFERENCE THIS PACKAGE DIRECTLY! Reference MonoMod.Backports instead. @@ -10,7 +9,7 @@ 1.1.0 1.0.0 - + + + + + + + + \ No newline at end of file From 21dac91416b6fa5cce7fd26043a481c07f1a8f93 Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 8 Jun 2024 03:11:35 -0500 Subject: [PATCH 132/133] Remove ILHelpers S.R.CS.Unsafe compatability suppressions --- .../CompatibilitySuppressions.xml | 25 ------------------- .../MonoMod.ILHelpers.ilproj | 2 +- .../System.Runtime.CompilerServices.Unsafe.il | 6 ++++- 3 files changed, 6 insertions(+), 27 deletions(-) diff --git a/src/MonoMod.ILHelpers/CompatibilitySuppressions.xml b/src/MonoMod.ILHelpers/CompatibilitySuppressions.xml index b096c469..464a886f 100644 --- a/src/MonoMod.ILHelpers/CompatibilitySuppressions.xml +++ b/src/MonoMod.ILHelpers/CompatibilitySuppressions.xml @@ -1,12 +1,6 @@  - - CP0001 - T:System.Runtime.CompilerServices.Unsafe - lib/net5.0/MonoMod.ILHelpers.dll - lib/net6.0/MonoMod.ILHelpers.dll - CP0002 M:MonoMod.ILHelpers.UnboxAnyUnsafe``1(System.Object@) @@ -49,25 +43,6 @@ lib/netstandard2.0/MonoMod.ILHelpers.dll true - - CP1002 - System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/net6.0/MonoMod.ILHelpers.dll - - - CP1002 - System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null - left - lib/net6.0/MonoMod.ILHelpers.dll - true - - - CP1002 - System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null - lib/net6.0/MonoMod.ILHelpers.dll - right - CP1002 System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null diff --git a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj index 3e790781..0312ce4c 100644 --- a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj +++ b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj @@ -1,7 +1,7 @@  - # DO NOT REFERENCE THIS PACKAGE DIRECTLY! Reference MonoMod.Backports instead. + DO NOT REFERENCE THIS PACKAGE DIRECTLY! Reference MonoMod.Backports instead. $(BackportsTargetFrameworks) diff --git a/src/MonoMod.ILHelpers/System.Runtime.CompilerServices.Unsafe.il b/src/MonoMod.ILHelpers/System.Runtime.CompilerServices.Unsafe.il index fd0fadb7..99f9440e 100644 --- a/src/MonoMod.ILHelpers/System.Runtime.CompilerServices.Unsafe.il +++ b/src/MonoMod.ILHelpers/System.Runtime.CompilerServices.Unsafe.il @@ -19,7 +19,11 @@ #ifdef NET6_0_OR_GREATER #ifdef NET6_0 -.assembly extern System.Runtime.CompilerServices.Unsafe { .ver CORE_ASSEMBLY_VERSION } +.assembly extern System.Runtime.CompilerServices.Unsafe +{ + .publickeytoken = CORE_PUBKEY_TOKEN + .ver CORE_ASSEMBLY_VERSION +} #endif .class extern forwarder System.Runtime.CompilerServices.Unsafe From 5ff9a7313cd7018b3e9e0cbe625d6c02ad21075c Mon Sep 17 00:00:00 2001 From: DaNike Date: Sat, 8 Jun 2024 03:18:28 -0500 Subject: [PATCH 133/133] Remove cycle in NuGet packages --- src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj index 0312ce4c..6ad3de21 100644 --- a/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj +++ b/src/MonoMod.ILHelpers/MonoMod.ILHelpers.ilproj @@ -11,6 +11,7 @@ + - +