项目文件中的已知属性(知道了这些,就不会随便在 csproj 中写死常量啦)

项目文件中的已知属性(知道了这些,就不会随便在 csproj 中写死常量啦)

发布于 2018-04-12 13:03 更新于 2018-08-29 01:36

知道了 csproj 文件中的一些常用属性,修改文件的时候就不会写很多的垃圾代码。


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

什么?你的 csproj 文件太长不想看?说明你用了旧格式的 csproj,阅读我的另一篇文章 将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成基于 Microsoft.NET.Sdk 的新 csproj 将它转为新格式之后,你就会觉得这么简短精炼的 csproj 文件,真不忍将它写杂。

比如通过以下写法,可以将所有的 *.xaml.cs 文件折叠到对应的 *.xaml 文件下,而不需要像旧 csproj 格式那样每个文件都写一份:

<Compile Update="**\*.xaml.cs"> <DependentUpon>%(Filename)</DependentUpon> </Compile>

编译上下文

以下属性是基本的输出路径属性,可以在 Microsoft.NET.DefaultOutputPaths.targets 找到。

  • $(Configuration)
    • 这就是我们传说中决定 Debug 还是 Release 的属性。如果没有指定,默认是 Debug。本身没有什么意义,因为各种其他行为判断了这个属性的值,于是就有了编译差别。
  • $(Platform)
    • 默认是 AnyCPU,还可以是 x86x64 或者 ARM
  • $(BaseOutputPath)
    • 输出路径的起始位置。如果没有指定,就是 bin\。修改这个属性可以间接修改 OutputPath
  • $(OutputPath)
    • 输出路径,默认有两种可能的值。如果 AnyCPU 编译,就是 $(BaseOutputPath)$(Configuration)\;否则就是 $(BaseOutputPath)$(PlatformName)\$(Configuration)\
  • $(BaseIntermediateOutputPath)
    • 临时生成路径的起始位置。如果没有指定,就是 obj\。修改这个属性可以间接修改 IntermediateOutputPath
  • $(IntermediateOutputPath)
    • 临时生成路径,默认有两种可能的值。如果 AnyCPU 编译,就是 $(BaseIntermediateOutputPath)$(Configuration)\;否则就是 $(BaseIntermediateOutputPath)$(PlatformName)\$(Configuration)\

额外的,如果你试图在编译期间使用 dll,你可能需要判断运行时环境:

  • $(MSBuildRuntimeType)
    • 例如你可以使用 Condition=" '$(MSBuildRuntimeType)' == 'Core'" 来判断当前编译环境是否是 .NET Core。

以下属性控制哪些文件应该被默认包含在编译中,可以在 Microsoft.NET.TargetFrameworkInference.targets 找到。

  • $(EnableDefaultItems)
    • 默认为 true,如果指定为 false,那么就不自动将 .cs 和 .resx 文件引入。
  • $(DefaultItemExcludes)
    • 默认为输出路径(OutputPath)和临时生成路径(IntermediateOutputPath)下的所有文件。
  • $(AppendTargetFrameworkToOutputPath)
    • 默认我们生成路径会包含 net47 或者 netcoreapp2.1 这样的一层文件夹,如果指定为 false,这一层文件夹就不会生成了。

下面是 Microsoft.NET.Sdk 中的一部分源码,在 Microsoft.NET.Sdk.DefaultItems.props 文件中,可以发现还有更多与控制自动引入文件相关的属性。

<ItemGroup Condition=" '$(EnableDefaultItems)' == 'true' ">
  <Compile Include="**/*$(DefaultLanguageSourceExtension)" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" Condition=" '$(EnableDefaultCompileItems)' == 'true' " />
  <EmbeddedResource Include="**/*.resx" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" Condition=" '$(EnableDefaultEmbeddedResourceItems)' == 'true' " />
</ItemGroup>
<ItemGroup Condition=" '$(EnableDefaultItems)' == 'true' And '$(EnableDefaultNoneItems)' == 'true' ">
  <None Include="**/*" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
  <None Remove="**/*$(DefaultLanguageSourceExtension)" />
  <None Remove="**/*.resx" />
</ItemGroup>

以下属性是 Microsoft.NET.Sdk 中的各种 Target 使用的配置属性,设置这些属性也影响到生成过程。

