怎么快速搞定 .NET Core 程序的跨平台独立发布
2018-01-01

.NET Framework 吐槽集合(部分)

从 .NET Framework 1.0 诞生之日起,一个可能被问过几百万遍的问题就出现了,“我写的 .NET 程序怎么才能够直接部署而不依赖 .NET Framework 呢?”那么,这种可能性有吗?

你可能听到的答案其实还蛮多,比如:

  • “做不到!”不论是初学者还是资深人士,给出这个答案都不意外,因为各自的思考方式不同。初学者是不太知道还有什么花头,而资深人士通常可能是懒得给你解释一大堆。
  • “打包 .NET Framework 安装包嘛!”能给你这个标准答案的人,相信已经是趟过这条河了,以后有技术问题,一定记得去找他/她问哈。
  • “用飞信虚拟机!”给出这种答案的人,其实很可能就是你身边隐藏的大神。就这一个名词背后就有太多故事可以大书特书,然而并非本文重点。有兴趣的朋友,可以在文后留言。如果人数足够多,我也不介意再写写。

对于多数个人用户来说,把小则几十兆,大则几百兆的 .NET Framework 安装包做到自己的程序安装包里面,在之前几年都是一个恐怖的事情。且不说网络下载之类以前带宽有限,即使是硬盘空间、光盘空间,也都是要留给其他重要数据(如图片、视频)的嘛,哪里容得下一个无关紧要的运行时。

所以 .NET 程序似乎从一开始就是为企业设计的,只有他们能够毫不厌烦地给机器装上这类东西,保证你的程序能够顺利运行。而独立开发人员,或者面向个人用户的软件开发公司,可能从技术选型那个时候就把 .NET 给放到一边去了。

不得不说 Java 在这方面就偷巧多了,在程序里面塞上一个其实并不小的 JVM,就可以让你毫无察觉(比如 Eclipse 内置 JVM)。

你也不要认为微软自己这么设计它日子就好过了。首先,.NET Framework 就此成为了 Windows 系统上一个定时炸弹,安全补丁什么的,生命周期什么的,其实微软自己也觉得很不好搞。所以从 Windows Server 2003 内置 .NET Framework 1.1 开始,微软自己也是噩梦连连。其次 .NET Framework 很快有了 2.0/3.0/3.5/4.0/4.5/4.5.x/4.6.x/4.7.x这样林林总总的版本,安装和卸载可能导致的不稳定时有发生,于是4.x以上都变成了原地替换安装。是的,其实这也是微软给自己省事,虽然客户一点也不开心。如果算上 Office 插件系统和 IIS 上不同框架版本 .NET 程序的并行执行(side by side),我去,微软自己都快炸了。IIS 还好一点,可以强制每个应用程序池只能用特定版本的 .NET Framework,而 Office 要同时加载多个框架版本的插件,OMG,其实背后麻烦的要死要活。

到这里你就不会奇怪怎么一到了新平台 .NET Core,微软就推荐大家用独立发布(self-contained)模式了吧。那么,这个模式到底怎么玩?

.NET Core 独立发布

其实这个部分真是简单到。。。不能再简单。安装完 .NET Core SDK,找个空目录,试下下面的命令,

dotnet new console

dotnet publish -c Release --self-contained

OK,有没有卡住啊?

/usr/local/share/dotnet/sdk/2.0.2/Sdks/Microsoft.NET.Sdk/build/Microsoft.NET.RuntimeIdentifierInference.targets(116,5): error : It is not supported to build or publish a self-contained application without specifying a RuntimeIdentifier.  Please either specify a RuntimeIdentifier or set SelfContained to false. [/Users/lextm/Projects/selfcontained/selfcontained.csproj]

一点也不要意外,微软这是提醒你还有些东西你需要提供,否则这个部署任务没法搞。

所以这种时候我们就只好打开工程文件看看,

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>

<OutputType>Exe</OutputType>

<TargetFramework>netcoreapp2.0</TargetFramework>

</PropertyGroup>

</Project>

