MSBuild для копирования динамически генерируемых файлов как части зависимости проекта

17

У меня есть пользовательская задача msbuild, которая генерирует некоторые выходные файлы в выходной каталог ($ (TargetDir)) ProjectA. Текущий код выглядит примерно так:

<MyCustomTask ...>
   <Output TaskParameter="OutputFiles" ItemName="FileWrites"/>
</MyCustomTask>

ПроектB ссылается на ProjectA , но проблема в том, что при создании ProjectB сгенерированные файлы MyCustomTask не копируются в выходной каталог ProjectB.

Как мы можем получить динамически генерируемые дополнительные файлы, которые будут скопированы как часть зависимости проекта от MSBuild?

    
задан xoofx 14.01.2013 в 17:24
источник
  • Вот аналогичный вопрос, который привел к немного другому подходу stackoverflow.com/q/44752139/165500, что может быть удобно для случаев, когда пользовательская задача не задействована. –  Andrew Russell 27.06.2017 в 04:33

4 ответа

11

Наконец-то мне удалось автоматически выполнить копию из Project B без необходимости ее изменения. IIya не был так далек от решения, но факт в том, что я не могу генерировать статически, так как список файлов для создания из Project A с MyCustomTask является динамическим. После копания большего количества в Microsoft.Common.targets , я обнаружил, что ProjectB получит список результатов из Project A , вызвав целевой GetCopyToOutputDirectoryItems . Эта цель зависит от AssignTargetPaths , которая сама зависит от свойства списка целей AssignTargetPathsDependsOn .

Итак, чтобы генерировать динамическое содержимое и автоматически копировать этот контент с помощью стандартной зависимости проекта, нам нужно подключить Project A в двух разных местах:

  • В AssignTargetPathsDependsOn , поскольку он косвенно называется Project B в Project A через GetCopyToOutputDirectoryItems. А также он косвенно называется Project A , когда вызывается PrepareResource . Здесь мы просто выводим список файлов, которые будут сгенерированы (через Project A ) или будут использованы Project B . AssignTargetPathsDependsOn вызовет настраиваемую задачу MyCustomTaskList , которая отвечает только за вывод списка файлов (но не для их создания), этот список файлов создаст динамическое «Content» с CopyOutputDirectory .
  • В BuildDependsOn для фактического создания содержимого в Project A . Это вызовет MyCustomTask , который будет генерировать контент.

Все это было задано в ProjectA:

<!-- In Project A -->

<!-- Task to generate the files -->
<UsingTask TaskName="MyCustomTask" AssemblyFile="$(PathToMyCustomTaskAssembly)"/>

<!-- Task to output the list of generated of files - It doesn't generate the file -->
<UsingTask TaskName="MyCustomTaskList" AssemblyFile="$(PathToMyCustomTaskAssembly)"/>

<!-- 1st PART : When Project A is built, It will generate effectively the files -->
<PropertyGroup>
  <BuildDependsOn>
    MyCustomTaskTarget;
    $(BuildDependsOn);
  </BuildDependsOn>
</PropertyGroup>

<Target Name="MyCustomTaskTarget">
  <!-- Call MyCustomTask generate the files files that will be generated by MyCustomTask -->
  <MyCustomTask
      ProjectDirectory="$(ProjectDir)"
      IntermediateDirectory="$(IntermediateOutputPath)"
      Files="@(MyCustomFiles)"
      RootNamespace="$(RootNamespace)"
      >
  </MyCustomTask>
</Target>

<!-- 2nd PART : When Project B is built, It will call GetCopyToOutputDirectoryItems on ProjectA so we need to generate this list when it is called  -->
<!-- For this we need to override AssignTargetPathsDependsOn in order to generate the list of files -->
<!-- as GetCopyToOutputDirectoryItems  ultimately depends on AssignTargetPathsDependsOn -->
<!-- Content need to be generated before AssignTargets, because AssignTargets will prepare all files to be copied later by GetCopyToOutputDirectoryItems -->
<!-- This part is also called from ProjectA when target 'PrepareResources' is called -->
<PropertyGroup>
  <AssignTargetPathsDependsOn>
    $(AssignTargetPathsDependsOn);
    MyCustomTaskListTarget;
  </AssignTargetPathsDependsOn>
</PropertyGroup>

