在 MSBuild 中确定 ProjectReference 的输出而不触发冗余重建 [英] Determining outputs of a ProjectReference in MSBuild without triggering redundant rebuilds

查看:7
本文介绍了在 MSBuild 中确定 ProjectReference 的输出而不触发冗余重建的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

作为包含许多项目的解决方案的一部分,我有一个项目引用(通过 <ProjectReference> 解决方案中的其他三个项目,以及其他一些项目).在 AfterBuild 中,我需要将 3 个特定依赖项目的输出复制到另一个位置.

As part of a solution containing many projects, I have a project that references (via a <ProjectReference> three other projects in the solution, plus some others). In the AfterBuild, I need to copy the outputs of 3 specific dependent projects to another location.

通过各种 SO 答案等,我决定完成的方式是:

Via various SO answers, etc. the way I settled on to accomplish that was:

    <MSBuild 
        Projects="@(ProjectReference)" 
        Targets="Build" 
        BuildInParallel="true" 
        Condition="'%(Name)'=='ProjectA' OR '%(Name)'=='ProjectB' OR '%(Name)'=='ProjectC'">
        <Output TaskParameter="TargetOutputs" ItemName="DependentAssemblies" />
    </MSBuild>
    <Copy SourceFiles="@(DependentAssemblies)" DestinationFolder="XX" SkipUnchangedFiles="true" />

但是,我遇到了这个问题.<MSBuild 步骤的 IncrementalClean 任务最终会删除 ProjectC 的许多输出.在 VS2008 下运行时,build.force 文件被存放在 ProjectC 的 obj/Debug 文件夹中,如果我在整个解决方案上进行构建,则会触发 ProjectC 重新构建如果项目包含这个 AfterBuild 目标,而如果从构建中排除这个项目,它 [正确地] 不会触发 ProjectC 的重建(并且关键地ProjectC 的所有依赖项).在这种情况下,这可能是特定于 VS 的诡计,不会在 TeamBuild 或其他命令行 MSBuild 调用的上下文中发生(但最常见的用法是通过 VS,因此我需要以任何一种方式解决此问题)

However, I ran into problems with this. The <MSBuild step's IncrementalClean task ends up deleting a number of the outputs of ProjectC. When running this under VS2008, a build.force file being deposited in the obj/Debug folder of ProjectC which then triggers ProjectC getting rebuilt if I do a Build on the entire solution if the project containing this AfterBuild target, whereas if one excludes this project from the build, it [correctly] doesn't trigger a rebuild of ProjectC (and critically a rebuild of all dependents of ProjectC). This may be VS-specific trickery in this case which would not occur in the context of a TeamBuild or other commandline MSBuild invocation (but the most common usage will be via VS so I need to resolve this either way)

相关项目(以及一般的解决方案的其余部分)都已与 VS 交互创建,因此 ProjectRefence 包含相对路径等.我已经看到这很可能导致问题 - 但没有完整解释原因,或何时修复或如何解决它.换句话说,我对例如并不真正感兴趣.通过手动编辑 .csproj 将 ProjectReference 路径转换为绝对路径.

The dependent projects (and the rest of the solution in general) have all been created interactively with VS, and hence the ProjectRefences contain relative paths etc. I've seen mention of this being likely to causing issues - but without a full explanation of why, or when it'll be fixed or how to work around it. In other words, I'm not really interested in e.g. converting the ProjectReference paths to absolute paths by hand-editing the .csproj.

虽然我完全有可能在做一些愚蠢的事情并且有人会立即指出它是什么(这很好),但请放心,我已经花了很多时间研究 /v:diag 输出等(尽管我没有尝试从头开始构建复制 - 这是在相对复杂的整体构建的背景下)

While it's entirely possible I'm doing something stupid and someone will immediately point out what it is (which would be great), be assured I've spent lots of time poring over /v:diag outputs etc. (although I havent tried to build a repro from the ground up - this is in the context of a relatively complex overall build)

推荐答案

正如我在评论中指出的,在引用的项目上调用 GetTargetPath 只会返回该项目的主输出程序集.要获取引用项目的所有引用的复制本地程序集,这有点混乱.

