【问题标题】:MsBuild condition for new ("sdk-style") vs. old project format新(“sdk 风格”)与旧项目格式的 MsBuild 条件
【发布时间】:2022-01-25 01:59:04
【问题描述】:

我想编写一个 msbuild “include” (.props) 文件,该文件将被导入新的(“sdk 样式”)项目和旧项目中。

有没有办法找出 .props 文件导入到哪种类型的项目中,以便我可以在某种条件下使用它?例如,

<PropertyGroup Condition="'$(ProjectType)'=='sdk'">
   <SomeProperty>SomeValue</SomeProperty>
</PropertyGroup>

【问题讨论】:

    标签: visual-studio msbuild


    【解决方案1】:

    我在我的项目中遇到了同样的需求。起初我使用@wexman 的解决方案,但我发现该变量不可靠。我考虑过打开 csproj 文件并使用正则表达式查找 SDK 属性的方法,但我觉得它太脆弱了——尤其是考虑到 SDK 可以在元素或属性中指定。最后,我创建了一个打开二进制日志的构建(选项/bl),并在构建 SDK 样式项目时搜索导入的各种文件。

    我在使用 SDK 样式项目时自动导入 Microsoft.Common.props 的 props 文件中发现了属性 UsingMicrosoftNETSdk 设置为 true。这种自动导入(导致 Directory.Build.props 的自动导入)似乎是 SDK 风格项目最重要的定义特征之一,并且属性的名称使它看起来更有可能留下来.

    所以最后我的解决方案是:

    Condition="'$(UsingMicrosoftNETSdk)' == 'true'"
    

    (或其相反)作为确定正在使用的项目类型的可靠方法。

    【讨论】:

    • 啊,没看到那个属性。这确实是一个更好的解决方案,所以将其标记为答案!
    【解决方案2】:

    我似乎找到了一个可行的解决方案。我正在检查 Microsoft.NET.Sdk.Props 中声明的属性 $(_PlatformWithoutConfigurationInference)。如果不为空,我将处理一个 sdk 样式的项目,如果它为空,它是一个遗留项目:

    <Project>
    
      <PropertyGroup>
        <ProjectStyle Condition="'$(_PlatformWithoutConfigurationInference)'!=''">sdk</ProjectStyle>
        <ProjectStyle Condition="'$(_PlatformWithoutConfigurationInference)'==''">legacy</ProjectStyle>
      </PropertyGroup>
    
      <Target Name="PreBuild" BeforeTargets="PreBuildEvent">
        <Message Importance="High" Text="$(MsBuildProjectName) is a $(ProjectStyle) style project" />
      </Target>
    
    </Project>
    

    【讨论】:

    • 奇怪的是,当我尝试这个时,&lt;ProjectStyle Condition="'$(_PlatformWithoutConfigurationInference)'==''"&gt;legacy&lt;/ProjectStyle&gt; 似乎不起作用,因为当调用 Message 时,ProjectStyle 是空的。但是测试_PlatformWithoutConfigurationInference 可以作为启用部分道具文件的方式,具体取决于它是否是 sdk 项目,所以 +1 并感谢我。
    • 然后写了这个,我看到下面有一个更简单的答案。我仍然 +1,但我会切换到其他解决方案:)
    【解决方案3】:

    虽然测试属性值(或缺少属性值)是在 MSBuild 中完成几乎所有事情的方式,但有时还有其他方法。

    如何知道当前的MSBuild项目是否为SDK项目?

    换句话说:

    • 给定一个 XML 文件...
    • 其完整路径在MSBuildProjectFullPath well-known property...
    • 并且其第一个标签是Project...
    • 该标签是否有Sdk 属性?

    感谢 MSBuild 的property functions,您可以执行以下操作:

    • 使用[System.IO.File]::ReadAllText以字符串形式读取项目文件的内容;
    • 然后使用[System.Text.RegularExpressions.Regex]::IsMatch 测试带有Sdk 属性的Project 标记。
      <_projectcontents>$([System.IO.File]::ReadAllText(`$(MSBuildProjectFullPath)`))<_sdkregex>(?s-i)(^|\s|>)<Project\s(([^>]*?)\s)?Sdk(\s*?)=(\s*?)" <_issdkproject>$([System.Text.RegularExpressions.Regex]::IsMatch(`$(_ProjectContents)`, `$(_SdkRegex)`))

    或者,如果您更喜欢单线:

    <PropertyGroup>
      <_IsSdkProject>$([System.Text.RegularExpressions.Regex]::IsMatch(`$([System.IO.File]::ReadAllText(`$(MSBuildProjectFullPath)`))`, `(?s-i)(^|\s|&gt;)&lt;Project\s(([^&gt;]*?)\s)?Sdk(\s*?)=(\s*?)"`))</_IsSdkProject>
    </PropertyGroup>
    

    然后您可以在Condition 中使用_IsSdkProject 的值,即TrueFalse,如下所示:

    <PropertyGroup Condition="'$(_IsSdkProject)'=='True'">
      <SomeProperty>SomeValue</SomeProperty>
    </PropertyGroup>
    

    正则表达式解释

    在上述正则表达式中,&amp;lt;&amp;gt; 字符已替换为 XML 实体(分别为&amp;lt;&amp;gt;)。原来的正则表达式如下:

    (?s-i)(^|\s|>)<Project\s(([^>]*?)\s)?Sdk(\s*?)=(\s*?)"
    

    这意味着:

    • single-line, case-sensitive mode...
    • 无论是在文件的开头,还是跟在一个空白字符之后,或者跟在一个 &amp;gt; 字符之后...
    • 找到文字字符串&lt;Project...
    • 然后是一个空格字符...
    • 然后,可选地,除&amp;gt; 和另一个空白字符之外的任意数量的任意字符...
    • 然后是文字字符串Sdk...
    • 然后,可以选择任意数量的空白字符...
    • 然后是字符=...
    • 然后,可以选择任意数量的空白字符...
    • 然后是字符"

    on regex101.com 是一个更好的解释,以及一个在线测试器,您可以在其中插入项目文件的内容并实时测试。该网站是为 Javascript 正则表达式创建的,它的行为与 .NET 中的完全一样,但它仍然很有用。

    【讨论】:

    • 这不是一个好方法,因为您不应该关心导入是通过 Sdk 导入还是常规导入执行的。此外,还有其他引用sdk的方式:&lt;Import Sdk=&lt;Project&gt;&lt;Sdk
    • @RainerSigwald 是的,还有其他方法可以引用 SDK,但 OP 很可能正在寻找可以在他们自己的项目中使用的东西,因此很容易强制执行只使用 @RainerSigwald 的约定987654351@.
    • @RainerSigwald 至于 SDK 风格的项目检测的实用性:理论上,您是绝对正确的。但在实践中,它可以以有趣的方式加以利用,特别是如果您在每个项目中都导入了一些通用的 MSBuild 代码并且您仍然坚持使用(叹气!)WinForms,因此旧 -样式项目,用于应用程序,同时尽可能将 .NET Standard 用于库。
    【解决方案4】:

    新(“sdk 风格”)与旧项目格式的 MsBuild 条件

    这个问题没有直接的解决方案。因为在 MSBuild/Visual Studio 中没有 ProjectType 这样的属性。

    我想在这里提供一个解决方法,您可以检查它是否适合您。我们知道,在旧的项目格式中,通常会有一个文件AssemblyInfo.cs,而在新的“sdk-style”项目格式中是不存在的。因此我们可以使用Condition="Exists('$(ProjectDir)\Properties\AssemblyInfo.cs')" 来找出.props 文件被导入到哪种类型的项目中:

    我的测试内容.props文件:

     <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <PropertyGroup Condition="Exists('$(ProjectDir)\Properties\AssemblyInfo.cs')">
        <SomeProperty>SomeValue1</SomeProperty>
      </PropertyGroup>
    
      <PropertyGroup Condition="!Exists('$(ProjectDir)\Properties\AssemblyInfo.cs')">
        <SomeProperty>SomeValue2</SomeProperty>
      </PropertyGroup>
    
      <Target Name="Test" AfterTargets="AfterBuild">
        <Message Text="$(SomeProperty)"></Message>
      </Target>
    </Project>
    

    希望这会有所帮助。

    【讨论】:

    • 恐怕这对我不起作用。我确实有没有 assemblyinfo.cs 的遗留项目以及带有 assemblyinfo.cs 的 sdk 样式项目(我仍然将像 InternalsVisibleTo() 这样的程序集级属性放在那里)。
    猜你喜欢
    • 1970-01-01
    • 2019-12-17
    • 1970-01-01
    • 1970-01-01
    • 2022-12-18
    • 1970-01-01
    • 1970-01-01
    • 2021-03-19
    • 1970-01-01
    相关资源
    最近更新 更多