2010-02-24 3 views
6

많은 프로젝트가 포함 된 솔루션의 일부로 솔루션의 다른 세 가지 프로젝트와 <ProjectReference>을 통해 참조하는 프로젝트가 있습니다. AfterBuild에서 3 개의 특정 종속 프로젝트의 출력을 다른 위치로 복사해야합니다. 등이중 재 구축을 트리거하지 않고 MSBuild에서 ProjectReference의 출력 확인

다양한 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의 모든 종속 프로그램을 다시 빌드합니다. 이것은 TeamBuild 또는 다른 명령 행 MSBuild 호출의 컨텍스트에서 발생하지 않는 VS 특정 속임수 일 수 있습니다 (그러나 가장 일반적인 사용은 VS를 통해 이루어 지므로 어떤 방법 으로든 해결해야합니다)

종속 프로젝트 (및 솔루션의 나머지 부분)는 VS와 대화식으로 생성되었으므로 ProjectRefence에는 상대 경로가 포함되어 있습니다. 문제를 일으킬 가능성이 있다는 언급을 보았습니다. 또는 그것이 고쳐질 것이거나 그것의 주위에 일하는 방법. 다른 말로하면, 나는 정말로 예를 들어 관심이 없다. .csproj를 직접 편집하여 ProjectReference 경로를 절대 경로로 변환합니다.

그것이 내가 멍청한 짓을하고있어 전적으로 가능하고 사람이 바로, 나는 내가 시도 havent 한 있지만 (등 /v:diag 이상의 출력을 탐독 많은 시간을 보냈다 확신 할 (좋을 것이다)이 무엇인지 지적되지만 최대 지상에서 생식을 구축 -이 상대적으로 복잡한 전체 빌드의 문맥)에

답변

6

내 의견에서 언급했듯이 참조 된 프로젝트에서 GetTargetPath를 호출하면 해당 프로젝트의 기본 출력 어셈블리 만 반환됩니다. 참조 된 프로젝트의 참조 된 모든 복사본 - 로컬 어셈블리를 얻으려면 조금 복잡합니다.

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

내 특정 상황에 내가 빈에서 System.AddIn의 파이프 라인 폴더 구조를 다시해야한다는 것입니다 :

당신이의 CopyLocals을 얻고 싶은 것을 참조하는 각 프로젝트에 다음을 추가 최상위 레벨 호스트 프로젝트의 폴더. 이것은 좀 지저분하고 MSDN이 OutputPath과 일 처리의 솔루션을 제안에 나는 행복하지 않았다 - 그 추가와 함께 그래서 다른 프로젝트의 폴더 구조 (예 : SystemTest)

을 만드는 우리의 빌드 서버에 나누기 및 방지로 필요한 PipelineFolder 메타 데이터를 추가 할

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

나는 또한 필요 : 위 대상 (A .targets 가져 오기를 사용하여), 내가 만든 파이프 라인 폴더 필요한 각 "호스트"에 의해 가져온 .targets 파일에 다음을 추가 실제 프로젝트 참조. 예를 들면 다음과 같습니다.

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

current workaround is based on this SO question, 즉, 내가 가진 :

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

이 그러나 TeamBuild (아래 휴식 곳의 모든 출력은 하나의 디렉토리에서 끝난다.) 또한 종속 proj의 어떤 출력의 이름 변화가 일어납니다.

편집 :

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

당신은 ProjectC에서 파일을 보호 할 수 있습니다

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

과 : 또한보다 약간 청소기 하드 코딩을 만드는 방법에 대한 청소기 대답이 있는지 여부에 대한 의견을 찾고

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

감사합니다. 첫 번째 답변으로이 퍼즐을 선택해 주셔서 감사합니다. 나는 개인적으로 MSBuild의 내부 구조에 대한 의존성을 소개하는 것에 대해 과묵 할 것입니다. 그러나 그렇습니다. 이것은 "프로그램이 작성한 파일"을 프로그램 적으로 얻을 수있게 해줄 것입니다. 여기에 몇 가지 다른 "산출물 산출"질문이 있으므로 답이 더 잘 맞을 것입니다. 나에게 가장 큰 이슈는'

5

당신은 원래 : 당신이 첫 번째와 같은 대상을 호출하는 경우 L 솔루션은 GetTargetPath 목표는 단순히 TargetPath 속성을 반환하고 건물을 필요로하지 않습니다

Targets="GetTargetPath" 

Targets="Build" 

을 변경하여 간단하게 작동합니다.

+0

+1 감사합니다. 확실히 검증 된 위치가 아니기 때문에 (다리 아래에 많은 물이 있었지만!) 매우 실용적이고 깨끗한 접근 방법이었습니다. –

+0

YMMV : GetTargetPath는 프로젝트의 기본 출력 어셈블리 (.DLL/.EXE) 만 반환합니다. 해당 프로젝트의 _all_ 참조 된 어셈블리를 원한다면 다른 기법이 필요합니다. –

+0

다른 사람이 C++ 프로젝트에서이 작업을 수행해야하는 경우 올바른 대상 Targets = "GetNativeTargetPath"가 호출됩니다. – Neutrino