<Target Name="MyCustomTaskListTarget">

  <!-- Call MyCustomTaskList generating the list of files that will be generated by MyCustomTask -->
  <MyCustomTaskList
      ProjectDirectory="$(ProjectDir)"
      IntermediateDirectory="$(IntermediateOutputPath)"
      Files="@(MyCustomFiles)"
      RootNamespace="$(RootNamespace)"
      >
      <Output TaskParameter="ContentFiles" ItemName="MyCustomContent"/>
  </MyCustomTaskList>

  <ItemGroup>
    <!--Generate the lsit of content generated by MyCustomTask -->
    <Content Include="@(MyCustomContent)" KeepMetadata="Link;CopyToOutputDirectory"/>
  </ItemGroup>
</Target>

Этот метод работает с anykind проектов C #, использующих Common.Targets (поэтому он работает с чистым рабочим столом, WinRT XAML App или Windows Phone 8).     

ответ дан xoofx 25.01.2013 в 14:45
1

Если вы уже делаете это самостоятельно с помощью MSBuild, можете ли вы добавить Копировать задачу в нажимать файлы вокруг себя?

    
ответ дан Wolfwyrd 14.01.2013 в 17:30
  • Я предполагаю, что вопрос заключается в том, как настроить задачу, чтобы она фактически копировала файлы в цель ProjectB. Или, скорее всего, файлы в папке ProjectA каким-то образом позволяют ProjectB копировать файлы, похожие на ссылки и другие файлы, с «копировать на вывод Directory = всегда». –  Alexei Levenkov 14.01.2013 в 17:44
  • @Wolfwyrd, как сказал Алексей, ваше решение подразумевает, что мне придется изменить ProjectB для выполнения копии, но я бы хотел этого избежать. «FileWrites», по-видимому, используется только для отслеживания файлов при использовании цели «Чистота», но не используется для регистрации выходов. Кажется, что файл Microsoft.Common.targets выполняет некоторые конкретные операции для копирования зависимостей, и неясно, что он может расширяться, чтобы справиться с этим. –  xoofx 15.01.2013 в 02:05
  • @xoofx - поэтому ProjectB содержит ваш вызов MyCustomTask в какой-то момент? Почему вы не можете просто поставить вызов задачи <Копировать> в ту же цель после MyCustomTask? Или почему не просто, в вашем коде в MyCustomTask, сделать копию через ваш любимый .NET copy API? –  Jason Malinowski 25.01.2013 в 08:14
  • Нет проекта B содержит регулярную зависимость ProjectReference к проекту A. Но Project B не знает, что делает проект A (MyCustomTask или нет). Решение требует немного больше подключений в msbuild, см. Мой ответ. –  xoofx 25.01.2013 в 14:50
1

Кажется, что-то вроде этого работает, либо включайте его вручную в .csproj ProjectA (имейте в виду, что у VS есть плохая привычка иногда разрешать подстановочные знаки в абсолютные пути и перезаписывать .csproj) или динамически вводить сама пользовательская задача. Кроме того, VS кэширует группы элементов в открытом доступе, поэтому может не копировать файлы или выходить из строя, если они были там, но удалены. В этом случае проекты необходимо перезагрузить или перезапустить VS для групп товаров, которые будут переоценены. MSBuild, TFS и т. Д. Всегда должны работать.

<ItemGroup>
  <Content Include="$(TargetDir)\*.txt">
    <Link>%(Filename)%(Extension)</Link>
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </Content>
</ItemGroup>
    
ответ дан Ilya Kozhevnikov 21.01.2013 в 01:45
  • Пока что, но я не могу создать этот список статически, как вы это делали. См. Мой ответ. –  xoofx 25.01.2013 в 14:48
1

Как я понимаю, вы хотите добавить дополнительный шаг, написав только эту строку в ProjectB.msbuild:

<Import Project="ProjectA.msbuild" />

Чтобы достичь этого, вы можете написать что-то вроде ProjectA:

<PropertyGroup>
  <BuildDependsOn>$(BuildDependsOn);MyCustomTask</BuildDependsOn>
</PropertyGroup>

Это добавляет вашу задачу в список зависимостей задачи сборки.

Обратитесь к этому вопросу за дополнительной информацией: StyleCop MS Build magic? Кто вызывает цель StyleCop?

    
ответ дан gerichhome 25.01.2013 в 08:10
  • Не совсем. Я хочу динамически генерировать некоторый контент в Project A, который будет скопирован Project B, без необходимости изменять Project B. Project B имеет только зависимость ProjectReference, но никоим образом не должен знать, что Project A динамически генерирует некоторый контент , –  xoofx 25.01.2013 в 14:47