当前位置: 首页 > news >正文

项目文件中的已知 NuGet 属性(使用这些属性,创建 NuGet 包就可以不需要 nuspec 文件啦)

知道了 csproj 文件中的一些常用 NuGet 属性,创建 NuGet 包时就可以充分发挥新 Sdk 自动生成 NuGet 包的优势,不需要 nuspec 文件啦。(毕竟 nuspec 文件没有 .csproj 和 .targets 文件强大而又有扩展性。)


“项目文件中的已知属性系列”分为两个部分:

  • 项目文件中的已知属性(知道了这些,就不会随便在 csproj 中写死常量啦) - 吕毅
  • 本文:项目文件中的已知 NuGet 属性(使用这些属性,创建 NuGet 包就可以不需要 nuspec 文件啦) - 吕毅

NuGet 相关的属性也分为全局属性和项属性两类。不过,我更愿意分成三类来说明:

本文内容

      • nuspec 属性
      • 配置属性
      • 项属性
        • 文件
        • 引用
      • 可能没有开放的内部属性
        • 参考资料

nuspec 属性

当然,这部分的属性也是在 csproj 中使用的,是为了生成 nuspec 文件。

使用方法像这样:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <PackageId>Walterlv.Demo</PackageId>
    <PackageVersion>3.2.0-beta</PackageVersion>
    <TargetFramework>net46</TargetFramework>
  </PropertyGroup>
</Project>

不过我们通常没有这么直接去设置,因为大多数属性都是有默认值的,如果不设置,将自动使用默认值。甚至什么都不写也能生成正确的 nuspec 文件。

  • $(PackageId): NuGet 包的唯一 Id,对应 NuGet 的 Id 属性。这个 Id 需要在整个服务器(例如 nuget.org)上唯一,如果没设置,则使用 $(AssemblyName);例如 Newtonsoft.Json
  • $(PackageVersion): NuGet 包的包版本,可以使用语义版本号(参见语义版本号(Semantic Versioning) - 吕毅),如果没设置,则使用 $(Version);例如 3.2.0-beta
  • $(PackageVersionPrefix): 包版本前缀,默认为空。
  • $(PackageVersionSuffix): 包版本后缀,默认为空。
  • $(Authors): 包的作者;建议指定成在 nuget.org 上的用户名,这样访客可以点击包作者查看到包作者的信息;多个名字用分号分隔。
  • $(Title): 包的显示名称,如果没设置,则使用 $(PackageId)
  • $(PackageDescription): 包的描述文字,如果填写了,则用户在浏览包的时候可以看到。
  • $(Copyright): 包的版权声明
  • $(PackageRequireLicenseAcceptance): 是个布尔值,如果为 true,则在安装包之前要求同意协议。
  • $(PackageLicenseUrl): 此 NuGet 包协议所在的 url。
  • $(PackageProjectUrl): 此 NuGet 包的项目 url。
  • $(PackageIconUrl): 此 NuGet 包的图标 url,无论是 nuget.org 还是 Visual Studio 都将从这个 url 下载包的图标。
  • $(PackageTags): 标签,用分号分隔;指定多个标签有助于用户在 nuget.org 上搜索到你的 NuGet 包。
  • $(PackageReleaseNotes): 这个版本的 Release 记录。
  • $(RepositoryUrl): 仓库 url,例如 https://github.com/dotnet-campus/MSTestEnhancer.git
  • $(RepositoryType): 仓库类型,例如 git、tfs。
  • $(RepositoryBranch): **NuGet 4.7 才开始的新属性!**此包对应的仓库分支,例如 master
  • $(RepositoryCommit): **NuGet 4.7 才开始的新属性!**此包对应的提交号,例如 2d3ef96ee704d7896eeb2d88fbc987b2004ff786
  • $(PackageType): 我还没有理解到此属性的作用。

以上有些信息在每次 NuGet 发布之前都是要改的,例如:$(PackageVersion)$(PackageReleaseNotes)$(RepositoryCommit)。所以很明显——这不是用来给开发者设置的属性,是用于辅助我们生成打包工具的。

配置属性