OK,试下把它改成这样,

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>

<OutputType>Exe</OutputType>

<TargetFramework>netcoreapp2.0</TargetFramework>

<RuntimeIdentifiers>win-x86;win-x64;linux-x64;osx-x64</RuntimeIdentifiers>

</PropertyGroup>

</Project>

加入的这一行指定了所有这个程序支持的操作系统平台,依次是:Windows 32位,Windows 64位,Linux 64位,macOS 64位。更多描述符资料,请查看 官方文档 。

下面回到命令行,继续发布,

dotnet publish -c Release --self-contained -r win-x64

Microsoft (R) Build Engine version 15.4.8.50001 for .NET Core

Copyright (C) Microsoft Corporation. All rights reserved.


selfcontained -> /Users/lextm/Projects/selfcontained/bin/Release/netcoreapp2.0/win-x64/selfcontained.dll

selfcontained -> /Users/lextm/Projects/selfcontained/bin/Release/netcoreapp2.0/win-x64/publish/

这样就做好了 Windows 64位需要的程序部署文件了。(别奇怪上面的路径分隔符,因为我是在Mac上面进行 Windows 程序的发布哦。)

打开这个 publish 文件夹,我们瞄下微软都干了什么。

神秘的发布目录神秘的发布目录

++,就 TMD 一个 Hello Kitty。。。World 程序,硬是被塞了216个文件!!!(你的菊花痛了吗?)不过骂归骂,把这个文件夹原样拷贝到 Windows 机器上,然后双击 selfcontained.exe,它还就真的乖乖运行了。(怎么办,这么神奇,只能原谅它咯。)

注意力回到刚才的发布命令,那个 -r 开关就是用来指定目标平台的。如果要给多个平台发布,可以多次执行 dotnet publish,每次换下 -r 后面的描述符。

附赠秘籍:.NET IL Linker

不幸的是,并非所有的程序都如上面那个例子那么纤细苗条。

就问你大不大就问你大不大

呸!就问你大不大,一个 Hello World 六十多兆了。

虽然不太情愿,微软自己也只能承认这实在太不像话了,所以出了一个临时的解决方案,就是 .NET IL Linker。使用方法居然,一点也不复杂(真意外。。。)。

回到工程的根目录,运行

dotnet new nuget

如此一来会创建一个 nuget.config 文件,

<?xml version="1.0" encoding="utf-8"?>

<configuration>

<packageSources>

<!--To inherit the global NuGet package sources remove the <clear/> line below -->

<clear />

</packageSources>

</configuration>

将它修改一下,

<?xml version="1.0" encoding="utf-8"?>

<configuration>

<packageSources>

  <add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />

</packageSources>

</configuration>

这样我们就能够连接到微软自己的测试用 NuGet 源。继续在命令行里面敲,

dotnet add package ILLink.Tasks -v 0.1.4-preview-981901

好了,这样 IL Linker 就装好了。你再回头用 dotnet publish 试试看,比如

dotnet publish -c Release --self-contained -r win-x64 /p:ShowLinkerSizeComparison=true

(注意这里 /p 部分只是让你看看缩小的效果,它不是必须的。)

怎么样,满意否?这回只剩下三十多兆了。

关于未来

从 .NET Core 早期就开始关注它的朋友是绝对不会忘了微软承诺过“一个文件”的发布方式。不得不说微软那是把卫星放早了。不是说微软现在不能给你来下一个文件的部署方式,而是一旦那么玩,什么调试体验啦,什么优化了,什么都没有了。那么你又该回头来骂微软 TMD 的是个大骗子了。

所以,耐心再等等。一个文件的部署方式,至少要等 CoreRT 和 .NET Native 完全做完,而从进度看,还有点遥遥无期咧。。。心酸。。。

写在最后

最近有收到一些朋友私信提问。抱歉,除了一两句能解答的问题,其他复杂的我没法给出答案。方便的话,请前往 Stack Overflow 之类的专业论坛

原文: https://weibo.com/ttarticle/p/show?id=2309404189811899529202