书接上一回(http://www.javashuo.com/article/p-tnurzexd-cd.html)?[手动狗头]html
前段时间折腾了一下,总算是把我本身的图片缓存控件(https://github.com/h82258652/HN.Controls.ImageEx)发布到了 nuget 上,目前已经进入一个比较稳定的版本了,基本没有很严重的 bug 了。其实核心代码早就写完了,后期主要都在折腾持续集成以及自动构建(包含制做 nuget 包)。持续集成使用了 appveyor,园子里也有很多相关的资料,这里我就不说了。git
在制做 nuget 包的过程当中,我折腾了好久,最开始打算直接用 appveyor 的自动打包功能,可是在打包 UWP 的包时,打包出来的版本号一直都是 1.0.0,而 WPF 的就没这问题(这个已确认是 appveyor 的 bug,然而很久都尚未修复(lll¬ω¬))。另外包里的 dll 的版本号也很差统一控制,我发一次版本须要发 3 个 nuget 包,每一个 nuget 包都有一个 dll,手动折腾版本号那不切实际的。github
在通过一番调研以后,我终于发现了一个能知足我需求的工具,Cake,也叫 C# Make,是一个专门用来进行 .net 项目构建的工具。官方网站在此:https://cakebuild.net/shell
接下来就按着教程开始吧。bootstrap
这个是我项目的根目录。接下来咱们在此目录运行 Powershell(能够经过左上角的文件 –> 打开 Windows Powershell 来打开)。而后输入如下命令。windows
Invoke-WebRequest https://cakebuild.net/download/bootstrapper/windows -OutFile build.ps1
而后敲下回车,稍微等待以后,咱们的目录下就会出现一个叫 build.ps1 的文件。缓存
接下来咱们在该目录建立一个叫 build.cake 的空白文件,这个文件主要就是执行构建的逻辑。app
而后在宇宙最强的 IDE,Visual Studio 中进行编写脚本吧,在这里,建议各位看官先安装一个插件:工具
安装以后,Visual Studio 就具有对 .cake 脚本的语法高亮能力(惋惜仍是无法有语法提示功能/(ㄒoㄒ)/~~)。单元测试
使用 Visual Studio 打开 build.cake 以后,先编写个 Hello world 热热身:
var target = Argument("target", "Default"); Task("Default") .Does(() => { Information("Hello World!"); }); RunTarget(target);
而后保存并运行刚才的 build.ps1。(Powershell 里输入 .\build.ps1)
若是运气好的话,那么你应该和我同样碰到了这样的状况:
这是因为执行 Powershell 脚本是一直比较危险的操做,因此须要更改权限。
输入以下的脚本:
或者能够参考微软的官方文档来修改权限:https://docs.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-6
接着再次执行脚本,应该就能够看见以下信息。
咱们的 Hello world 终于跑起来了,热身完毕,接下来开始编写构建脚本。
Cake 脚本是由一个个 Task 串联起来的,咱们先定义一些变量和一个叫 Build 的 Task,用于执行构建。
var target = Argument("target", "Default"); var configuration = Argument("configuration", "Release"); var verbosity = Argument("verbosity", Verbosity.Minimal); var solution = "./HN.Controls.ImageEx.sln"; Task("Build") .Does(() => { if(IsRunningOnWindows()) { // Use MSBuild MSBuild(solution, configurator => configurator.SetConfiguration(configuration) .SetVerbosity(verbosity)); } else { // Use XBuild XBuild(solution, configurator => configurator.SetConfiguration(configuration) .SetVerbosity(verbosity)); } }); Task("Default") .IsDependentOn("Build"); RunTarget(target);
这里定义了一些变量,configuration 定义为 Release,在 Build Task 里用到,设置为使用 Release 模式。verbosity 表示编译时的信息输入,这里设置为 Minimal,以避免输出过多的信息。solution 表示解决方案的路径。而后运行:
这样一执行就把这个解决方案构建了一遍。
而后咱们开始编写打包的 Task,命名为 Package。修改脚本以下:
var target = Argument("target", "Default"); var configuration = Argument("configuration", "Release"); var verbosity = Argument("verbosity", Verbosity.Minimal); var version = Argument("version", "1.0.0"); var solution = "./HN.Controls.ImageEx.sln"; Task("Build") .Does(() => { if(IsRunningOnWindows()) { // Use MSBuild MSBuild(solution, configurator => configurator.SetConfiguration(configuration) .SetVerbosity(verbosity)); } else { // Use XBuild XBuild(solution, configurator => configurator.SetConfiguration(configuration) .SetVerbosity(verbosity)); } }); Task("Package") .IsDependentOn("Build") .Does(() => { var nuGetPackSettings = new NuGetPackSettings { Version = version }; var nuspecFiles = GetFiles("./src/*/*.nuspec"); NuGetPack(nuspecFiles, nuGetPackSettings); }); Task("Default") .IsDependentOn("Package"); RunTarget(target);
在这里我加了一个 version 的变量,在下面打包 nuget 包的时候会用到,统一每一个 nuget 包的版本号。GetFiles("./src/*/*.nuspec") 这个则获取了源文件夹下面的项目下的 nuspec 文件(我这里一个 nuspec 对应一个 csproj,就放到同一个文件夹下了),这个文件的做用是用于描述 nuget 包如何进行打包,具体能够参考本文开头指向的上一篇文章。
执行以后,咱们会获得些 nuget 包(固然对于看官大家的项目须要有 nuspec 才行啦):
编译、打包都说完了。最后就是版本号的问题。nuget 包的版本在上面已经解决了,如今就是 dll 的版本号比较棘手。在我这三个项目中,WPF 和 UWP 都是传统的项目,都是有一个 AssemblyInfo.cs 的文件,而后里面经过 AssemblyVersionAttribute 来设置 dll 的版本号的。可是我这个 Core 的项目是新类型的项目,并无 AssemblyInfo.cs 文件,版本号是在 csproj 里设置的。这难道没办法了么,最后经过万能的 Google 和 StackOverflow,我仍是找到了办法。编辑 csproj 文件,并添加下面一节。
这样,这个项目的版本号等信息就不会从 csproj 里面读取。咱们能够添加本身的 AssemblyInfo.cs 文件进行版本号管理。
如今状况就是每一个项目都有本身的 AssemblyInfo.cs 了,如何统一使用一个 AssemblyInfo.cs 文件来管理这几个项目呢?还记得 Visual Studio 有一个添加引用文件的功能么?
这样几个项目都经过这种方式引用同一个 AssemblyInfo.cs 文件就好了。
最后的问题就是如何将 AssemblyInfo.cs 里的版本号跟 build.cake 脚本里的版本号保持一致。在查阅 Cake 的官方文档后,我发现有一个能生成 AssemblyInfo 的功能。修改咱们的 build.cake 脚本:
var target = Argument("target", "Default"); var configuration = Argument("configuration", "Release"); var verbosity = Argument("verbosity", Verbosity.Minimal); var version = Argument("version", "1.0.0"); var solution = "./HN.Controls.ImageEx.sln"; Task("Version") .Does(() => { var file = "./src/SolutionInfo.cs"; CreateAssemblyInfo(file, new AssemblyInfoSettings { Version = version, FileVersion = version, InformationalVersion = version }); }); Task("Build") .IsDependentOn("Version") .Does(() => { if(IsRunningOnWindows()) { // Use MSBuild MSBuild(solution, configurator => configurator.SetConfiguration(configuration) .SetVerbosity(verbosity)); } else { // Use XBuild XBuild(solution, configurator => configurator.SetConfiguration(configuration) .SetVerbosity(verbosity)); } }); Task("Package") .IsDependentOn("Build") .Does(() => { var nuGetPackSettings = new NuGetPackSettings { Version = version }; var nuspecFiles = GetFiles("./src/*/*.nuspec"); NuGetPack(nuspecFiles, nuGetPackSettings); }); Task("Default") .IsDependentOn("Package"); RunTarget(target);
在 Version Task 中,会生成一个 SolutionInfo.cs 的文件,也就至关于前面的 AssemblyInfo.cs。那如今全部的项目都引用这个文件来进行统一的版本管理就好了。
这样就已经实现了统一版本、构建、打包的一件脚本化了。后续还能够添加上构建完以后执行单元测试以及打包后自动发布到 nuget 的功能,但我本身比较习惯打包完以后本地也测试一下(毕竟 nuget 发了就不能删),因此就暂时不折腾这功能了。
博主图片缓存控件项目的 build.cake 脚本能够参考这里:https://github.com/h82258652/HN.Controls.ImageEx/blob/master/build.cake,虽然配置好了 xunit,可是没有写单元测试就是了,ε=ε=ε=┏(゜ロ゜;)┛
这篇博文主要记录操做步骤,方便之后本身(我还有个微博的库的坑想要填……)。但也但愿看完这篇博文的各位,制做一个 nuget 包并非一件难事,立刻行动把珍藏都弄一份 nuget 包,让各位 .net 开发者也机会用上吧。