<Project>
  <PropertyGroup>
    <!-- 此程序集的版本,这是很多其他版本号未设置时的默认值。而此值的默认值是 1.0.0 -->
    <Version>3.1.2-beta</Version>

    <!-- 以下属性是当引用的 dll 出现版本冲突时,用于自动生成绑定重定向的。
         详见:https://www.erikheemskerk.nl/transitive-nuget-dependencies-net-core-got-your-back/ -->

    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
  </PropertyGroup>
</Project>

可以阅读 解读 Microsoft.NET.Sdk 的源码,你能定制各种奇怪而富有创意的编译过程Reading the Source Code of Microsoft.NET.Sdk, Writing the Creative Extension of Compiling 了解更多 Microsoft.NET.Sdk 源码。

文件路径

项路径

写在 csproj 文件中 ItemGroup 组中的每一个元素即“项”。

对以下这一项进行说明的话:

<ItemGroup> <Compile Include="src\Program.cs" /> </ItemGroup>

那么,可用的属性有:

  • %(FullPath)
    • 文件的完全路径,例如: C:\Users\walterlv\GitHub\Demo\Walterlv.DemoProject\src\Program.cs
  • %(RootDir)
    • 文件所在的根目录,例如: C:\
  • %(Filename)
    • 文件名(不含扩展名),例如: Program
  • %(Extension)
    • 文件扩展名,例如: .cs
  • %(RelativeDir)
    • 文件所在的文件夹,例如: src\
  • %(Directory)
    • 除了根目录之外的目录,例如: walterlv\GitHub\Demo\Walterlv.DemoProject\src\
  • %(RecursiveDir)
    • 如果项是用通配符写的,那么此值表示匹配到某一项时的目录,例如: walterlv\GitHub\Demo\Walterlv.DemoProject\src\
  • %(Identity)
    • 项的标识符,也就是 Include 里写的东西,例如: src\Program.cs
  • %(ModifiedTime)
    • 文件的修改时间,例如: 2018-04-12 21:00:43.7851385
  • %(CreatedTime)
    • 文件的创建时间,例如: 2018-04-12 21:01:50.1417635
  • %(AccessedTime)
    • 文件最近被访问的时间,例如: 2018-04-12 21:02:15.4132476

全局路径

  • 项目文件
    • $(MSBuildProjectFullPath)
      • 项目文件的绝对路径,例如: C:\Users\walterlv\GitHub\Demo\Walterlv.DemoProject.csproj
    • $(MSBuildProjectDirectory)
      • 项目所在的文件夹,例如: C:\Users\walterlv\GitHub\Demo
    • $(MSBuildProjectFile)
      • 项目文件的完整名称,例如: Walterlv.DemoProject.csproj
    • $(MSBuildProjectName)
      • 项目文件的名称,不含扩展名,例如 Walterlv.DemoProject
    • $(MSBuildProjectExtension)
      • 项目文件的扩展名,例如: .csproj
    • $(MSBuildProjectDirectoryNoRoot)
      • 项目文件去除驱动器的路径,包含反斜杠
  • 部件(例如 .props 文件或 .targets 文件,当然也包含 .csproj 文件)
    • $(MSBuildThisFileFullPath)
      • 用这个属性的文件所在的绝对路径,例如 C:\Users\walterlv\.nuget\packages\walterlv.nuget.demo\2.13.0\build\netstandard2.0\Walterlv.NuGet.Demo.targets
    • $(MSBuildThisFileDirectory)
      • 此文件所在的文件夹,例如: C:\Users\walterlv\.nuget\packages\walterlv.nuget.demo\2.13.0\build\netstandard2.0\
    • $(MSBuildThisFile)
      • 此文件的完整名称,例如 Walterlv.NuGet.Demo.targets
    • $(MSBuildThisFileName)
      • 此文件的名称,不含扩展名,例如 Walterlv.NuGet.Demo
    • $(MSBuildThisFileExtension)
      • 此文件的扩展名,例如 .targets
    • $(MSBuildThisFileDirectoryNoRoot)
      • 此文件去除驱动器的路径,包含反斜杠
  • 环境
    • $(MSBuildStartupDirectory)
      • 启动 MSBuild 时的路径,类似于工作目录(输入 msbuild 命令时所在的那个文件夹)
  • 工具
    • $(MSBuildToolsPath)
      • MSBuild 工具所在的路径
    • $(MSBuildToolsVersion)
      • 此次编译锁使用的工具的版本

