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

查看:79
本文介绍了在不触发冗余重建的情况下确定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下运行此文件时,将在ProjectC的obj/Debug文件夹中存放一个build.force文件,如果在包含此AfterBuild目标的项目中对整个解决方案进行构建,则触发ProjectC进行重新构建,而如果排除了此项目(从构建版本开始)不会[正确]触发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输出等(尽管我没有试图从头开始构建一个repro-这是在相对复杂的整体构建的背景下进行的

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)" /> 

我的特殊情况是,我需要在顶层Host项目的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.AddInSideAdapter\Dogs.Pipeline.AddInSideAdapter.csproj">
        <Project>{FFCD0BFC-5A7B-4E13-9E1B-8D01E86975EA}</Project>
        <Name>Dogs.Pipeline.AddInSideAdapter</Name>
        <Private>False</Private>
        <PipelineFolder>Pipeline\AddInSideAdapter\</PipelineFolder>
    </ProjectReference>

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

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