冗長なリビルドをトリガーせずに MSBuild で ProjectReference の出力を決定する

StackOverflow https://stackoverflow.com/questions/2325598

質問

多くのプロジェクトを含むソリューションの一部として、( <ProjectReference> ソリューション内の他の 3 つのプロジェクトとその他のプロジェクト)。の中に 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" />

しかし、これでは問題が発生しました。の <MSBuild ステップの IncrementalClean タスクは最終的に多くの出力を削除することになります。 ProjectC. 。これを VS2008 で実行すると、 build.force ファイルが保存されています obj/Debug ProjectC のフォルダー。プロジェクトにこれが含まれている場合、ソリューション全体でビルドを実行すると、ProjectC の再ビルドがトリガーされます。 AfterBuild 一方、このプロジェクトをビルドから除外すると、[正しく] ProjectC のリビルドがトリガーされません (そして 批判的に ProjectC のすべての依存関係の再構築)。この場合、これは VS 固有のトリックである可能性があり、TeamBuild またはその他のコマンドライン MSBuild 呼び出しのコンテキストでは発生しません (ただし、最も一般的な使用法は VS 経由であるため、いずれかの方法でこれを解決する必要があります)

依存プロジェクト (および一般的なソリューションの残りの部分) はすべて VS と対話的に作成されているため、 ProjectRefences には相対パスなどが含まれます。これが問題を引き起こす可能性があるという言及を見たことがありますが、その理由、いつ修正されるのか、どのように回避するのかについては十分な説明がありません。言い換えれば、私はあまり興味がありません。を変換する ProjectReference .csproj を手動で編集して、パスを絶対パスに変更します。

私が何か愚かなことをしている可能性は十分にありますが、誰かがそれが何であるかをすぐに指摘してくれるでしょう(それは素晴らしいことですが)、私はじっくりと時間をかけて調べてきましたのでご安心ください。 /v:diag 出力など(ただし、私は再現物を一から構築しようとしたわけではありません。これは比較的複雑な全体的な構築のコンテキスト内でのことです)

役に立ちましたか?

解決

としては唯一のアセンブリ、そのプロジェクトのプライマリ出力を返す参照プロジェクトにGetTargetPathを呼び出して、私のコメントで指摘しました。それはビットメシエだ参照プロジェクトのすべての参照、コピー地方議会を取得する。

あなたはのCopyLocalsを取得したいことを参照しているプロジェクトごとに以下を追加します:

    <Target
    Name="ComputeCopyLocalAssemblies"
    DependsOnTargets="ResolveProjectReferences;ResolveAssemblyReferences"
    Returns="@(ReferenceCopyLocalPaths)" /> 
私の特定の状況は、私は私のトップレベルのホストプロジェクトのbinフォルダにSystem.AddInのためのパイプラインフォルダ構造を再作成するために必要なことです。これはちょっと厄介だと私はMSDNがOutputPathをいじくるのソリューションを提案して幸せではなかった - 私たちのビルドサーバーと別のプロジェクトにフォルダ構造を作成防止上の区切りとして(例えばA 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>

私はまた、実際のプロジェクトの参照に必要な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>

他のヒント

あなたのオリジナルのソリューションは、変更するだけで動作するはずです。

Targets="Build"

Targets="GetTargetPath"

GetTargetPath目標は、単にTargetPathプロパティを返し、構築する必要はありません。

:あなたは、この最初のようにターゲットを呼び出す場合は、

あなたはProjectCでファイルを保護することができます

  <Target Name="ProtectFiles">
    <ReadLinesFromFile File="obj\ProjectC.csproj.FileListAbsolute.txt">
        <Output TaskParameter="Lines" ItemName="_FileList"/>
    </ReadLinesFromFile>
    <CreateItem Include="@(_DllFileList)" Exclude="File1.sample; File2.sample">
        <Output TaskParameter="Include" ItemName="_FileListWitoutProtectedFiles"/>
    </CreateItem>      
      <WriteLinesToFile 
        File="obj\ProjectC.csproj.FileListAbsolute.txt"
        Lines="@(_FileListWitoutProtectedFiles)"
        Overwrite="true"/>
  </Target>

私の 現在の回避策はこのSOの質問に基づいています, つまり、私は次のものを持っています:

    <ItemGroup>
        <DependentAssemblies Include="
            ..\ProjectA\bin\$(Configuration)\ProjectA.dll;
            ..\ProjectB\bin\$(Configuration)\ProjectB.dll;
            ..\ProjectC\bin\$(Configuration)\ProjectC.dll">
        </DependentAssemblies>
    </ItemGroup>

ただし、これは TeamBuild (すべての出力が 1 つのディレクトリにまとめられる) では中断され、依存プロジェクトのいずれかの出力の名前が変更された場合にも中断されます。

編集:また、ハードコーディングをもう少しクリーンにする方法について、次のようなよりクリーンな答えがあるかどうかについてのコメントも探しています。

    <PropertyGroup>
        <_TeamBuildingToSingleOutDir Condition="'$(TeamBuildOutDir)'!='' AND '$(CustomizableOutDir)'!='true'">true</_TeamBuildingToSingleOutDir>
    </PropertyGroup>

そして:

    <ItemGroup>
        <DependentAssemblies 
            Condition="'$(_TeamBuildingToSingleOutDir)'!='true'"
            Include="
                ..\ProjectA\bin\$(Configuration)\ProjectA.dll;
                ..\ProjectB\bin\$(Configuration)\ProjectB.dll;
                ..\ProjectC\bin\$(Configuration)\ProjectC.dll">
        </DependentAssemblies>
        <DependentAssemblies 
            Condition="'$(_TeamBuildingToSingleOutDir)'=='true'"
            Include="
                $(OutDir)\ProjectA.dll;
                $(OutDir)\ProjectB.dll;
                $(OutDir)\ProjectC.dll">
        </DependentAssemblies>
    </ItemGroup>
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top