Determinação de saídas de uma projectreferência no msbuild sem desencadear reconstruções redundantes

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

Pergunta

Como parte de uma solução contendo muitos projetos, tenho um projeto que referências (via A <ProjectReference> Três outros projetos na solução, além de outros). No AfterBuild, Preciso copiar as saídas de 3 projetos dependentes específicos para outro local.

Por meio de várias respostas, etc. A maneira como eu decidi a realizar isso foi:

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

No entanto, tive problemas com isso. o <MSBuild degraus IncrementalClean tarefa acaba excluindo várias das saídas de ProjectC. Ao executar isso em vs2008, um build.force arquivo sendo depositado no obj/Debug Pasta do Projectc, que aciona o Projectc sendo reconstruído se eu fizer uma construção em toda a solução se o projeto que contém isso AfterBuild Target, enquanto que se alguém excluir este projeto da construção, ele [corretamente] não desencadeia uma reconstrução do ProjectC (e criticamente uma reconstrução de todos os dependentes do ProjectC). Isso pode ser truque específico do VS neste caso, o que não ocorreria no contexto de um Teambuild ou outro comando MSBuild Invocation (mas o uso mais comum será via VS, então preciso resolver isso de qualquer maneira)

Os projetos dependentes (e o restante da solução em geral) foram todos criados interativamente com vs e, portanto, o ProjectRefences contêm caminhos relativos etc. Eu vi mencionar que isso provável de causar problemas - mas sem uma explicação completa do porquê, ou quando será corrigida ou como contorná -lo. Em outras palavras, não estou realmente interessado em converter o ProjectReference Caminhos para caminhos absolutos, editando manualmente o .csproj.

Embora seja perfeitamente possível, estou fazendo algo estúpido e alguém imediatamente apontará o que é (o que seria ótimo), tenha certeza de que gastei muito tempo se debatendo /v:diag saídas etc. (embora eu não tenha tentado construir uma repro desde o início - isso está no contexto de uma construção geral relativamente complexa)

Foi útil?

Solução

Conforme observado no meu comentário, chamando o GetTargetPath no projeto referenciado retorna apenas a montagem de saída primária desse projeto. Para obter todos os conjuntos de cópias referenciados do projeto referenciado, é um pouco mais confuso.

Adicione o seguinte a cada projeto que você está referenciando que deseja obter os copilocais de:

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

Minha situação específica é que eu precisava recriar a estrutura da pasta do pipeline para o sistema.Addin na pasta BIN do meu projeto de host de nível superior. Isso é meio bagunçado e eu não fiquei feliz com o MSDN sugerido soluções de mexer com o outputPath - pois isso quebra em nosso servidor de construção e evita a criação da estrutura da pasta em um projeto diferente (por exemplo, um sistema de sistema)

Assim, além de adicionar o destino acima (usando um .Targets Import), adicionei o seguinte a um arquivo .Targets importado por cada "host" que precisa da pasta de pipeline criada:

<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>

Eu também precisava adicionar os meta dados necessários do PipeLEFder às referências reais do projeto. Por exemplo:

    <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>

Outras dicas

Sua solução original deve funcionar simplesmente mudando

Targets="Build"

para

Targets="GetTargetPath"

o GetTargetPath alvo simplesmente retorna o TargetPath propriedade e não requer construção.

Você pode proteger seus arquivos no ProjectC se ligar para um alvo como este primeiro:

  <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>

Meu A solução alternativa atual é baseada nisso, então pergunta, ou seja, eu tenho:

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

No entanto, isso será dividido no TeamBuild (onde todas as saídas acabam em um diretório) e também se os nomes de qualquer uma das saídas dos projetos dependentes mudarem.

EDIT: Também procurando comentários sobre se há uma resposta mais limpa de como tornar o codificação um pouco mais limpa do que:

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

e:

    <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>
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top