另外还有一些在新的 SDK 中几乎不会在日常开发中用到的全局属性:

  • $(MSBuildBinPath): MSBuild 程序所在的路径
  • $(MSBuildExtensionsPath): 自定义 targets 所在的路径
  • $(MSBuildExtensionsPath32): 自定义 targets 所在的路径
  • $(MSBuildExtensionsPath64): 自定义 targets 所在的路径
  • $(MSBuildLastTaskResult): 如果前一个 Task 结束后成功,则为 true;否则为 false
  • $(MSBuildNodeCount): 编译时并发的进程数,与命令行中的 /maxcpucount 时一个意思
  • $(MSBuildProgramFiles32): 通常是 C:\Program Files (x86)
  • $(MSBuildProjectDefaultTargets): 在 Project 根节点上设置的默认 Targets,例如: <Project DefaultTargets="A;B;C" >
  • $(MSBuildBinPath): MSBuild 程序所在的路径
  • $(MSBuildBinPath): MSBuild 程序所在的路径
  • $(MSBuildBinPath): MSBuild 程序所在的路径
  • $(MSBuildBinPath): MSBuild 程序所在的路径

如果希望了解在 csproj 中创建 NuGet 包时可用的属性,请参考我的另一篇博客:项目文件中的已知 NuGet 属性(知道了这些,创建 NuGet 包就可以不需要 nuspec 文件啦) - 吕毅


参考资料

本文会经常更新,请阅读原文: https://walterlv.com/post/known-properties-in-csproj.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏游戏杂谈

解决Visual Studio 2010过卡的问题

装了Visual Assists 插件后,开发中经常会导致整个电脑都处于“挂起”的状态,任务管理器都打不开。后来修改了一下设置,现在已经正常了…

27530
来自专栏伪君子的梦呓

优雅地使用 PyCharm

0.0 前言 昨天教了大家安装和使用PyCharm 后,有朋友留言说 PyCharm 使用的语言是英文,看得头晕,该怎么设置成中文。那我今天就写一个教程,教大...

1.3K50
来自专栏依乐祝

使用Visual Studio Code开发.NET Core看这篇就够了

在本文中,我将带着大家一步一步的通过图文的形式来演示如何在Visual Studio Code中进行.NET Core程序的开发,测试以及调试。尽管Visual...

22100
来自专栏技术博客

Asp.Net Web API 2第十七课——Creating an OData Endpoint in ASP.NET Web API 2(OData终结点)

  很久没更新博客了,加上刚过年,现在准备重新开战,继续自己的学习之路。本文已同步到Web API2系列文章中http://www.cnblogs.com/ae...

10120
来自专栏葡萄城控件技术团队

.Net 高效开发之不可错过的实用工具 工欲善其事,必先利其器,没有好的工具,怎么能高效的开发出高质量的代码呢?本文为各ASP.NET 开发者介绍一些高效实用的工具,涉及SQL 管理,VS插件,内

工欲善其事,必先利其器,没有好的工具,怎么能高效的开发出高质量的代码呢?本文为各ASP.NET 开发者介绍一些高效实用的工具,涉及SQL 管理,VS插件,内存管...

24860
来自专栏欧阳大哥的轮子

Windows窗口消息和消息队列

所有基于事件驱动的操作系统中的GUI程序,都会在主线程中运行一个消息泵来从消息队列中取出消息并执行对应的处理逻辑。消息队列中的消息除了由系统产生外,还提供了对应...

32950
来自专栏程序员的SOD蜜

Erlang语言学习入门

近期研究RabbitMQ,发现它是基于Erlang实现的,于是对Erlang这么语言发生了兴趣,官网地址 http://www.erlang.org/ ,去下载...

311100
来自专栏ml

ijg库的使用的几点注意

ijg库(http://www.ijg.org/)是用于处理jpeg解码和压缩的库,最新版本为2014发布的版本,可以在官网中下载jpegsr9a.zip 使用...

35850
来自专栏吴伟祥

常用的 Java核心包 原

JVM的常用包一般在C:\Program Files\Java\jre1.5.0_04\lib\rt.jar 一般都会放在C:\Program Files\J...

22630
来自专栏iOS开发攻城狮的集散地

runloop的解读

19760

扫码关注云+社区

领取腾讯云代金券