As noted in my comment, calling GetTargetPath on the referenced project only returns the Primary output assembly of that project. To get all the referenced copy-local assemblies of the referenced project it's a bit messier.

将以下内容添加到您要获取其 CopyLocals 的每个引用的项目中:

Add the following to each project that you are referencing that you want to get the CopyLocals of:

    <Target
    Name="ComputeCopyLocalAssemblies"
    DependsOnTargets="ResolveProjectReferences;ResolveAssemblyReferences"
    Returns="@(ReferenceCopyLocalPaths)" /> 

我的特殊情况是我需要在我的顶级主机项目的 bin 文件夹中为 System.AddIn 重新创建 Pipeline 文件夹结构.这有点混乱,我对 MSDN 建议的使用 OutputPath 的解决方案不满意 - 因为这会在我们的构建服务器上中断并阻止在不同的项目(例如 SystemTest)中创建文件夹结构

My particular situation is that I needed to recreate the Pipeline folder structure for System.AddIn in the bin folder of my top level Host project. This is kinda messy and I was not happy with the MSDN suggested solutions of mucking with the OutputPath - as that breaks on our build server and prevents creating the folder structure in a different project (eg a SystemTest)

因此,除了添加上述目标(使用 .targets 导入)之外,我还将以下内容添加到需要创建管道文件夹的每个主机"导入的 .targets 文件中:

So along with adding the above target (using a .targets import), I added the following to a .targets file imported by each "host" that needs the pipeline folder created:

<Target
    Name="ComputePipelineAssemblies"
    BeforeTargets="_CopyFilesMarkedCopyLocal"
    Outputs="%(ProjectReference.Identity)">

    <ItemGroup>
        <_PrimaryAssembly Remove="@(_PrimaryAssembly)" />
        <_DependentAssemblies Remove="@(_DependentAssemblies)" />
    </ItemGroup>

    <!--The Primary Output of the Pipeline project-->
    <MSBuild Projects="%(ProjectReference.Identity)"
             Targets="GetTargetPath"
             Properties="Configuration=$(Configuration)"
             Condition=" '%(ProjectReference.PipelineFolder)' != '' ">
        <Output TaskParameter="TargetOutputs"
                ItemName="_PrimaryAssembly" />
    </MSBuild>

    <!--Output of any Referenced Projects-->
    <MSBuild Projects="%(ProjectReference.Identity)"
             Targets="ComputeCopyLocalAssemblies"
             Properties="Configuration=$(Configuration)"
             Condition=" '%(ProjectReference.PipelineFolder)' != '' ">
        <Output TaskParameter="TargetOutputs"
                ItemName="_DependentAssemblies" />
    </MSBuild>

    <ItemGroup>
        <ReferenceCopyLocalPaths Include="@(_PrimaryAssembly)"
                                 Condition=" '%(ProjectReference.PipelineFolder)' != '' ">
            <DestinationSubDirectory>%(ProjectReference.PipelineFolder)</DestinationSubDirectory>
        </ReferenceCopyLocalPaths>
        <ReferenceCopyLocalPaths Include="@(_DependentAssemblies)"
                                 Condition=" '%(ProjectReference.PipelineFolder)' != '' ">
            <DestinationSubDirectory>%(ProjectReference.PipelineFolder)</DestinationSubDirectory>
        </ReferenceCopyLocalPaths>
    </ItemGroup>
</Target>

我还需要将所需的 PipelineFolder 元数据添加到实际的项目引用中.例如:

I also needed to add the required PipelineFolder meta data to the actual project references. For example:

    <ProjectReference Include="..Dogs.Pipeline.AddInSideAdapterDogs.Pipeline.AddInSideAdapter.csproj">
        <Project>{FFCD0BFC-5A7B-4E13-9E1B-8D01E86975EA}</Project>
        <Name>Dogs.Pipeline.AddInSideAdapter</Name>
        <Private>False</Private>
        <PipelineFolder>PipelineAddInSideAdapter</PipelineFolder>
    </ProjectReference>

这篇关于在 MSBuild 中确定 ProjectReference 的输出而不触发冗余重建的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