这些属性会影响生成 NuGet 包的过程。

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

    <!-- 此程序集不可打包,通常在单元测试项目中设置此属性。 -->
    <IsPackable>false</IsPackable>
    <Description></Description>
    <!-- 这是一个仅开发阶段使用的 NuGet 包,详情请参见 https://walterlv.com/post/prevent-nuget-package-been-depended.html。 -->
    <DevelopmentDependency>true</DevelopmentDependency>
    <!-- 单独指定 NuGet 包应该输出到哪个目录(可以跟项目文件的输出目录不一样)。 -->
    <PackageOutputPath></PackageOutputPath>
    <!-- 如果指定为 true,那么还会额外生成 PackageId.symbols.nupkg 包,
         除了原有包的内容外,还额外包含全部的输出文件,以及源码和项目文件,用于调试。 -->
    <IncludeSymbols>true</IncludeSymbols>
    <!-- 大致与 IncludeSymbols 相同,不过只会额外把 pdb 和 Compile 类型的文件打包到 NuGet 包中。
         如果使用 ProjectReference 引用的项目没有指定 TreatAsPackageReference=false,也会一起被打包。 -->
    <IncludeSource>true</IncludeSource>
    <PackageTypes></PackageTypes>
    <!-- 如果指定为 true,那么生成的 dll 将拷贝到 NuGet 包的 tools 目录下。 -->
    <IsTool>true</IsTool>
    <!-- 如果 lib/**/*dll 中没有发现 dll,NuGet 打包过程中会有警告;
         将这个属性设为 true 可以禁用警告;这在制作纯工具型 NuGet 包是非常有用。 -->
    <NoPackageAnalysis>true</NoPackageAnalysis>
    <MinClientVersion></MinClientVersion>
    <IncludeContentInPack></IncludeContentInPack>
    <!-- 默认情况下,项目输出的 dll 会被打包到 lib 目录下;
         设置了此属性后,就可以打包到其他目录下了。此例打包到 task 目录下 -->
    <BuildOutputTargetFolder>tasks</BuildOutputTargetFolder>
    <ContentTargetFolders></ContentTargetFolders>
    
    <!-- 以下属性都是为了使用单独的 nuspec 文件而准备的;如果不使用 nuspec 文件,通常无需设置这些属性。 -->

    <!-- 默认情况下,使用 dotnet pack 打 NuGet 包时,也会顺便编译;
         但设置此值为 true 后,就会像 nuget.exe 那样不进行编译了。 -->
    <NoBuild>true</NoBuild>
    <!-- 默认是 true,如果指定为 false,那么项目编译输出的 dll 文件将不会被打包到 NuGet 包中。 -->
    <IncludeBuildOutput>false</IncludeBuildOutput>
    <!-- 如果需要额外手工编写 nuspec 文件,那么使用此属性指定绝对或相对路径。 -->
    <NuspecFile>Walterlv.Demo.nuspec</NuspecFile>
    <!-- 生成的属性可以时 nuspec 文件中的占位符生效,
         例如 <file src="$SampleProperty$" target="src/" />  -->
    <NuspecProperties>SampleProperty=Program.cs</NuspecProperties>
    <!-- 如果 NuspecFile 使用相对路径,那么就会相对于此路径;通常不需要指定。 -->
    <NuspecBasePath></NuspecBasePath>

  </PropertyGroup>
</Project>

其中,DevelopmentDependency 的设置可参见:帮助官方 NuGet 解掉 Bug,制作绝对不会传递依赖的 NuGet 包 - walterlv。

以上没有设置值和注释的属性,我正在查阅资料。

项属性

文件

为了脱离 nuspec 文件来打包,csproj 中需要对特殊用途的文件设置特别的 NuGet 属性。

例如 Pack 属性可以额外指定一或一组通配符文件需要被打包到 NuGet 包中;PackagePath 则指定了打包到 NuGet 包的路径(NuGet 会通过扩展名来自动识别这是文件夹还是文件,所以可以通过这个属性来重新指定名称,但无法重新指定扩展名)。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <Content Include="readme.txt">
      <Pack>true</Pack>
      <PackagePath>\</PackagePath>
    </Content>
    <Content Include="PackageId.targets">
      <Pack>true</Pack>
      <PackagePath>buildMultiTargeting\</PackagePath>
    </Content>
  </PropertyGroup>
</Project>

引用

引用中也可以加入一些 NuGet 包的生成属性。

无论是 <ProjectReference /> 还是 <PackageReference />,都可以额外加上 <IncludeAssets> <ExcludeAssets> <PrivateAssets> 属性。

使用方法类似这样:

<PackageReference Include="Walterlv.Demo" Version="3.0.0-beta">
  <IncludeAssets>all</IncludeAssets>
  <ExcludeAssets>contentFiles</ExcludeAssets>
  <PrivateAssets>contentFiles;analyzers</PrivateAssets>
</PackageReference>

或者这样:

<PackageReference Include="Walterlv.Demo" Version="3.0.0-beta" PrivateAssets="all" />

不区分大小写。

  • <IncludeAssets> 引用的项目或包中的指定部分是本项目的依赖项。默认为 all
  • <ExcludeAssets> 引用的项目或包中的指定部分不是本项目的依赖项,应该排除。默认为 none
  • <PrivateAssets> 引用的项目或包中的指定部分依然是本项目的依赖项,但是在打 NuGet 包时不作为依赖项(不会传递到下一个项目)。默认为 contentfiles;analyzers;build

