.NET Core离线开发与编译-进阶
这是.NET Core离线开发与编译系列的第二篇。本系列目录如下:
Paket
安装
dotnet tool
管理
最简单的方式是通过dotnet tool
来安装:
安装成全局工具
dotnet tool install --global Paket
这样就可以使用paket
命令了。不过,我并不喜欢这个方式。原因很简单,强迫所有代码仓库都使用同一个版本的Paket
进行管理不够灵活。
好在我们可以把Paket
安装成局部工具:
dotnet new tool-manifest
dotnet tool install Paket
这种情况下,我们可以在每个具体的项目目录下,以dotnet paket
子命令的形式使用Paket
。相比于全局工具安装,这个方式非常方便、干净。
但是,这个方式有两个缺点:
- 在完全断网的情况下,
dotnet tool install
默认需要联网安装。我们当然可以把nuget
包下载到本地来安装,但是这样依然不够方便。 - 此外,
dotnet tool
局部工具对于fsx
的#r "paket: nuget ...."
的支持并不友好。该机制需要会逐级向上查找父目录是否存在.paket/paket.exe
(最终如果没有找到,会再去查找全局的)。但是本地dotnet tool
的方式下并不会把paket.exe
这个二进制文件拷贝到.paket/
下。
解决办法有两个:
- 安装成局部工具的同时指定到
.paket/
路径:dotnet tool install paket --tool-path ./.paket
。这样dotnet tool
就会在仓库目录下创建一个.paket/paket.exe
。注意这时候paket
不会变成dotnet
子命令(dotnet paket
不起作用,需要直接调用.paket/paket.exe
) - 直接拷贝
paket.exe
二进制文件到.paket/
文件夹下。
直接使用paket.exe
二进制文件
除了使用dotnet tool
来安装Paket
,还可以直接使用paket.exe
二进制文件。使用这种方法,我们可以做到在线下载、离线分发和安装。
- 在
GitHub
上下载paket.bootstrapper.exe
——这是一个下载器,会帮我们下载号最新的paket.exe
。 - 在项目根目录下创建一个
.paket
目录,并将paket.bootstrapper.exe
拷贝到其中。 - 运行下载器
.paket/paket.bootstrapper.exe
。下载完成后,Paket
会被保存到路径./.paket/paket.exe
。 - 运行
.paket/paket.exe <子命令>
来调用Paket工具。
代码仓库的项目结构
- .paket/
paket.bootstrapper.exe # paket.exe下载器
paket.exe # 二进制文件
paket.targets
Paket.Restore.targets
- paket.dependencies # 整个代码仓库的依赖定义
- paket.lock # 版本锁定文件
- paket-files/ # 应该加到gitignore中。
- 项目1/
paket.references # 当前子项目的依赖声明
... 其他项目文件
- 项目2/
paket.references # 当前子项目的依赖声明
... 其他项目文件
对于一个代码仓库,所有的依赖是放在一起管理的(由paket.dependencies
定义)。每个子项目的引用是代码仓库依赖的一个子集(由独立的paket.references
声明)。
Paket
命令行工具
初始化:
和npm init
类似,Paket
也有一个init
命令:
dotnet paket init
该命令会替我们创建两个文件:
- 在当前目录下,创建一个
paket.dependencies
依赖文件。 - 在
.paket/
目录下,创建一个paket.targets
项目文件
,供具体的*.csproj
导入
添加Nuget包依赖、安装、与恢复
和dotnet add package <包名>
命令类似,paket
也提供了类似的方式:
dotnet paket add <包名>
该命令会在paket.dependencies
里添加一行依赖声明。
安装依赖:
dotnet paket install
恢复依赖:
dotnet paket restore
离线缓存和本地存储
配置离线
可以用cache
和storage
控制离线功能:
source https://api.nuget.org/v3/index.json
cache ./nuget-caches # 缓存
// storage: none # 注释掉这个,使用默认值,会创建一个传统 packages/ 文件夹。
cache
可以指定一个本地路径或者一个网络共享位置,并把依赖的*.nupkg
缓存到其中。
而storage
则指定了*.nupkg
依赖解压后的本地存储选项。storage
选项可以指定三种值:
storage: packages
默认值,把依赖文件存储到packages/
文件夹下。storage: none
禁用packages/
文件夹,使用 全局NuGet cachestorage: symlink
使用packages/
文件夹,但是使用软连接而非物理文件
如何从离线缓存中恢复
- 首先确保
cache
所指定的文件夹能被离线机器访问。 - 移除
paket-files/
文件夹 - 然后执行
.paket/paket.exe restore
。这时会创建packages/
文件夹 - 执行
dotnet restore --source ./packages
来恢复依赖
FSX
的支持
自.NET5
开始,.NET Core
支持在fsx
中直接通过#r
自动引用相关依赖。FSharp
提供了一个扩展机制来支持Paket
。也即通过#r "paket: nuget..."
的方式引用nupkg
:
#r "paket: nuget NewtonSoft.Json"
open Newtonsoft.Json
let a = {| A = 1 ;B = "a" |}
JsonConvert.SerializeObject(a)
当然,这样做的前提是,为FSharp安装依赖管理器扩展。
FSharp
的依赖管理器扩展
为了让#r
支持paket
扩展,我们需要从 nuget.org 上下载 FSharp.DependencyManager.Paket
,或者在GitHub Release
上下载。然后把其中的FSharp.DependencyManager.Paket.dll
拷贝到FSharp
SDK的目录下。如果你使用的是Windows
操作系统,路径可能长这样:
C:/Program Files/dotnet/sdk/6.0.101/FSharp
一般来说,FSharp
会内置一个FSharp.DependencyManager.Nuget.dll
,当我们把FSharp.DependencyManager.Paket.dll
拷进去后,就会出现两个dll
:
C:/Program Files/dotnet/sdk/6.0.101/FSharp
... // 其他文件和文件夹
FSharp.DependencyManager.Nuget.dll
FSharp.DependencyManager.Paket.dll
... // 其他文件和文件夹
除了这个全局扩展,我们还需要一个可以被发现的Paket.exe
。具体来说,是fsx
文件的父目录或者逐级向上的父目录中要存在.paket/paket.exe
(如果找不到,则会去一些全局位置检索,但是我个人更喜欢逐级向上检索这样的方式)。
dotnet fsi
的执行
执行之前,需要在脚本所在目录下先执行恢复依赖的命令:
.paket/paket.exe restore
当执行dotnet fsi <脚本文件.fsx>
时,会创建一个临时文件,并把相关paket
文件都拷贝进去。然后从cache
路径中恢复依赖到本地packages/
中。
VSCode fsx智能提示
当然,在VSCode
中,还有一个额外的动作才能让智能提示工作:把FSharp.DependencyManager.Paket.dll
也拷贝到vscode
的fsharp
扩展中。举个例子,在我的电脑上,路径可能是~/.vscode/extensions/ionide.ionide-fsharp-5.10.2/bin
。