MSBuild Property Scope

Еще раз я сражаюсь с MSBuild. Я хочу, чтобы значение свойства было определено с корневым путем. Как часть сборки, путь будет обновляться с информацией о версии. Тем не менее, MSBuild, похоже, имеет свои собственные правила видимости, которые выглядят совершенно задом наперед. Возьмите этот первый пример:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">

  <PropertyGroup>
    <MyPath>\\server\folder</MyPath>
  </PropertyGroup>

  <Target Name="Main">
    <Message Text="In Main Before - MyPath = $(MyPath)"/>
    <CallTarget Targets="Task1" />
    <CallTarget Targets="Task2" />
    <CallTarget Targets="Task3" />
    <Message Text="In Main After - MyPath = $(MyPath)"/>
  </Target>

  <Target Name="Task1">
    <PropertyGroup>
        <MyPath>$(MyPath)\version5</MyPath>
    </PropertyGroup>
    <Message Text="In Task1 - MyPath = $(MyPath)"/>
  </Target>

  <Target Name="Task2">
    <Message Text="In Task2 - MyPath = $(MyPath)"/>
  </Target>

  <Target Name="Task3">
    <Message Text="In Task3 - MyPath = $(MyPath)"/>
  </Target>

</Project>

Вот вывод этой командной строки: msbuild PropertyScopeTest1.proj /target:Main

Project "C:\Temp\PropertyScopeTest1.proj" on node 1 (Main target(s)).
Main:
  In Main Before - MyPath = \\server\folder
Task1:
  In Task1 - MyPath = \\server\folder\version5
Task2:
  In Task2 - MyPath = \\server\folder\version5
Task3:
  In Task3 - MyPath = \\server\folder\version5
Main:
  In Main After - MyPath = \\server\folder
Done Building Project "C:\Temp\PropertyScopeTest1.proj" (Main target(s)).

Теперь, немного отличающаяся версия, устанавливающая переменную MyPath в цели Main:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">

  <PropertyGroup>
    <MyPath>\\server\path</MyPath>
  </PropertyGroup>

  <Target Name="Main">
    <Message Text="In Main Before - MyPath = $(MyPath)"/>
    <PropertyGroup>
        <MyPath>$(MyPath)\version5</MyPath>
    </PropertyGroup>
    <Message Text="In Main After PropertyGroup - MyPath = $(MyPath)"/>
    <CallTarget Targets="Task1" />
    <CallTarget Targets="Task2" />
    <CallTarget Targets="Task3" />
    <Message Text="In Main After - MyPath = $(MyPath)"/>
  </Target>

  <Target Name="Task1">
    <Message Text="In Task1 - MyPath = $(MyPath)"/>
  </Target>

  <Target Name="Task2">
    <Message Text="In Task2 - MyPath = $(MyPath)"/>
  </Target>

  <Target Name="Task3">
    <Message Text="In Task3 - MyPath = $(MyPath)"/>
  </Target>

</Project>

Вот вывод этой командной строки: msbuild PropertyScopeTest2.proj /target:Main

Project "C:\Temp\PropertyScopeTest2.proj" on node 1 (Main target(s)).
Main:
  In Main Before - MyPath = \\server\path
  In Main After PropertyGroup - MyPath = \\server\path\version5
Task1:
  In Task1 - MyPath = \\server\path
Task2:
  In Task2 - MyPath = \\server\path
Task3:
  In Task3 - MyPath = \\server\path
Main:
  In Main After - MyPath = \\server\path\version5
Done Building Project "C:\Temp\PropertyScopeTest2.proj" (Main target(s)).

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

2 ответа

Решение

Основываясь на ответе sll, делая цель, которая устанавливает новый путь, зависимостью, вместо использования CallTarget даст ожидаемое поведение:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">

  <PropertyGroup>
    <MyPath>\\server\folder</MyPath>
  </PropertyGroup>

  <Target Name="Main" DependsOnTargets="SetMyPathProperty">
    <Message Text="In Main Before - MyPath = $(MyPath)"/>
    <CallTarget Targets="Task1" />
    <Message Text="In Main After - MyPath = $(MyPath)"/>
  </Target>

  <Target Name="SetMyPathProperty">
    <PropertyGroup>
      <MyPath>$(MyPath)\version5</MyPath>
    </PropertyGroup>
  </Target>

  <Target Name="Task1">
    <Message Text="In Task1 - MyPath = $(MyPath)"/>
  </Target>

</Project>

Построить вывод:

Main:
  In Main Before - MyPath = \\server\folder\version5
Task1:
  In Task1 - MyPath = \\server\folder\version5
Main:
  In Main After - MyPath = \\server\folder\version5

Создание SetMyPathProperty зависимостью Task1 вместо Main приведет к тому же поведению, что и ваш PropertyScopeTest1.proj.

Это очень интересный вопрос, который глубоко исследуется на примерах в следующей статье: Область действия свойств и элемента в скрипте MSBuild

По сути, существуют хитрости с локальным и глобальным переключением контекста в целевых исполнениях:

  • Один экземпляр класса Project создается для сценария и содержит все значения свойств и элементов в глобальном контексте.
  • Когда цель выполняется, глобальный контекст копируется в локальный контекст, который будет использоваться целью.
  • В конце целевого выполнения обновления локального контекста объединяются с глобальным контекстом.
  • Пока выполнение цели не завершено, локальные обновления не доступны для целей, вызываемых с помощью задач CallTarget или MSBuild.
Другие вопросы по тегам