如果你正试图用 NuGet 编写一个编译时工具,那么,你可能需要在所有引用的最后加上如下行,将所有的包引用都设为 PrivateAssets

<PackageReference Update="@(PackageReference)" PrivateAssets="All" />

如果你希望了解 Reference PackageReference 以及上面 @ 的含义,可以阅读我的另一篇文章:理解 C# 项目 csproj 文件格式的本质和编译流程 - walterlv。

可能没有开放的内部属性

在 Microsoft.NET.Sdk 中,NuGet 包的打包主要靠的是 NuGet.Build.Tasks.Pack.targets 文件中一个名为 PackTask 的任务来完成的,它是一个使用了非常多参数的 Task

<PackTask PackItem="$(PackProjectInputFile)"
          PackageFiles="@(_PackageFiles)"
          PackageFilesToExclude="@(_PackageFilesToExclude)"
          PackageVersion="$(PackageVersion)"
          PackageId="$(PackageId)"
          Title="$(Title)"
          Authors="$(Authors)"
          Description="$(PackageDescription)"
          Copyright="$(Copyright)"
          RequireLicenseAcceptance="$(PackageRequireLicenseAcceptance)"
          LicenseUrl="$(PackageLicenseUrl)"
          ProjectUrl="$(PackageProjectUrl)"
          IconUrl="$(PackageIconUrl)"
          ReleaseNotes="$(PackageReleaseNotes)"
          Tags="$(PackageTags)"
          DevelopmentDependency="$(DevelopmentDependency)"
          BuildOutputInPackage="@(_BuildOutputInPackage)"
          ProjectReferencesWithVersions="@(_ProjectReferencesWithVersions)"
          TargetPathsToSymbols="@(_TargetPathsToSymbols)"
          TargetFrameworks="@(_TargetFrameworks)"
          AssemblyName="$(AssemblyName)"
          PackageOutputPath="$(PackageOutputAbsolutePath)"
          IncludeSymbols="$(IncludeSymbols)"
          IncludeSource="$(IncludeSource)"
          PackageTypes="$(PackageType)"
          IsTool="$(IsTool)"
          RepositoryUrl="$(RepositoryUrl)"
          RepositoryType="$(RepositoryType)"
          SourceFiles="@(_SourceFiles->Distinct())"
          NoPackageAnalysis="$(NoPackageAnalysis)"
          MinClientVersion="$(MinClientVersion)"
          Serviceable="$(Serviceable)"
          FrameworkAssemblyReferences="@(_FrameworkAssemblyReferences)"
          ContinuePackingAfterGeneratingNuspec="$(ContinuePackingAfterGeneratingNuspec)"
          NuspecOutputPath="$(NuspecOutputAbsolutePath)"
          IncludeBuildOutput="$(IncludeBuildOutput)"
          BuildOutputFolder="$(BuildOutputTargetFolder)"
          ContentTargetFolders="$(ContentTargetFolders)"
          RestoreOutputPath="$(RestoreOutputAbsolutePath)"
          NuspecFile="$(NuspecFileAbsolutePath)"
          NuspecBasePath="$(NuspecBasePath)"
          NuspecProperties="$(NuspecProperties)"
          AllowedOutputExtensionsInPackageBuildOutputFolder="$(AllowedOutputExtensionsInPackageBuildOutputFolder)"
          AllowedOutputExtensionsInSymbolsPackageBuildOutputFolder="$(AllowedOutputExtensionsInSymbolsPackageBuildOutputFolder)"/>
</Target>

所以总结起来我们还有这些 NuGet 的属性还可以配置(想必下划线开头的属性或集合是 NuGet 内部不愿意公开的属性了):

  • $(PackProjectInputFile)
  • @(_PackageFiles)
  • @(_PackageFilesToExclude)
  • $(PackageVersion)
  • $(PackageId)
  • $(Title)
  • $(Authors)
  • $(PackageDescription)
  • $(Copyright)
  • $(PackageRequireLicenseAcceptance)
  • $(PackageLicenseUrl)
  • $(PackageProjectUrl)
  • $(PackageIconUrl)
  • $(PackageReleaseNotes)
  • $(PackageTags)
  • $(DevelopmentDependency)
  • @(_BuildOutputInPackage)
  • @(_ProjectReferencesWithVersions)
  • @(_TargetPathsToSymbols)
  • @(_TargetFrameworks)
  • $(AssemblyName)
  • $(PackageOutputAbsolutePath)
  • $(IncludeSymbols)
  • $(IncludeSource)
  • $(PackageType)
  • $(IsTool)
  • $(RepositoryUrl)
  • $(RepositoryType)
  • @(_SourceFiles->Distinct())
  • $(NoPackageAnalysis)
  • $(MinClientVersion)
  • $(Serviceable)
  • @(_FrameworkAssemblyReferences)
  • $(ContinuePackingAfterGeneratingNuspec)
  • $(NuspecOutputAbsolutePath)
  • $(IncludeBuildOutput)
  • $(BuildOutputTargetFolder)
  • $(ContentTargetFolders)
  • $(RestoreOutputAbsolutePath)
  • $(NuspecFileAbsolutePath)
  • $(NuspecBasePath)
  • $(NuspecProperties)
  • $(AllowedOutputExtensionsInPackageBuildOutputFolder)
  • $(AllowedOutputExtensionsInSymbolsPackageBuildOutputFolder)

