2010-02-24 5 views
6

В качестве части решения, содержащего много проектов, у меня есть проект, который ссылается (черезтри других проекта в решении, а также некоторые другие). В AfterBuild мне нужно скопировать выходы трех конкретных зависимых проектов в другое место.Определение выходов ProjectReference в MSBuild без запуска избыточных перестроек

Через различные 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 депонируется в папке Project12, созданной ProjectC, которая затем запускает восстановление ProjectC, если я создаю сборку на всем решении, если проект содержит эту цель AfterBuild, тогда как если исключить этот проект из сборки, он [правильно] не вызывает восстановление ProjectC (и критически восстановление всех иждивенцев ProjectC). В этом случае это может быть обман VS-спецификой, который не будет возникать в контексте вызова TeamBuild или другого командного вызова MSBuild (но наиболее частое использование будет выполняться через VS, поэтому мне необходимо разрешить это в любом случае)

Зависимая проекты (и остальная часть решения в целом) были созданы в интерактивном режиме с VS, и, следовательно, ProjectRefence s содержат относительные пути и т. д. Я видел упоминание об этом, которое может вызвать проблемы, но без полного объяснения причин, или когда он будет исправлен или как его обойти. Другими словами, меня действительно не интересуют, например, конвертируя пути ProjectReference в абсолютные пути, вручную редактируя .csproj.

Возможно, я делаю что-то глупое, и кто-то сразу укажет, что это такое (что было бы замечательно), будьте уверены, я потратил много времени на то, чтобы посмотреть /v:diag выходов и т. Д. (Хотя я и не пытался построить репро с нуля - это в контексте относительно сложной общей сборки)

ответ

6

Как отмечено в моем комментарии, вызов GetTargetPath в проекте, на который делается ссылка, возвращает только исходную сборку этого проекта. Чтобы получить все ссылочные копии локальных сборок ссылочного проекта, это немного грязнее.

Добавьте следующий к каждому проекту, который вы ссылаетесь, что вы хотите получить CopyLocals из:

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

Моя конкретная ситуация в том, что мне нужно, чтобы воссоздать структуру папок трубопроводов для System.AddIn в бункере папке моего хост-проекта верхнего уровня. Это довольно беспорядочно, и я не был доволен предлагаемыми 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> 

Я также необходимо добавить требуемое 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> 
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 изменения ects.

EDIT: Кроме того, глядя на любые комментарии на есть ли уборщик ответ о том, как сделать жестко прописывать немного чище, чем:

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

Вы можете защитить ваши файлы в 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> 
+0

Во-первых, спасибо/поздравляю вас в выборе этой головоломки как вашего первого SO! Я лично сдержанно представлял бы зависимость от внутренних компонентов MSBuild такого характера, но да, это, конечно же, позволило бы мне «программно писать» файлы ». Есть несколько других «вычислить результаты» здесь, на SO, чтобы это могло поместиться лучше в качестве ответа. Основная проблема здесь для меня заключается в том, что бит '

5

Ваше происхождение л раствора должна работать просто путем изменения

Targets="Build" 

в

Targets="GetTargetPath" 

GetTargetPath цель просто возвращает TargetPath свойства и не требует строительств.

+0

+1 Спасибо, не в состоянии проверить, что это определенно сработало (много воды под мостом с!), Но кажется очень жизнеспособным и чистым подходом. –

+0

YMMV: GetTargetPath возвращает только исходную выходную сборку проекта (.DLL/.EXE). Если вы хотите, чтобы _all_ ссылались на сборки этого проекта, тогда нужен другой метод. –

+0

Если кому-то еще нужно сделать это для проекта C++, для вызова - Targets = "GetNativeTargetPath". – Neutrino

 Смежные вопросы

  • Нет связанных вопросов^_^