确定MSBuild中ProjectReference的输出而不触发冗余重建

Rub*_*ink 7 msbuild project-reference msbuild-task

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

通过各种SO答案等,我决定实现这一目标的方式是:

    <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" />
Run Code Online (Sandbox Code Playgroud)

但是,我遇到了这个问题.该<MSBuild步骤的IncrementalClean任务最终删除了一些输出ProjectC.在VS2008下运行时,如果项目包含此目标,如果我对整个解决方案进行构建,那么build.force存放在obj/DebugProjectC文件夹中的文件将触发ProjectC重建AfterBuild,如果从构建中排除此项目,则[正确] ]不会触发ProjectC的重建(并且重要的是重建ProjectC的所有家属).在这种情况下,这可能是VS特定的技巧,这不会发生在TeamBuild或其他命令行MSBuild调用的上下文中(但最常见的用法是通过VS,所以我需要解决这个问题)

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

虽然我完全有可能做一些愚蠢的事情,有人会立即指出它是什么(这将是伟大的),请放心,我花了很多时间仔细考虑/v:diag输出等(虽然我没有尝试建立一个repro从从头开始 - 这是在相对复杂的整体构建的背景下)

Pet*_*voy 8

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

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

    <Target
    Name="ComputeCopyLocalAssemblies"
    DependsOnTargets="ResolveProjectReferences;ResolveAssemblyReferences"
    Returns="@(ReferenceCopyLocalPaths)" /> 
Run Code Online (Sandbox Code Playgroud)

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

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

<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>
Run Code Online (Sandbox Code Playgroud)

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

    <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>
Run Code Online (Sandbox Code Playgroud)


Dav*_*ave 6

您的原始解决方案应该只需更改即可

Targets="Build"
Run Code Online (Sandbox Code Playgroud)

Targets="GetTargetPath"
Run Code Online (Sandbox Code Playgroud)

GetTargetPath目标只是返回的TargetPath财产,不需要建设.