这是 .NET Core 2.1 中自带的 NuGet 的打包属性,比 .NET Core 2.0 中多了个 @(_SourceFiles->Distinct())


参考资料

  • NuGet pack and restore as MSBuild targets - Microsoft Docs
  • NuGet PackageReference format (package references in project files) - Microsoft Docs

我的博客会首发于 https://walterlv.com/,而 CSDN 和博客园仅从其中摘选发布,而且一旦发布了就不再更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

相关文章:

  • 理解 C# 项目 csproj 文件格式的本质和编译流程
  • 如何创建一个基于命令行工具的跨平台的 NuGet 工具包
  • 如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包
  • 如何最快速地将旧的 NuGet 包 (2.x, packages.config) 升级成新的 NuGet 包 (4.x, PackageReference)
  • 每次都要重新编译?太慢!让跨平台的 MSBuild/dotnet build 的 Target 支持差量编译
  • C# 中那些可以被重载的操作符,以及使用它们的那些丧心病狂的语法糖
  • 神器如 dnSpy,无需源码也能修改 .NET 程序
  • Roslyn 入门:使用 .NET Core 版本的 Roslyn 编译并执行跨平台的静态的源码
  • .NET Standard / dotnet-core / net472 —— .NET 究竟应该如何大小写?
  • 微软 Windows 系统检测网络连通性(用于显示感叹号)竟然是通过访问一个特殊网址来实现的
  • 如何根据一个绝对文件路径生成一个相对文件路径
  • 如何使用 MSBuild Target(Exec)中的控制台输出
  • C#/.NET 中推荐的 Dispose 模式的实现
  • Windows 10 四月更新,文件夹名称也能区分大小写?
  • WPF 中使用附加属性,将任意 UI 元素或控件裁剪成圆形(椭圆)
  • android高仿小视频、应用锁、3种存储库、QQ小红点动画、仿支付宝图表等源码...
  • CentOS6 编译安装 redis-3.2.3
  • E-HPC支持多队列管理和自动伸缩
  • Essential Studio for ASP.NET Web Forms 2017 v2,新增自定义树形网格工具栏
  • Fabric架构演变之路
  • Go 语言编译器的 //go: 详解
  • HashMap ConcurrentHashMap
  • JS函数式编程 数组部分风格 ES6版
  • Leetcode 27 Remove Element
  • magento 货币换算
  • passportjs 源码分析
  • 简单基于spring的redis配置(单机和集群模式)
  • 聚类分析——Kmeans
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 时间复杂度与空间复杂度分析
  • 最近的计划
  • ​Distil-Whisper:比Whisper快6倍,体积小50%的语音识别模型
  • ​用户画像从0到100的构建思路
  • #单片机(TB6600驱动42步进电机)
  • (1)bark-ml
  • (ibm)Java 语言的 XPath API
  • (webRTC、RecordRTC):navigator.mediaDevices undefined
  • (分布式缓存)Redis哨兵
  • (完整代码)R语言中利用SVM-RFE机器学习算法筛选关键因子
  • (一)RocketMQ初步认识
  • **PHP分步表单提交思路(分页表单提交)
  • .form文件_SSM框架文件上传篇
  • .NET Core SkiaSharp 替代 System.Drawing.Common 的一些用法
  • .net core 连接数据库,通过数据库生成Modell
  • .net 写了一个支持重试、熔断和超时策略的 HttpClient 实例池
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • .net打印*三角形
  • .NET导入Excel数据
  • .NET中的Exception处理(C#)
  • @DependsOn:解析 Spring 中的依赖关系之艺术
  • [ NOI 2001 ] 食物链
  • [android] 请求码和结果码的作用
  • [ArcPy百科]第三节: Geometry信息中的空间参考解析
  • [BUG]Datax写入数据到psql报不能序列化特殊字符