Я только что написал об этом в блоге (http://sedodream.com/2011/12/29/UpdatingXMLFilesWithMSBuild.aspx), но я также вставлю сюда информацию для вас.
Сегодня я только что увидел вопрос, размещенный на StackOverflow, с вопросом, как обновить файл XML с помощью MSBuild во время сборки CI, выполняемой из Team City.
Не существует правильного единственного ответа, существует несколько различных способов обновления XML-файла во время сборки. В частности:
- Используйте SlowCheetah, чтобы преобразовать файлы для вас
- Используйте задачу TransformXml напрямую
- Используйте встроенную (MSBuild 4.0) задачу XmlPoke.
- Используйте стороннюю библиотеку задач
1 Используйте SlowCheetah, чтобы преобразовать файлы для вас
Прежде чем вы начнете слишком углубляться в этот пост, позвольте мне сначала рассмотреть вариант № 3, потому что я думаю, что это самый простой подход и его легче всего поддерживать. Вы можете загрузить мою надстройку SlowCheetah XML Transforms для Visual Studio. Как только вы сделаете это для своих проектов, вы увидите новую команду меню для преобразования файла при сборке (для веб-проектов при пакетировании/публикации). Если вы строите из командной строки или с сервера CI, преобразования также должны выполняться.
2 Используйте задачу TransformXml напрямую
Если вам нужен метод, в котором у вас есть «основной» XML-файл, и вы хотите иметь возможность содержать преобразования этого файла в отдельном XML-файле, вы можете напрямую использовать задачу TransformXml. Для получения дополнительной информации см. мой предыдущий пост в блоге по адресу http://sedodream.com/2010/11/18/XDTWebconfigTransformsInNonwebProjects.aspx
3 Используйте встроенную задачу XmlPoke
Иногда нет смысла создавать файл XML с преобразованиями для каждого файла XML. Например, если у вас есть файл XML и вы хотите изменить одно значение, но создать 10 разных файлов, подход преобразования XML плохо масштабируется. В этом случае может быть проще использовать задачу XmlPoke. Обратите внимание, что для этого требуется MSBuild 4.0.
Ниже приведено содержимое sample.xml (получено из вопроса SO).
<Provisioning.Lib.Processing.XmlConfig instancetype="XmlConfig, Processing, Version=1.0.0.0, Culture=neutral">
<item>
<key>IsTestEnvironment</key>
<value>True</value>
<encrypted>False</encrypted>
</item>
<item>
<key>HlrFtpPutDir</key>
<value>C:\DevPath1</value>
<encrypted>False</encrypted>
</item>
<item
<key>HlrFtpPutCopyDir</key>
<value>C:\DevPath2</value>
<encrypted>False</encrypted>
</item>
</Provisioning.Lib.Processing.XmlConfig>
Итак, в этом случае мы хотим обновить значения элемента value. Итак, первое, что нам нужно сделать, это придумать правильный XPath для всех элементов, которые мы хотим обновить. В этом случае мы можем использовать следующие выражения XPath для каждого элемента значения.
- /Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutDir']/значение
- /Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutCopyDir']/value Я не буду подробно рассказывать, что вам нужно сделать, чтобы определить правильный XPath, потому что это не является целью этого поста. В Интернете есть множество ресурсов, связанных с XPath. В разделе ресурсов я дал ссылку на онлайн-тестер XPath, которым всегда пользуюсь.
Теперь, когда у нас есть необходимые выражения XPath, нам нужно создать наши элементы MSBuild, чтобы все было обновлено. Вот общая техника:
- Поместите всю информацию обо всех обновлениях XML в элемент
- Используйте XmlPoke вместе с пакетной обработкой MSBuild для выполнения всех обновлений.
Для # 2, если вы не очень хорошо знакомы с пакетной обработкой MSBuild, я бы порекомендовал купить мою книгу или вы можете взглянуть на имеющиеся у меня в Интернете ресурсы, касающиеся пакетной обработки (ссылка находится ниже в разделе ресурсов). Ниже вы найдете созданный мной простой файл MSBuild, UpdateXm01.proj.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="UpdateXml" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SourceXmlFile>$(MSBuildProjectDirectory)\sample.xml</SourceXmlFile>
<DestXmlFiles>$(MSBuildProjectDirectory)\result.xml</DestXmlFiles>
</PropertyGroup>
<ItemGroup>
<!-- Create an item which we can use to bundle all the transformations which are needed -->
<XmlConfigUpdates Include="ConfigUpdates-SampleXml">
<XPath>/Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutDir']/value</XPath>
<NewValue>H:\ReleasePath1</NewValue>
</XmlConfigUpdates>
<XmlConfigUpdates Include="ConfigUpdates-SampleXml">
<XPath>/Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutCopyDir']/value</XPath>
<NewValue>H:\ReleasePath2</NewValue>
</XmlConfigUpdates>
</ItemGroup>
<Target Name="UpdateXml">
<Message Text="Updating XML file at $(DestXmlFiles)" />
<Copy SourceFiles="$(SourceXmlFile)"
DestinationFiles="$(DestXmlFiles)" />
<!-- Now let's execute all the XML transformations -->
<XmlPoke XmlInputPath="$(DestXmlFiles)"
Query="%(XmlConfigUpdates.XPath)"
Value="%(XmlConfigUpdates.NewValue)"/>
</Target>
</Project>
Особое внимание следует уделить элементу XmlConfigUpdates и содержимому самой задачи UpdateXml. Что касается XmlConfigUpdates, это имя является произвольным, вы можете использовать любое имя, которое хотите, вы можете видеть, что значение Include (которое обычно указывает на файл) просто оставлено в ConfigUpdates-SampleXml. Значение атрибута Include здесь не используется. Я бы поместил уникальное значение атрибута Include для каждого файла, который вы обновляете. Это просто облегчает людям понимание того, для чего предназначена эта группа значений, и вы можете использовать ее позже для пакетных обновлений. Элемент XmlConfigUpdates имеет следующие два значения метаданных:
- XPath — содержит XPath, необходимый для выбора элемента, который будет обновлен.
- NewValue — содержит новое значение для элемента, который будет обновлен. Внутри цели UpdateXml вы можете видеть, что мы используем задачу XmlPoke и передаем XPath как %(XmlConfigUpdate.XPath), а значение как %(XmlConfigUpdates .Новое Значение). Поскольку мы используем синтаксис %(…) для элемента, это запускает пакетную обработку MSBuild. Пакетная обработка — это когда над «партией» значений выполняется более одной операции. В этом случае есть два уникальных пакета (по 1 для каждого значения в XmlConfigUpdates), поэтому задача XmlPoke будет вызываться два раза. Пакетная обработка может сбивать с толку, поэтому обязательно ознакомьтесь с ней, если вы не знакомы.
Теперь мы можем использовать msbuild.exe для запуска процесса. Результирующий файл XML:
<Provisioning.Lib.Processing.XmlConfig instancetype="XmlConfig, Processing, Version=1.0.0.0, Culture=neutral">
<item>
<key>IsTestEnvironment</key>
<value>True</value>
<encrypted>False</encrypted>
</item>
<item>
<key>HlrFtpPutDir</key>
<value>H:\ReleasePath1</value>
<encrypted>False</encrypted>
</item>
<item>
<key>HlrFtpPutCopyDir</key>
<value>H:\ReleasePath2</value>
<encrypted>False</encrypted>
</item>
</Provisioning.Lib.Processing.XmlConfig>
Итак, теперь мы видим, как легко было использовать задачу XmlPoke. Давайте теперь посмотрим, как мы можем расширить этот пример, чтобы управлять обновлениями того же файла для дополнительной среды.
Как управлять обновлениями одного и того же файла для нескольких разных результатов
Поскольку мы создали элемент, который сохранит все необходимые XPath, а также новые значения, у нас появилось немного больше гибкости в управлении несколькими средами. В этом сценарии у нас есть тот же файл, который мы хотим записать, но нам нужно записать разные значения в зависимости от целевой среды. Сделать это довольно легко. Взгляните на содержимое UpdateXml02.proj ниже.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="UpdateXml" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SourceXmlFile>$(MSBuildProjectDirectory)\sample.xml</SourceXmlFile>
<DestXmlFiles>$(MSBuildProjectDirectory)\result.xml</DestXmlFiles>
</PropertyGroup>
<PropertyGroup>
<!-- We can set a default value for TargetEnvName -->
<TargetEnvName>Env01</TargetEnvName>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetEnvName)' == 'Env01' ">
<!-- Create an item which we can use to bundle all the transformations which are needed -->
<XmlConfigUpdates Include="ConfigUpdates">
<XPath>/Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutDir']/value</XPath>
<NewValue>H:\ReleasePath1</NewValue>
</XmlConfigUpdates>
<XmlConfigUpdates Include="ConfigUpdates">
<XPath>/Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutCopyDir']/value</XPath>
<NewValue>H:\ReleasePath2</NewValue>
</XmlConfigUpdates>
</ItemGroup>
<ItemGroup Condition=" '$(TargetEnvName)' == 'Env02' ">
<!-- Create an item which we can use to bundle all the transformations which are needed -->
<XmlConfigUpdates Include="ConfigUpdates">
<XPath>/Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutDir']/value</XPath>
<NewValue>G:\SomeOtherPlace\ReleasePath1</NewValue>
</XmlConfigUpdates>
<XmlConfigUpdates Include="ConfigUpdates">
<XPath>/Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutCopyDir']/value</XPath>
<NewValue>G:\SomeOtherPlace\ReleasePath2</NewValue>
</XmlConfigUpdates>
</ItemGroup>
<Target Name="UpdateXml">
<Message Text="Updating XML file at $(DestXmlFiles)" />
<Copy SourceFiles="$(SourceXmlFile)"
DestinationFiles="$(DestXmlFiles)" />
<!-- Now let's execute all the XML transformations -->
<XmlPoke XmlInputPath="$(DestXmlFiles)"
Query="%(XmlConfigUpdates.XPath)"
Value="%(XmlConfigUpdates.NewValue)"/>
</Target>
</Project>
Различия довольно просты, я представил новое свойство TargetEnvName, которое позволяет нам узнать, что такое целевая среда. (примечание: я только что придумал это имя свойства, используйте любое имя, которое вам нравится). Также вы можете видеть, что есть два элемента ItemGroup, содержащие разные элементы XmlConfigUpdate. Каждая ItemGroup имеет условие, основанное на значении TargetEnvName, поэтому будет использоваться только одно из двух значений ItemGroup. Теперь у нас есть один файл MSBuild со значениями для обеих сред. При сборке просто передайте свойство TargetEnvName, например msbuild .\UpdateXml02.proj /p:TargetEnvName=Env02. Когда я выполнил это, результирующий файл содержит:
<Provisioning.Lib.Processing.XmlConfig instancetype="XmlConfig, Processing, Version=1.0.0.0, Culture=neutral">
<item>
<key>IsTestEnvironment</key>
<value>True</value>
<encrypted>False</encrypted>
</item>
<item>
<key>HlrFtpPutDir</key>
<value>G:\SomeOtherPlace\ReleasePath1</value>
<encrypted>False</encrypted>
</item>
<item>
<key>HlrFtpPutCopyDir</key>
<value>G:\SomeOtherPlace\ReleasePath2</value>
<encrypted>False</encrypted>
</item>
</Provisioning.Lib.Processing.XmlConfig>
Вы можете видеть, что файл был обновлен с разными путями в элементе value.
4 Используйте стороннюю библиотеку задач
Если вы не используете MSBuild 4, вам потребуется использовать стороннюю библиотеку задач, например пакет расширений MSBuild (ссылка в ресурсах).
Надеюсь, это поможет.
Ресурсы
person
Sayed Ibrahim Hashimi
schedule
29.12.2011