最推荐 dotnet pack 命令,要求项目 SDK 为 Microsoft.NET.Sdk,需配置 TargetFramework 和 Version;非 SDK 项目用 nuget.exe pack;VS GUI 仅适合验证;CI/CD 推荐 dotnet msbuild /t:pack。

用 dotnet pack 命令最直接,但项目 SDK 类型必须是 Microsoft.NET.Sdk
绝大多数现代 C# 项目都适用这个方式,前提是 .csproj 文件开头是 <project sdk="Microsoft.NET.Sdk"></project>。如果不是(比如老式 PackageReference + packages.config 项目),dotnet pack 会静默失败或生成空包。
实操建议:
- 确保
<targetframework></targetframework>或<targetframeworks></targetframeworks>已设置,否则打包会报错NU5026: The 'targetFramework' parameter was not specified - 在项目目录下运行:
dotnet pack --configuration Release --output ./nupkgs,避免默认输出到bin/下混杂文件 - 若需包含 XML 文档,加
<generatedocumentationfile>true</generatedocumentationfile>,否则dotnet pack默认不打包.xml - 版本号优先取自
<version></version>,不是AssemblyVersion;没设<version></version>就 fallback 到1.0.0
用 nuget.exe pack 处理非 SDK 项目或需要精细控制 .nuspec
老项目、含本机 DLL、或要自定义依赖项范围(如仅 net472 运行时)时,nuget.exe pack 更灵活。但它不读 .csproj,必须手动维护 .nuspec 文件,容易和源码脱节。
常见错误现象:
-
Unable to find project file '*.csproj':因为nuget pack默认找.csproj,但你传了.nuspec,得加-NoDefaultExcludes和显式-IncludeReferencedProjects才能拉进依赖项 - 打包后引用时提示
The type or namespace name 'xxx' could not be found:多半是<files></files>节漏写了.dll或路径写错,比如用了相对路径但没配basePath -
nuget.exe版本太低(如 4.x)不支持<packagetype></packagetype>,会导致上传到 NuGet.org 被拒
VS GUI 打包看着方便,但会偷偷改 .csproj 并隐藏关键配置
右键项目 → “打包” 确实一键生成,但它会自动注入 <generatepackageonbuild>true</generatepackageonbuild> 和 <packageoutputpath></packageoutputpath>,下次你用 CLI 打包可能被干扰。更麻烦的是:它默认不打包 contentFiles(如嵌入式 JSON 配置),也不处理 buildTransitive 场景。
使用场景有限:
- 只用于快速验证包结构,比如确认图标、描述是否显示正常
- 调试
.nuspec时临时生成对比,别用它发正式版 - 如果项目启用了
<ispackable>false</ispackable>,VS GUI 会直接灰掉“打包”菜单,连试都试不了
CI/CD 中用 dotnet msbuild /t:pack 更稳定,尤其跨平台构建
GitHub Actions 或 Azure Pipelines 里,dotnet pack 在 macOS/Linux 上有时因路径分隔符或权限出问题;而 dotnet msbuild /t:pack 底层走 MSBuild 引擎,行为更一致,且能复用项目中已定义的 <packageid></packageid>、<authors></authors> 等属性。
性能与兼容性注意点:
- 加
/p:ContinuousIntegrationBuild=true可禁用源链接(SourceLink)相关耗时操作,提速 30%+,适合 PR 构建 - 若项目含
<packagereference include="Microsoft.SourceLink.GitHub" version="..."></packagereference>,不加该参数可能导致 Linux agent 上git权限失败 - 不要在
dotnet build后再跑dotnet pack—— 重复编译浪费时间;直接/t:pack会自动触发 build
真正难的不是选哪种方式,而是搞清「包里到底要放什么」:是只放编译产出,还是带 build/ 目录下的 .targets?要不要 analyzers?这些决定了该用 .csproj 声明式打包,还是手写 .nuspec 控制细节。漏一项,下游引用就可能编译失败,而且错误信息往往不指哪打哪。


















