使用通过 .NET Core 和 Xamarin 从 IL 编译的程序集
Using assemblies compiled from IL with .NET Core & Xamarin
更新了适合我的解决方案。见本题底部。
上下文:
我需要一种方法来评估泛型类型的大小,以便计算适合特定字节大小的数组长度。基本上,
sizeof
与 C/C++ 提供的类似。
C# 的 sizeof
和 Marshal.SizeOf 不适合这个,因为它们有很多限制。
考虑到这一点,我用 IL 编写了一个程序集,它通过 sizeof
操作码实现了我正在寻找的功能。我知道它基本上评估为 IntPtr.Size
引用类型。
我为 .NET Standard & Core 复制了这个,引用了我认为是 mscorlib 的正确等价物。请注意,IL 编译正常,这个问题是关于另一个问题的。
代码:
Headers 每个目标框架:
.NET: (Windows\Microsoft.NET\Framework\v4.0.30319\ilasm.exe)
.assembly extern mscorlib {}
.NET 标准:(从 nuget 中提取的 ilasm)
.assembly extern netstandard
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89)
.ver 0:0:0:0
}
.assembly extern System.Runtime
{
.ver 0:0:0:0
}
.NET Core:(与标准的 ilasm 相同,尽管我已经对两者进行了测试)
.assembly extern System.Runtime
{
.ver 0:0:0:0
}
来源:
.assembly Company.IL
{
.ver 0:0:1:0
}
.module Company.IL.dll
// CORE is a define for mscorlib, netstandard, and System.Runtime
.class public abstract sealed auto ansi beforefieldinit
Company.IL.Embedded extends [CORE]System.Object
{
.method public hidebysig specialname rtspecialname instance void
.ctor() cil managed
{
.maxstack 8
ldarg.0
call instance void [CORE]System.Object::.ctor()
ret
}
.method public hidebysig static uint32
SizeOf<T>() cil managed
{
sizeof !!0
ret
}
}
问题:
当 .NET Core 或 Xamarin 应用程序引用以这种方式编译的任何 dll 时,我收到以下错误:
The type 'Object' is defined in an assembly that is not referenced.
You must add a reference to assembly 'System.Runtime, Version=0.0.0.0,
Culture=neutral, PublicKeyToken=null'.
当此类 dll 被 .NET 项目或 .NET 标准库引用然后被 .NET 项目引用时,不会发生此问题。
我已经阅读了无数文章、帖子和存储库,详细介绍了不同版本和程序集的此错误。典型的解决方案似乎是添加对目标框架等效于 mscorlib(破坏可移植性)的显式引用。似乎缺少有关为 .NET Standard & Core 使用 IL 编译程序集的信息。
据我了解,.NET Standard 和 Core 使用外观来转发类型定义,因此它们可以由目标框架的运行时解析,从而实现可移植性。
我试过以下方法:
System.Runtime
的显式版本
- 反汇编从 C# 编译的 .NET Core 库,使用完全相同的引用。奇怪的是,它们似乎以显式版本为目标(例如 .NET Core 2.0 中的 System.Runtime 4.2)。
- 使用 Emit API 动态生成代码。编译到内存似乎可行,但不是一个选项,因为我也以 Xamarin.iOS(仅限 AOT)为目标。从磁盘引用此类动态编译的程序集会导致与我手动编译它们相同的错误。
更新:
我尝试了 (following the build instructions here), yet couldn't configure my system to compile corefx with the build script or VS 2017. However, after digging through the code of System.Runtime.CompilerServices.Unsafe 中的解决方案,我发现了一个解决方案。
这似乎很明显,但我引用了错误版本的 System.Runtime
。
Headers 每个目标框架(从 corefx 复制):
.NET:
#define CORELIB "mscorlib"
.assembly extern CORELIB {}
.NET 标准:
#define CORELIB "System.Runtime"
#define netcoreapp
// Metadata version: v4.0.30319
.assembly extern CORELIB
{
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )
.ver 4:0:0:0
}
.NET 核心:
#define CORELIB "System.Runtime"
// Metadata version: v4.0.30319
.assembly extern CORELIB
{
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )
.ver 4:0:0:0
}
在所有源文件中,使用 CORELIB
来引用 mscorlib 中的类型(即 [CORELIB]System.Object
)。
在 DotNet CoreFX 存储库中有一个很好的示例说明如何正确执行此操作。 System.Runtime.CompilerServices.Unsafe
是仅 IL 程序集,可由 .NET Core 和 Xamarin 使用。
https://github.com/dotnet/corefx/tree/master/src/System.Runtime.CompilerServices.Unsafe
有两种方法可以解决该问题:(i) 尝试从头开始在您的项目中重新创建所需的构建配置元素 - 这将非常耗时且非常复杂 - corefx 构建系统非常复杂,(ii)使用现有的构建基础设施并通过复制 System.Runtime.CompilerServices.Unsafe
项目、更改命名并用您的代码替换 IL 代码,在 .NET Core CoreFX 存储库中创建您的 IL 项目。在我看来,这是构建基于 IL 的程序集的最快方法,可以保证在所有目标上正常工作。
要在针对任何特定版本的 .NET Core 或 .NET Standard 构建程序集,只需在发布分支中创建它:release/2.0.0
、release/1.1.0
等
The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
有一个选项可以尝试在触发它的引用程序集的项目中单独抑制该编译错误。将以下 属性 转换为新格式 csproj/vbproj 应该会抑制它:
<PropertyGroup>
<_HasReferenceToSystemRuntime>true</_HasReferenceToSystemRuntime>
</PropertyGroup>
好的,这是我研究的结果(基于https://github.com/dotnet/corefx/tree/master/src/System.Runtime.CompilerServices.Unsafe and https://github.com/jvbsl/ILProj):
global.json
:
{
"msbuild-sdks": {
"Microsoft.NET.Sdk.IL": "3.0.0-preview-27318-01"
}
}
ILProj\ILProj.ilproj
:
<Project Sdk="Microsoft.NET.Sdk.IL">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<MicrosoftNetCoreIlasmPackageVersion>3.0.0-preview-27318-01</MicrosoftNetCoreIlasmPackageVersion>
<IncludePath Condition="'$(TargetFramework)' == 'netstandard1.0'">include\netstandard</IncludePath>
<IncludePath Condition="'$(TargetFramework)' == 'netstandard2.0'">include\netstandard</IncludePath>
<IncludePath Condition="'$(TargetFramework)' == 'netcoreapp1.0'">include\netcoreapp</IncludePath>
<IncludePath Condition="'$(TargetFramework)' == 'netcoreapp2.0'">include\netcoreapp</IncludePath>
<IlasmFlags>$(IlasmFlags) -INCLUDE=$(IncludePath)</IlasmFlags>
</PropertyGroup>
</Project>
ILProj\include\netstandard\coreassembly.h
:
#define CORE_ASSEMBLY "System.Runtime"
// Metadata version: v4.0.30319
.assembly extern CORE_ASSEMBLY
{
.publickeytoken = ( B0 3F 5F 7F 11 D5 0A 3A )
.ver 4:0:0:0
}
ILProj\Class.il
#include "coreassembly.h"
.assembly ILProj
{
.ver 1:0:0:0
}
.module ILProj.dll
.class public abstract auto ansi sealed beforefieldinit ILProj.Class
extends [CORE_ASSEMBLY]System.Object
{
.method public hidebysig static int32 Square(int32 a) cil managed
{
.maxstack 2
ldarg.0
dup
mul
ret
}
}
如果你很难把它们放在一起,那么看看这里的例子:https://github.com/Konard/ILProj
我学习了最近的 System.Runtime.CompilerServices.Unsafe 项目,并创建了具有多目标(.NET Core 6、NET Framework 4.8、netstandard2.0、netcoreapp3.1)和两种签名类型的工作示例
您需要创建文件global.json
{
"msbuild-sdks": {
"Microsoft.NET.Sdk.IL": "6.0.0"
}
}
然后黑魔法开始了:
<Project Sdk="Microsoft.NET.Sdk.IL">
<PropertyGroup>
<TargetFrameworks>net6.0;netcoreapp3.1;netstandard2.0;net48</TargetFrameworks>
<DebugOptimization>IMPL</DebugOptimization>
<DebugOptimization Condition="'$(Configuration)' == 'Release'">OPT</DebugOptimization>
<CoreCompileDependsOn>$(CoreCompileDependsOn);GenerateVersionFile</CoreCompileDependsOn>
<IlasmFlags>$(IlasmFlags) -DEBUG=$(DebugOptimization)</IlasmFlags>
<IsPackable>true</IsPackable>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<Version>1.0.0-beta.1</Version>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) == '.NETCoreApp'">
<ExtraMacros>#define netcoreapp</ExtraMacros>
<CoreAssembly>System.Runtime</CoreAssembly>
<_FeaturePublicSign>true</_FeaturePublicSign>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) == '.NETStandard'">
<CoreAssembly>netstandard</CoreAssembly>
<_FeaturePublicSign>true</_FeaturePublicSign>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) == '.NETFramework'">
<CoreAssembly>mscorlib</CoreAssembly>
<_FeaturePublicSign>false</_FeaturePublicSign>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net6.0'">
<_FeatureUsePopCount>true</_FeatureUsePopCount>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'!='net6.0'">
<_FeatureUsePopCount>false</_FeatureUsePopCount>
</PropertyGroup>
<PropertyGroup Condition="'$(_FeaturePublicSign)'=='true'">
<AssemblyOriginatorKeyFile>..\solar_pub.snk</AssemblyOriginatorKeyFile>
<PublicSign>True</PublicSign>
</PropertyGroup>
<PropertyGroup Condition="'$(_FeaturePublicSign)'!='true'">
<AssemblyOriginatorKeyFile>..\solar.snk</AssemblyOriginatorKeyFile>
<DelaySign>false</DelaySign>
</PropertyGroup>
<ItemGroup Condition="'$(_FeatureUsePopCount)'=='true'">
<Compile Include="FlagEnumUtil.PopCount.msil"/>
</ItemGroup>
<ItemGroup Condition="'$(_FeatureUsePopCount)'!='true'">
<Compile Include="FlagEnumUtil.NoPopCount.msil"/>
</ItemGroup>
<ItemGroup>
<!-- mscorlib is passed in as an explicit reference from C# targets but not via the IL SDK. -->
<Reference Include="$(CoreAssembly)"
Condition="'$(TargetFrameworkIdentifier)' != '.NETStandard'" />
</ItemGroup>
<Target Name="GenerateVersionFile"
DependsOnTargets="GetAssemblyVersion;ResolveReferences"
Inputs="$(MSBuildAllProjects)"
Outputs="'$(VersionFilePath)">
<PropertyGroup>
<IncludePath>$([MSBuild]::NormalizeDirectory('$(IntermediateOutputPath)', 'version'))</IncludePath>
<IncludePathTrimmed>$(IncludePath.TrimEnd('\'))</IncludePathTrimmed>
<IlasmFlags>$(IlasmFlags) -INCLUDE="$(IncludePathTrimmed)"</IlasmFlags>
<VersionFilePath Condition="'$(VersionFilePath)' == ''">$([MSBuild]::NormalizePath('$(IncludePath)', 'version.h'))</VersionFilePath>
<_AssemblyVersion>$(AssemblyVersion.Replace('.', ':'))</_AssemblyVersion>
<_coreAssemblyName Condition="'%(ReferencePath.FileName)' == '$(CoreAssembly)'">%(ReferencePath.FusionName)</_coreAssemblyName>
<_assemblyNamePattern><![CDATA[[^,]+, Version=(?<v1>[0-9]+)\.(?<v2>[0-9]+)\.(?<v3>[0-9]+)\.(?<v4>[0-9]+), .*PublicKeyToken=(?<p1>[0-9a-f]{2})(?<p2>[0-9a-f]{2})(?<p3>[0-9a-f]{2})(?<p4>[0-9a-f]{2})(?<p5>[0-9a-f]{2})(?<p6>[0-9a-f]{2})(?<p7>[0-9a-f]{2})(?<p8>[0-9a-f]{2})]]></_assemblyNamePattern>
<_coreAssemblyVersion>
$([System.Text.RegularExpressions.Regex]::Replace(
$(_coreAssemblyName),
$(_assemblyNamePattern),
'${v1}:${v2}:${v3}:${v4}'))
</_coreAssemblyVersion>
<_coreAssemblyVersionTrimmed>$(_coreAssemblyVersion.Trim())</_coreAssemblyVersionTrimmed>
<_coreAssemblyPublicKeyToken>
$([System.Text.RegularExpressions.Regex]::Replace(
$(_coreAssemblyName),
$(_assemblyNamePattern),
'${p1} ${p2} ${p3} ${p4} ${p5} ${p6} ${p7} ${p8}').ToUpperInvariant())
</_coreAssemblyPublicKeyToken>
<_VersionFileContents>
<![CDATA[
#define CORE_ASSEMBLY "$(CoreAssembly)"
#define ASSEMBLY_VERSION "$(_AssemblyVersion)"
#define CORE_ASSEMBLY_VERSION "$(_coreAssemblyVersionTrimmed)"
#define FILE_VERSION "{string('$(FileVersion)')}"
#define INFORMATIONAL_VERSION "{string('$(InformationalVersion)')}"
$(ExtraMacros)
// Metadata version: v4.0.30319
.assembly extern CORE_ASSEMBLY
{
.publickeytoken = ($(_coreAssemblyPublicKeyToken) )
.ver CORE_ASSEMBLY_VERSION
}
]]>
</_VersionFileContents>
</PropertyGroup>
<Message Importance="high" Text="Building:$(TargetFramework) $([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) CoreAssembly $(CoreAssembly)#$(_coreAssemblyVersionTrimmed) PublicSign=$(_FeaturePublicSign) PopCount=$(_FeatureUsePopCount)"/>
<WriteLinesToFile
File="$(VersionFilePath)"
Lines="$(_VersionFileContents)"
Overwrite="true"
WriteOnlyWhenDifferent="true" />
<ItemGroup>
<FileWrites Include="$(VersionFilePath)" />
</ItemGroup>
</Target>
<!-- Decompile the ILResourceReference to get native resources. -->
<Target Name="SetILResourceReference"
BeforeTargets="DisassembleIlasmResourceFile"
Condition="'@(ResolvedMatchingContract)' != ''">
<ItemGroup>
<ILResourceReference Include="@(ResolvedMatchingContract)" />
</ItemGroup>
</Target>
</Project>
我们来解释一下这个项目文件
- DebugOptimization - 我们仅针对发布使用 OPT 优化
- CoreCompileDependsOn - 我们需要使用主程序集名称和版本生成 version.h 文件(在目标
GenerateVersionFile
中生成)
- IlasmFlags - 用于将 DebugOptimization 传递给 ilasm 工具
- IsPackable - 不工作,但它必须创建 nuget 包
- ProduceReferenceAssembly - 我们需要禁用 ref 程序集搜索 .NET 6
- 然后我们需要为不同的框架定义一些编译时常量
- 特殊文件
version.h
看起来像一个C++头文件并且可以包含一些宏,我们用它来掩码real mscorlib/System.运行时程序集名称。 MSBuild 中的 .NET 内联代码在生成的 version.h
文件中检测平台和 fil 宏值(这是在最近的
- 我为不同的源包含规则使用了两个扩展名(所有*.il 文件都是自动编译的,所有*.msil 文件都是手动包含的)
您可以在以下位置查看整个项目(包括泛型、枚举和测试):
更新了适合我的解决方案。见本题底部。
上下文:
我需要一种方法来评估泛型类型的大小,以便计算适合特定字节大小的数组长度。基本上,
sizeof
与 C/C++ 提供的类似。
C# 的 sizeof
和 Marshal.SizeOf 不适合这个,因为它们有很多限制。
考虑到这一点,我用 IL 编写了一个程序集,它通过 sizeof
操作码实现了我正在寻找的功能。我知道它基本上评估为 IntPtr.Size
引用类型。
我为 .NET Standard & Core 复制了这个,引用了我认为是 mscorlib 的正确等价物。请注意,IL 编译正常,这个问题是关于另一个问题的。
代码:
Headers 每个目标框架:
.NET: (Windows\Microsoft.NET\Framework\v4.0.30319\ilasm.exe)
.assembly extern mscorlib {}
.NET 标准:(从 nuget 中提取的 ilasm)
.assembly extern netstandard
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89)
.ver 0:0:0:0
}
.assembly extern System.Runtime
{
.ver 0:0:0:0
}
.NET Core:(与标准的 ilasm 相同,尽管我已经对两者进行了测试)
.assembly extern System.Runtime
{
.ver 0:0:0:0
}
来源:
.assembly Company.IL
{
.ver 0:0:1:0
}
.module Company.IL.dll
// CORE is a define for mscorlib, netstandard, and System.Runtime
.class public abstract sealed auto ansi beforefieldinit
Company.IL.Embedded extends [CORE]System.Object
{
.method public hidebysig specialname rtspecialname instance void
.ctor() cil managed
{
.maxstack 8
ldarg.0
call instance void [CORE]System.Object::.ctor()
ret
}
.method public hidebysig static uint32
SizeOf<T>() cil managed
{
sizeof !!0
ret
}
}
问题:
当 .NET Core 或 Xamarin 应用程序引用以这种方式编译的任何 dll 时,我收到以下错误:
The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
当此类 dll 被 .NET 项目或 .NET 标准库引用然后被 .NET 项目引用时,不会发生此问题。
我已经阅读了无数文章、帖子和存储库,详细介绍了不同版本和程序集的此错误。典型的解决方案似乎是添加对目标框架等效于 mscorlib(破坏可移植性)的显式引用。似乎缺少有关为 .NET Standard & Core 使用 IL 编译程序集的信息。
据我了解,.NET Standard 和 Core 使用外观来转发类型定义,因此它们可以由目标框架的运行时解析,从而实现可移植性。
我试过以下方法:
System.Runtime
的显式版本
- 反汇编从 C# 编译的 .NET Core 库,使用完全相同的引用。奇怪的是,它们似乎以显式版本为目标(例如 .NET Core 2.0 中的 System.Runtime 4.2)。
- 使用 Emit API 动态生成代码。编译到内存似乎可行,但不是一个选项,因为我也以 Xamarin.iOS(仅限 AOT)为目标。从磁盘引用此类动态编译的程序集会导致与我手动编译它们相同的错误。
更新:
我尝试了
这似乎很明显,但我引用了错误版本的 System.Runtime
。
Headers 每个目标框架(从 corefx 复制):
.NET:
#define CORELIB "mscorlib"
.assembly extern CORELIB {}
.NET 标准:
#define CORELIB "System.Runtime"
#define netcoreapp
// Metadata version: v4.0.30319
.assembly extern CORELIB
{
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )
.ver 4:0:0:0
}
.NET 核心:
#define CORELIB "System.Runtime"
// Metadata version: v4.0.30319
.assembly extern CORELIB
{
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )
.ver 4:0:0:0
}
在所有源文件中,使用 CORELIB
来引用 mscorlib 中的类型(即 [CORELIB]System.Object
)。
在 DotNet CoreFX 存储库中有一个很好的示例说明如何正确执行此操作。 System.Runtime.CompilerServices.Unsafe
是仅 IL 程序集,可由 .NET Core 和 Xamarin 使用。
https://github.com/dotnet/corefx/tree/master/src/System.Runtime.CompilerServices.Unsafe
有两种方法可以解决该问题:(i) 尝试从头开始在您的项目中重新创建所需的构建配置元素 - 这将非常耗时且非常复杂 - corefx 构建系统非常复杂,(ii)使用现有的构建基础设施并通过复制 System.Runtime.CompilerServices.Unsafe
项目、更改命名并用您的代码替换 IL 代码,在 .NET Core CoreFX 存储库中创建您的 IL 项目。在我看来,这是构建基于 IL 的程序集的最快方法,可以保证在所有目标上正常工作。
要在针对任何特定版本的 .NET Core 或 .NET Standard 构建程序集,只需在发布分支中创建它:release/2.0.0
、release/1.1.0
等
The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
有一个选项可以尝试在触发它的引用程序集的项目中单独抑制该编译错误。将以下 属性 转换为新格式 csproj/vbproj 应该会抑制它:
<PropertyGroup>
<_HasReferenceToSystemRuntime>true</_HasReferenceToSystemRuntime>
</PropertyGroup>
好的,这是我研究的结果(基于https://github.com/dotnet/corefx/tree/master/src/System.Runtime.CompilerServices.Unsafe and https://github.com/jvbsl/ILProj):
global.json
:
{
"msbuild-sdks": {
"Microsoft.NET.Sdk.IL": "3.0.0-preview-27318-01"
}
}
ILProj\ILProj.ilproj
:
<Project Sdk="Microsoft.NET.Sdk.IL">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<MicrosoftNetCoreIlasmPackageVersion>3.0.0-preview-27318-01</MicrosoftNetCoreIlasmPackageVersion>
<IncludePath Condition="'$(TargetFramework)' == 'netstandard1.0'">include\netstandard</IncludePath>
<IncludePath Condition="'$(TargetFramework)' == 'netstandard2.0'">include\netstandard</IncludePath>
<IncludePath Condition="'$(TargetFramework)' == 'netcoreapp1.0'">include\netcoreapp</IncludePath>
<IncludePath Condition="'$(TargetFramework)' == 'netcoreapp2.0'">include\netcoreapp</IncludePath>
<IlasmFlags>$(IlasmFlags) -INCLUDE=$(IncludePath)</IlasmFlags>
</PropertyGroup>
</Project>
ILProj\include\netstandard\coreassembly.h
:
#define CORE_ASSEMBLY "System.Runtime"
// Metadata version: v4.0.30319
.assembly extern CORE_ASSEMBLY
{
.publickeytoken = ( B0 3F 5F 7F 11 D5 0A 3A )
.ver 4:0:0:0
}
ILProj\Class.il
#include "coreassembly.h"
.assembly ILProj
{
.ver 1:0:0:0
}
.module ILProj.dll
.class public abstract auto ansi sealed beforefieldinit ILProj.Class
extends [CORE_ASSEMBLY]System.Object
{
.method public hidebysig static int32 Square(int32 a) cil managed
{
.maxstack 2
ldarg.0
dup
mul
ret
}
}
如果你很难把它们放在一起,那么看看这里的例子:https://github.com/Konard/ILProj
我学习了最近的 System.Runtime.CompilerServices.Unsafe 项目,并创建了具有多目标(.NET Core 6、NET Framework 4.8、netstandard2.0、netcoreapp3.1)和两种签名类型的工作示例
您需要创建文件global.json
{
"msbuild-sdks": {
"Microsoft.NET.Sdk.IL": "6.0.0"
}
}
然后黑魔法开始了:
<Project Sdk="Microsoft.NET.Sdk.IL">
<PropertyGroup>
<TargetFrameworks>net6.0;netcoreapp3.1;netstandard2.0;net48</TargetFrameworks>
<DebugOptimization>IMPL</DebugOptimization>
<DebugOptimization Condition="'$(Configuration)' == 'Release'">OPT</DebugOptimization>
<CoreCompileDependsOn>$(CoreCompileDependsOn);GenerateVersionFile</CoreCompileDependsOn>
<IlasmFlags>$(IlasmFlags) -DEBUG=$(DebugOptimization)</IlasmFlags>
<IsPackable>true</IsPackable>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<Version>1.0.0-beta.1</Version>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) == '.NETCoreApp'">
<ExtraMacros>#define netcoreapp</ExtraMacros>
<CoreAssembly>System.Runtime</CoreAssembly>
<_FeaturePublicSign>true</_FeaturePublicSign>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) == '.NETStandard'">
<CoreAssembly>netstandard</CoreAssembly>
<_FeaturePublicSign>true</_FeaturePublicSign>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) == '.NETFramework'">
<CoreAssembly>mscorlib</CoreAssembly>
<_FeaturePublicSign>false</_FeaturePublicSign>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net6.0'">
<_FeatureUsePopCount>true</_FeatureUsePopCount>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'!='net6.0'">
<_FeatureUsePopCount>false</_FeatureUsePopCount>
</PropertyGroup>
<PropertyGroup Condition="'$(_FeaturePublicSign)'=='true'">
<AssemblyOriginatorKeyFile>..\solar_pub.snk</AssemblyOriginatorKeyFile>
<PublicSign>True</PublicSign>
</PropertyGroup>
<PropertyGroup Condition="'$(_FeaturePublicSign)'!='true'">
<AssemblyOriginatorKeyFile>..\solar.snk</AssemblyOriginatorKeyFile>
<DelaySign>false</DelaySign>
</PropertyGroup>
<ItemGroup Condition="'$(_FeatureUsePopCount)'=='true'">
<Compile Include="FlagEnumUtil.PopCount.msil"/>
</ItemGroup>
<ItemGroup Condition="'$(_FeatureUsePopCount)'!='true'">
<Compile Include="FlagEnumUtil.NoPopCount.msil"/>
</ItemGroup>
<ItemGroup>
<!-- mscorlib is passed in as an explicit reference from C# targets but not via the IL SDK. -->
<Reference Include="$(CoreAssembly)"
Condition="'$(TargetFrameworkIdentifier)' != '.NETStandard'" />
</ItemGroup>
<Target Name="GenerateVersionFile"
DependsOnTargets="GetAssemblyVersion;ResolveReferences"
Inputs="$(MSBuildAllProjects)"
Outputs="'$(VersionFilePath)">
<PropertyGroup>
<IncludePath>$([MSBuild]::NormalizeDirectory('$(IntermediateOutputPath)', 'version'))</IncludePath>
<IncludePathTrimmed>$(IncludePath.TrimEnd('\'))</IncludePathTrimmed>
<IlasmFlags>$(IlasmFlags) -INCLUDE="$(IncludePathTrimmed)"</IlasmFlags>
<VersionFilePath Condition="'$(VersionFilePath)' == ''">$([MSBuild]::NormalizePath('$(IncludePath)', 'version.h'))</VersionFilePath>
<_AssemblyVersion>$(AssemblyVersion.Replace('.', ':'))</_AssemblyVersion>
<_coreAssemblyName Condition="'%(ReferencePath.FileName)' == '$(CoreAssembly)'">%(ReferencePath.FusionName)</_coreAssemblyName>
<_assemblyNamePattern><![CDATA[[^,]+, Version=(?<v1>[0-9]+)\.(?<v2>[0-9]+)\.(?<v3>[0-9]+)\.(?<v4>[0-9]+), .*PublicKeyToken=(?<p1>[0-9a-f]{2})(?<p2>[0-9a-f]{2})(?<p3>[0-9a-f]{2})(?<p4>[0-9a-f]{2})(?<p5>[0-9a-f]{2})(?<p6>[0-9a-f]{2})(?<p7>[0-9a-f]{2})(?<p8>[0-9a-f]{2})]]></_assemblyNamePattern>
<_coreAssemblyVersion>
$([System.Text.RegularExpressions.Regex]::Replace(
$(_coreAssemblyName),
$(_assemblyNamePattern),
'${v1}:${v2}:${v3}:${v4}'))
</_coreAssemblyVersion>
<_coreAssemblyVersionTrimmed>$(_coreAssemblyVersion.Trim())</_coreAssemblyVersionTrimmed>
<_coreAssemblyPublicKeyToken>
$([System.Text.RegularExpressions.Regex]::Replace(
$(_coreAssemblyName),
$(_assemblyNamePattern),
'${p1} ${p2} ${p3} ${p4} ${p5} ${p6} ${p7} ${p8}').ToUpperInvariant())
</_coreAssemblyPublicKeyToken>
<_VersionFileContents>
<![CDATA[
#define CORE_ASSEMBLY "$(CoreAssembly)"
#define ASSEMBLY_VERSION "$(_AssemblyVersion)"
#define CORE_ASSEMBLY_VERSION "$(_coreAssemblyVersionTrimmed)"
#define FILE_VERSION "{string('$(FileVersion)')}"
#define INFORMATIONAL_VERSION "{string('$(InformationalVersion)')}"
$(ExtraMacros)
// Metadata version: v4.0.30319
.assembly extern CORE_ASSEMBLY
{
.publickeytoken = ($(_coreAssemblyPublicKeyToken) )
.ver CORE_ASSEMBLY_VERSION
}
]]>
</_VersionFileContents>
</PropertyGroup>
<Message Importance="high" Text="Building:$(TargetFramework) $([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) CoreAssembly $(CoreAssembly)#$(_coreAssemblyVersionTrimmed) PublicSign=$(_FeaturePublicSign) PopCount=$(_FeatureUsePopCount)"/>
<WriteLinesToFile
File="$(VersionFilePath)"
Lines="$(_VersionFileContents)"
Overwrite="true"
WriteOnlyWhenDifferent="true" />
<ItemGroup>
<FileWrites Include="$(VersionFilePath)" />
</ItemGroup>
</Target>
<!-- Decompile the ILResourceReference to get native resources. -->
<Target Name="SetILResourceReference"
BeforeTargets="DisassembleIlasmResourceFile"
Condition="'@(ResolvedMatchingContract)' != ''">
<ItemGroup>
<ILResourceReference Include="@(ResolvedMatchingContract)" />
</ItemGroup>
</Target>
</Project>
我们来解释一下这个项目文件
- DebugOptimization - 我们仅针对发布使用 OPT 优化
- CoreCompileDependsOn - 我们需要使用主程序集名称和版本生成 version.h 文件(在目标
GenerateVersionFile
中生成) - IlasmFlags - 用于将 DebugOptimization 传递给 ilasm 工具
- IsPackable - 不工作,但它必须创建 nuget 包
- ProduceReferenceAssembly - 我们需要禁用 ref 程序集搜索 .NET 6
- 然后我们需要为不同的框架定义一些编译时常量
- 特殊文件
version.h
看起来像一个C++头文件并且可以包含一些宏,我们用它来掩码real mscorlib/System.运行时程序集名称。 MSBuild 中的 .NET 内联代码在生成的version.h
文件中检测平台和 fil 宏值(这是在最近的 - 我为不同的源包含规则使用了两个扩展名(所有*.il 文件都是自动编译的,所有*.msil 文件都是手动包含的)
您可以在以下位置查看整个项目(包括泛型、枚举和测试):