代码之家  ›  专栏  ›  技术社区  ›  mark

如何编写实用程序msbuild项目,使其依赖于“真正的”C#项目?

  •  0
  • mark  · 技术社区  · 3 年前

    我所说的实用程序是指一个没有任何C#文件的项目,它不会生成一个。NET程序集,但实现了一些自定义构建逻辑。

    我本可以将其安排为感兴趣的C#项目中的AfterBuild目标,但我不想增加该C#项目的构建时间。相反,我希望msbuild与该C#项目的其他依赖项并行运行此逻辑。

    一种解决方案是创建一个虚拟的C#项目,该项目将真正构建一些虚拟代码,并将我的逻辑放在AfterBuild目标中。但这很丑陋。

    所以,这是我的解决方案(剧透警报-它不起作用):

    目录结构

    C:\work\u [master]> tree /F
    Folder PATH listing for volume OSDisk
    Volume serial number is F6C4-7BEF
    C:.
    │   .gitignore
    │   Deployer.sln
    │
    ├───Deployer
    │       Deployer.csproj
    │
    ├───DeploymentEngine
    │       DeploymentEngine.csproj
    │
    └───Utility
            Utility.csproj
    
    C:\work\u [master]>
    

    Deployer.csproj

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
      <PropertyGroup>
        <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
        <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
        <ProjectGuid>{B451936B-54B7-41D1-A359-4B06865248CE}</ProjectGuid>
        <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
        <OutputType>Library</OutputType>
        <BaseOutputPath>bin</BaseOutputPath>
        <PlatformTarget>AnyCPU</PlatformTarget>
        <ErrorReport>prompt</ErrorReport>
      </PropertyGroup>
      <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
        <DefineConstants>DEBUG;TRACE</DefineConstants>
      </PropertyGroup>
      <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
        <DefineConstants>TRACE</DefineConstants>
        <Optimize>true</Optimize>
      </PropertyGroup>
      <ItemGroup>
        <ProjectReference Include="..\DeploymentEngine\DeploymentEngine.csproj">
          <Project>{901487BE-C604-4251-8485-3E96D5993145}</Project>
          <Name>DeploymentEngine</Name>
        </ProjectReference>
      </ItemGroup>
      <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
      <Target Name="TakeTime" AfterTargets="Build">
        <Exec Command="powershell -NoProfile -Command Start-Sleep -Seconds 5" />
      </Target>
    </Project>
    

    是的,这是一个遗留风格的项目,因为真正的解决方案是遗留和SDK风格项目的混合。

    部署引擎.csproj

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <TargetFramework>net472</TargetFramework>
      </PropertyGroup>
      <Target Name="TakeTime" AfterTargets="Build">
        <Exec Command="powershell -NoProfile -Command Start-Sleep -Seconds 5" />
      </Target>
    </Project>
    

    实用程序.csproj

    <Project>
      <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
      <PropertyGroup>
        <TargetFramework>net472</TargetFramework>
        <EnableDefaultItems>False</EnableDefaultItems>
        <GenerateAssemblyInfo>False</GenerateAssemblyInfo>
      </PropertyGroup>
      <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
      <Target Name="Build">
        <Message Text="*** Good" Importance="high" Condition="Exists('..\DeploymentEngine\bin\Debug\net472\DeploymentEngine.dll')" />
        <Message Text="*** Bad" Importance="high" Condition="!Exists('..\DeploymentEngine\bin\Debug\net472\DeploymentEngine.dll')" />
      </Target>
      <Target Name="Clean" />
      <Target Name="Rebuild" DependsOnTargets="Clean;Build" />
      <ItemGroup>
        <ProjectReference Include="..\DeploymentEngine\DeploymentEngine.csproj" />
      </ItemGroup>
    </Project>
    

    部署器.sln

    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.31205.134
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Deployer", "Deployer\Deployer.csproj", "{B451936B-54B7-41D1-A359-4B06865248CE}"
    EndProject
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeploymentEngine", "DeploymentEngine\DeploymentEngine.csproj", "{901487BE-C604-4251-8485-3E96D5993145}"
    EndProject
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utility", "Utility\Utility.csproj", "{9369D18D-D81D-4CA3-A287-C62C89BFB751}"
    EndProject
    Global
            GlobalSection(SolutionConfigurationPlatforms) = preSolution
                    Debug|Any CPU = Debug|Any CPU
                    Release|Any CPU = Release|Any CPU
            EndGlobalSection
            GlobalSection(ProjectConfigurationPlatforms) = postSolution
                    {B451936B-54B7-41D1-A359-4B06865248CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                    {B451936B-54B7-41D1-A359-4B06865248CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
                    {B451936B-54B7-41D1-A359-4B06865248CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
                    {B451936B-54B7-41D1-A359-4B06865248CE}.Release|Any CPU.Build.0 = Release|Any CPU
                    {901487BE-C604-4251-8485-3E96D5993145}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                    {901487BE-C604-4251-8485-3E96D5993145}.Debug|Any CPU.Build.0 = Debug|Any CPU
                    {901487BE-C604-4251-8485-3E96D5993145}.Release|Any CPU.ActiveCfg = Release|Any CPU
                    {901487BE-C604-4251-8485-3E96D5993145}.Release|Any CPU.Build.0 = Release|Any CPU
                    {9369D18D-D81D-4CA3-A287-C62C89BFB751}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                    {9369D18D-D81D-4CA3-A287-C62C89BFB751}.Debug|Any CPU.Build.0 = Debug|Any CPU
                    {9369D18D-D81D-4CA3-A287-C62C89BFB751}.Release|Any CPU.ActiveCfg = Release|Any CPU
                    {9369D18D-D81D-4CA3-A287-C62C89BFB751}.Release|Any CPU.Build.0 = Release|Any CPU
            EndGlobalSection
            GlobalSection(SolutionProperties) = preSolution
                    HideSolutionNode = FALSE
            EndGlobalSection
            GlobalSection(ExtensibilityGlobals) = postSolution
                    SolutionGuid = {A70FF6AB-85B1-49F0-B2B0-25E20256A88F}
            EndGlobalSection
    EndGlobal
    

    笔记:

    • 我人为地推迟了两个“真正的”C#项目。
    • 公用事业项目产出 *** Bad 当它在声明的依赖关系之后运行NOT时,即在Deployment Engine项目之后运行NOT。

    现在让我们运行它:

    C:\work\u [master]> git clean -qdfx ; msbuild /v:m /restore /m
    Microsoft (R) Build Engine version 16.11.0+0538acc04 for .NET Framework
    Copyright (C) Microsoft Corporation. All rights reserved.
    
      Determining projects to restore...
      Restored C:\work\u\DeploymentEngine\DeploymentEngine.csproj (in 171 ms).
      Restored C:\work\u\Utility\Utility.csproj (in 172 ms).
      *** Bad
      DeploymentEngine -> C:\work\u\DeploymentEngine\bin\Debug\net472\DeploymentEngine.dll
    CSC : warning CS2008: No source files specified. [C:\work\u\Deployer\Deployer.csproj]
      Deployer -> C:\work\u\Deployer\bin\Debug\Deployer.dll
    C:\work\u [master]>
    

    输出表明,尽管声明了依赖Deployment Engine项目的意图,但实用工具项目是首先构建的。

    注意,如果我运行构建单线程,输出将是 *** Good ,因此输出逻辑确实正常工作:

    C:\work\u [master]> git clean -qdfx ; msbuild /v:m /restore
    Microsoft (R) Build Engine version 16.11.0+0538acc04 for .NET Framework
    Copyright (C) Microsoft Corporation. All rights reserved.
    
      Determining projects to restore...
      Restored C:\work\u\Utility\Utility.csproj (in 172 ms).
      Restored C:\work\u\DeploymentEngine\DeploymentEngine.csproj (in 172 ms).
      DeploymentEngine -> C:\work\u\DeploymentEngine\bin\Debug\net472\DeploymentEngine.dll
    CSC : warning CS2008: No source files specified. [C:\work\u\Deployer\Deployer.csproj]
      Deployer -> C:\work\u\Deployer\bin\Debug\Deployer.dll
      *** Good
    C:\work\u [master]>
    

    所以,只是宣布 ProjectReference 这还不够。看来我应该实现某种目标来让它发挥作用。

    那么,我错过了什么?我应该添加什么来让msbuild知道必须在部署引擎之后构建实用程序项目?

    编辑1

    我知道我可以在解决方案文件中设置依赖关系。然而,由于种种原因,我不想这样做。

    编辑2

    我的最终目标是在一个或多个“真实”的C#项目之后运行一个简单的实用程序项目。即很少。NET构建导入尽可能。如果它能有 .proj 扩展,而不是 .csproj -最好的。

    0 回复  |  直到 3 年前
        1
  •  1
  •   stijn    3 年前

    我想知道你为什么不使用 <Project Sdk="Microsoft.Net.Sdk"/> 为公用事业项目和阅读 the docs 这是因为它会在其他所有内容的末尾隐式导入Sdk.targets,从而覆盖您的 Build 目标。

    我还没有弄清楚具体是如何做到的(现在没有更多的时间,但我很确定,应该有可能有一个更简单的项目,并且仍然有ProjectReference正常运行——这将是一个声明正确属性和目标的问题;尽管这可能最终比在现有结构中黑客攻击更费力),但这个目标是使msbuild尊重ProjectReference并保持正确构建顺序的关键:除其他事项外,它取决于ResolveProjectReferences,后者是负责实际处理ProjectReference的目标。Msbuild本身对此一无所知,其逻辑由微软提供。普通。CurrentVersion.targets。

    因此,简单地覆盖构建目标将使ProjectReference完全被忽略。解决方案在不使用时按预期顺序构建的唯一原因 -m 公用事业项目排在最后。如果你在.sln中向上移动它,msbuild会提前构建它,并打印“***Bad”。

    第一次尝试: 构建 做a 许多 所以我想,只利用它来满足你的需要,而让它完好无损地留给其他人应该可以做到这一点。不是超级干净,但可以做到:

    <Project>
      <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
      <PropertyGroup>
        <TargetFramework>net472</TargetFramework>
        <GenerateAssemblyInfo>False</GenerateAssemblyInfo>
      </PropertyGroup>
      <ItemGroup>
        <ProjectReference Include="..\DeploymentEngine\DeploymentEngine.csproj">
        </ProjectReference>
      </ItemGroup>
      <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
      <!-- Override Compile instead of Build, thereby also skipping
            creating of Utility.dll -->
      <Target Name="Compile">
        <Message Text="*** Good" Importance="high" Condition="Exists('..\DeploymentEngine\bin\Debug\net472\DeploymentEngine.dll')" />
        <Message Text="*** Bad" Importance="high" Condition="!Exists('..\DeploymentEngine\bin\Debug\net472\DeploymentEngine.dll')" />
      </Target>
      <!-- Empty so it doesn't try to copy the nonexisting Utility.dll. -->
      <Target Name="CopyFilesToOutputDirectory" />
    </Project>
    

    第二次尝试:第一次尝试使用Sdk.targets等,这基本上是在说“我是一个完整的.Net项目”,因此是一种黑客解决方法。更简单的方法是只使用CurrentVersion中的内容。目标,即ResolveProjectReferences目标,使ProjectReference工作,因此接近“真正的”实用程序项目(命名为utility.proj):

    <Project>
      <PropertyGroup>
        <!-- ProjectReference requires the referenced project to have the same version -->
        <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
        <!-- Required for Microsoft.Common.CurrentVersion.targets -->
        <OutputPath>bin</OutputPath>
      </PropertyGroup>
      <ItemGroup>
        <ProjectReference Include="..\DeploymentEngine\DeploymentEngine.csproj">
        </ProjectReference>
      </ItemGroup>
      <!-- For ResolveProjectReferences and everything it does -->
      <Import Project="$(MSBuildBinPath)\Microsoft.Common.CurrentVersion.targets"/>
      <Target Name="Build" DependsOnTargets="ResolveProjectReferences">
        <Message Text="*** Good" Importance="high" Condition="Exists('..\DeploymentEngine\bin\Debug\net472\DeploymentEngine.dll')" />
        <Warning Text="*** Bad" Condition="!Exists('..\DeploymentEngine\bin\Debug\net472\DeploymentEngine.dll')" />
      </Target>
    </Project>