当你编译C#、F#或http://VB.NET代码时,编译器不会将其编译成原生代码,而是将其编译成通用中间语言。然后,IL代码被CLR/CoreCLR编译成可以在CPU上运行的机器代码。我想给大家展示的是通过Microsoft.NET.Sdk.IL用纯IL语言创建和构建项目。
在Windows系统上,我们可以使用Visual Studio附带的ilasm.exe来编译我们手写的、符合ECMA-335标准的IL代码文本,那么在Mac上我们应该用什么工具来编译IL代码呢?
有一个有趣的SDK是Microsoft.NET.Sdk.IL。它允许我们手写IL代码来开发.NET(库、应用程序),然后可以通过dotnet CLI来构建ILASM作为桌面.NET Framework、.NET Core或.NETStandard的.NET应用程序。
SDK链接:https://dotnet.myget.org/feed/dotnet-core/package/nuget/Microsoft.NET.Sdk.IL
首先,在项目的顶层目录编写一个Nuget.Config ,关于如何编写Nuget配置文件参见:https://docs.microsoft.com/en-us/nuget/reference/nuget-config-file
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="Microsoft.NET.Sdk.IL" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
<add key="nuget.org" value="https://www.nuget.org/api/v2/" />
</packageSources>
<activePackageSource>
<add key="All" value="(Aggregate source)" />
</activePackageSource>
</configuration>
配置文件中的 <add key="Microsoft.NET.Sdk.IL" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" /> 表示我们要在项目中添加这个SDK的feed,你还可以在项目的顶层目录添加以下global.json文件,说明应该使用这个特定版本的SDK:
{
"msbuild-sdks":
{
"Microsoft.NET.Sdk.IL": "3.0.0-preview1-27101-02"
}
}
有了这样的设置,我们就可以创建项目了。IL项目的扩展名为.ilproj,但在结构上与.csproj非常相似。
在我的例子中,它的结构如下。
<Project Sdk="Microsoft.NET.Sdk.IL">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<MicrosoftNetCoreIlasmPackageVersion>3.0.0-preview1-27101-02</MicrosoftNetCoreIlasmPackageVersion>
<IncludePath Condition="'$(TargetFramework)' == 'netstandard2.0'">includenetstandard</IncludePath>
<IlasmFlags>$(IlasmFlags) -INCLUDE=$(IncludePath)</IlasmFlags>
</PropertyGroup>
</Project>
我这里只为.NET Standard 2.0构建SDK引用,其他框架也是类似做法。对于每个支持的框架,你可以创建一个include文件夹,以便包含某些常用的引用。在我的例子中,我将包含对CorLib的引用。为了做到这一点,我创建了一个includenetstandardcoreassembly.h 路径包含以下内容:
#define CORE_ASSEMBLY "System.Runtime"
.assembly extern CORE_ASSEMBLY
{
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )
.ver 4:0:0:0
}
这时,剩下的就是写一些IL代码了。因此,我创建了一个File.il,首先需要导入core lib,以及定义汇编和模块。这是最基本的需要。
#include "coreassembly.h"
.assembly SampleIL
{
.ver 1:0:0:0
}
.module SampleIL.dll
剩下的是我的常规代码--因为这是一个库而不是一个控制台应用程序,所以我不需要创建一个入口点。不过,我将创建一个Hello类型,并带有一个World()方法:
.class public auto ansi beforefieldinit Hello
extends [CORE_ASSEMBLY]System.Object
{
.method public hidebysig static string World() cil managed
{
ldstr "Hello World!"
ret
}
}
执行命令:
dotnet build <项目文件夹名称>/SampleIL.ilproj
看到类似于下面的内容时表示成功编译了一个.Net Standard 2.0 DLL:
Microsoft (R) Build Engine version 16.5.0+d4cbfca49 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Restore completed in 6.41 sec for /Users/janyee/Documents/dotnet/SampleIL/SampleIL.ilproj.
SampleIL -> /Users/janyee/Documents/dotnet/SampleIL/bin/Debug/netstandard2.0/SampleIL.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:10.64
这时我们就有了一个有效的DLL,我们可以从任何兼容.NET Standard 2.0的项目中加载这个DLL并使用它:
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Hello.World());
}
}