Картинка блога

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

Отладка

Мало кто знает, но XML MSBuild можно запустить в режиме отладки. С ее помощью можно просмотреть текущие значения параметров и групп. Включается отладка не тривиально, и требует изменений в реестре. Для включения недокументированного параметра — /debug, используйте следующую команду:


reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\4.0" /v debuggerenabled=true /d true

После этого, комманда MSBuild /? должна показать дополнительную опцию — debug. Если запустить скрипт с этим параметром, система предложит подключить Visual Studio к процессу. В ней можно выставлять точки прерывания (breakpoints) и анализировать скрипт.

Трансформации XML файлов с помощью Document Transform синтаксиса.

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

  1. TransformXml не может изменять файл который читает. Destination  — это всегда новый файл.
  2. MSBuild не отпускает файлы до завершения скрипта, по этому сделать несколько преобразований с одним файлом просто так не получится. Как вариант, каждый раз копировать файл во временный каталог.

Логирование результатов в файл.

Самый простой способ, воспользоватся цепочками консоли и перевести весь вывод в отдельный файл: msbuild script.xml > out.txt. Но в некоторых случаях (continious integration, запись промежуточных результатов с целью их чтения или преобразования и т.д.) это может не подойти. Не надо писать велосипед. Присмотритесь к стандартной библиотеке скриптов! Все делается легко и просто:


<WriteLinestoFile File="log.txt" Lines="[$(NowDate)] Some log information." />

Вызов CLR методов, совсем как в PowerShell.

В предыдущем примере используется параметр NowDate. Получить текущую дату можно с помощью CLR:

<NowDate>$([System.DateTime]::Now.ToString("dd.MM.yyyy hh:mm"))</NowDate>

Переопределение переменных

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

  • Файл пользовательской конфигурации. Тут переопределяются значения, отличающиеся от стандартных.
  • Файл стандартной конфигурации (значения по умолчанию). Тут нужно задавать значения только пустым параметрам.
  • Собственно сама логика скрипта (включает 2 первых по порядку).

Вот таким образом декларируются значения по умолчанию:

<Param1 Condition=" '$(Param1)' == '' ">default value for Param1</Param1>
Может возникнуть вопрос: Почему нельзя просто поставить пользовательскую конфигурацию после стандартной, и таким образом переопределить все нужные значения?

Дело в том, что выражения получения значения переменной выполняется сразу. Если у вас составная переменная, например в этом случае

<BookFile>${ContentRoot}/Books.xml</BookFile>

нужно будет переопределять обе переменные и ContentRoot и BookFile.

Циклы

Это больная тема MSBuild скриптов. Предположим у нас есть набор конфигураций с которыми нужно выполнить таск:

<ItemGroup>
	<Configs Include="Name1">
		<Environment>Env1</Environment>
		<Url>env1.test.com</CmsUrl>
	</Configs>
	<Configs Include="Name2">
		<Environment>Env2</Environment>
		<Url>env2.test.com</CmsUrl>
	</Configs>
</ItemGroup>

Выполнить таск к каждому элементу из Configs можно так:

<Target Name="ConfigsTarget">
<Message Text="Target called for $(Name) Env: $(Environment) Url: $(Url)" />
</Target>
<MSBuild Projects="$(MSBuildProjectFile)"
Properties="Name=%(Configs.Identity);Environment=%(Configs.Environment);Url=%(Configs.Url)"
Targets="ConfigsTarget" />

Пользовательские таски.

Написать таск для MSBuild проще чем может показаться. Это класс-наследник от Microsoft.Build.Utilities.Task.

Чтобы таск можно было использовать в скрипте, его надо за декларировать с помощью UsingTask:

<UsingTask AssemblyFile="BinaryAnalysis.Tasks" TaskName="BinaryAnalysis.Tasks.MyTask"/>

После этого его можно использовать по названию.

Готовые таски

Прежде чем писать свой таск для билда, убедитесь что вы не изобретаете велосипед (в 80% случаев это именно так). Вот несколько сборок, которые могут пригодится:

  • Таски от Tigris. Еще известен как Community Tasks.
  • MSBuild extension pack
  • И еще куча в интернете для отдельных нужд.
Может возникнуть ситуация, когда собирать надо проект, написанный на разных языках программирования. Например backend на С#, а frontend на Flash, или backend на Java, а frontend на Silverlight (интересно, такое извращение бывает?). Так как Java и Flash лучше дружат с Ant, можно воспользоватся мостом для запуска Ant скриптов из MSBuild. Такое решение отлично работает как минимум в связке: MSBuild + NAnt.

Метки:, , ,

4 комментария в “C# Типсы триксы MSBUILD и XBUILD”

  1. Вот еще забыл добавить интересный трюк: Вытаскиваем версию сборки и на основе нее делаем архив:

    <getassemblyidentity AssemblyFiles="$(SourceCmsDirectory)\bin\MyLibrary.dll">
    <output TaskParameter="Assemblies" ItemName="MyAssemblyIdentities"/>
    </getassemblyidentity>
    <propertygroup>
    <version>%(MyAssemblyIdentities.Version)</version>
    </propertygroup>

    После этого $Verions будет содержать версию сборки MyLibrary.dll

  2. Доброго времени суток.

    Ваш код в разделе с циклами неверен. Во первых — открывающемуся тэгу Url у Вас противопоставлен закрывающийся CmsUrl. Во вторых, MSBuild — это задача. Соответственно она, как минимум, должна находится в теле Target.

    С уважением, Андрей.

  3. Огромное спасибо!
    Именно то что я искал, столкнулся с тем что необходимо распарсить вводимые пользователем TeamCity параметры и по ним проехаться циклом.
    Так же очень полезно было узнать про возможность дебажить.

  4. в команде включения дебага — debuggerenabled=true не верно, нужно просто debuggerenabled.