Home > MSBuild > Publish Click-Once Application with MSBuild

Publish Click-Once Application with MSBuild

Задача: Удалить гланды

Ограничение: Через рот нельзя.

Как-то раз мне пришлось публиковать click-once приложение с помощью MSBuid’a. Проект, который нужно публиковать, лежит в TFS’e, в котором настроен автоматический билд. В решении, которое строится билдом – до 50 проектов, одно из которых click-once приложение. Моей задачей было доработать этот билд таким образом, чтобы кроме собственно создания бинарных файлов и опубликованых сайтов, сброшенных в одну кучу, строилась четкая иерархия, которую уже можно было бы “показать” InstallShield’y. В том числе корректно должна выполняться публикация click-once для установки из сети. Вносить изменения в проекты – запрещено 🙂

Итак, все по порядку. Для начала опишу немного структуру самого MSBuid-проекта. Вся работа у меня выполняется в цели AfterDropBuild. Таким образом, к моменту, когда в ход вступает моя артилерия, все файлы, которые мне нужно разложить по папкам уже готовы, бинарники построены, сайты опубликованы. На этом этапе публикация click-once приложения не осуществляется, потому что для этого на проекте нужно выполнить цель Publish, вместо выполняемой по умолчанию цели Build. Соответственно, нужно вызвать публикацию приложения.

<Target Name="PublishApp">
    <MSBuild Projects="$(SolutionRoot)\$(AMDesigner)"
          Targets="Publish"
          Properties="Configuration=Release;
                      PublishDir=$(DropLocation)\$(BuildNumber)\$(Configuration)\$(AMDesignerDestination);
                      IsWebBootstrapper=true">
    </MSBuild>
  </Target>

 Здесь я использовал ранее определенные переменные:

AMDesigner – путь к моему проекту, относительно корня решения

AMDesignerDestination – путь, куда должно быть опубликовано приложение, относительно папки Drop’a

Пробный запуск показал, что строится всё, кроме файла publish.html. Как это исправить отлично описано в статье Марка Воллиса (Mark Wallis) ClickOnce via msbuild, которая ссылается на другую его статью про публикацию click-once  в целом: ClickOnce Publish to Web with MSBuild.

Суть предложения Марка в том, что в проект вводится файл шаблона страницы. При публикации шаблон заполняется данными и копируется к опубликованому приложению. Реализация основывается на использовании расширенного набора задач для MSBuild’a, которые можно взять тут: msbuild community tasks.

Возможно, кто-то на этом может и остановиться, но в моем случае решение мне немного не подошло, так как у меня не было возможности менять файл проекта публицируемого приложения, чтобы не нарушить работу разработчиков. Разработчики должны иметь возможность публиковать приложение вручную из Visual Studio, но не у каждого разработчика на компьютере установлены MSBuild Community Tasks, на которые нужно ссылаться. Поэтому нужно публиковать приложение из проекта билда. Тут возникает другая проблема – все данные, которые нужно подставлять в шаблон, определены именно в проекте приложения (в csproj файле) и в проекте билда они не видны.

Поэтому я немного модифицировал решение, таким образом, чтобы вся работа выполнялась исключительно в проекте билда. Для этого я ввел дополнительную цель, которая как предусловие выполняла цель PublishApp (см. предыдущий сниппет):

<Target Name="CreatePublishHtml" DependsOnTargets="PublishApp">
</Target>

 Далее опишу наполнение этой цели.

Для начала получаем в отдельные переменные все данные, которые нас интересуют. Мне не нужны были все поля, представленные в шаблоне, поэтому я его немного подредактировал в ворде. Итак, в заголовок проекта добавим ссылку на установленные MSBuild Community Tasks:

<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>

 Далее, так как csproj-файл есть ни что иное, как xml-файл, с помощью XPath запросов сохраняем каждый интересующий нас параметр в отдельную переменную:

<XmlRead Prefix="n"
                Namespace="http://schemas.microsoft.com/developer/msbuild/2003"
                XPath="/n:Project/n:PropertyGroup/n:ProductName"
                XmlFileName="$(SolutionRoot)\$(AMDesigner)">
      <Output TaskParameter="Value" PropertyName="ProductName" />
    </XmlRead>

    <XmlRead Prefix="n"
                Namespace="http://schemas.microsoft.com/developer/msbuild/2003"
                XPath="/n:Project/n:PropertyGroup/n:PublisherName"
                XmlFileName="$(SolutionRoot)\$(AMDesigner)">
      <Output TaskParameter="Value" PropertyName="PublisherName" />
    </XmlRead>

    <XmlRead Prefix="n"
                Namespace="http://schemas.microsoft.com/developer/msbuild/2003"
                XPath="/n:Project/n:PropertyGroup/n:ApplicationRevision"
                XmlFileName="$(SolutionRoot)\$(AMDesigner)">
      <Output TaskParameter="Value" PropertyName="ApplicationRevision" />
    </XmlRead>

    <XmlRead Prefix="n"
                Namespace="http://schemas.microsoft.com/developer/msbuild/2003"
                XPath="/n:Project/n:PropertyGroup/n:ApplicationRevision"
                XmlFileName="$(SolutionRoot)\$(AMDesigner)">
      <Output TaskParameter="Value" PropertyName="ApplicationRevision" />
    </XmlRead>

 Полный номер версии получился без номера ревизии, поэтому с помощью RegEx подстановки построим полный номер версии приложения:

<RegexReplace Input="$(ApplicationVersion)" Expression=".2a" Replacement="$(ApplicationRevision)" Count="1">
      <Output PropertyName ="ApplicationVersion1" TaskParameter="Output" />
    </RegexReplace>

 Создадим группу переменных для замены в шаблоне:

<ItemGroup>
      <Tokens Include="PublisherName">
        <ReplacementValue>$(PublisherName)</ReplacementValue>
        <Visible>false</Visible>
      </Tokens>
      <Tokens Include="ProductName">
        <ReplacementValue>$(ProductName)</ReplacementValue>
        <Visible>false</Visible>
      </Tokens>
      <Tokens Include="ApplicationVersion">
        <ReplacementValue>$(ApplicationVersion1)</ReplacementValue>
        <Visible>false</Visible>
      </Tokens>
    </ItemGroup>

 Остался последний шаг – наполнить шаблон данными:

<TemplateFile Template="$(SolutionRoot)\$(TemplateFile)" Tokens="@(Tokens)" OutputFilename="$(DropLocation)\$(BuildNumber)\$(Configuration)\$(AMDesignerDestination)\publish.html" />

 Готово! Теперь приложение может публиковаться автоматически и не зависит от настроек публикации разработчиков.

Удачных билдов 😉

Advertisements
Categories: MSBuild Tags: , , , , ,
  1. Владимир
    24.01.2011 at 13:19

    Здравствуйте!

    Извините за дилетантский вопрос, просто никогда раньше ничего подобного не делал, поэтому не много понимаю в данной тематике, но проблему решить надо)
    вопрос собственно вот какой: где располагается весь тот код, который вы описали? в каком(их) файлах?

    p.s. я вот еще возможно не все понимаю.. в начале вы писали про то, что ваша задача была доработать билд в TFS’e… а в дальнейшем речь шла о публикации click-once приложения с помощью MSBuild. Т.е. TFS работает через MSBuild? а сама Visual Studio нет?

  2. darkaxe
    24.01.2011 at 17:08

    Здравствуйте!

    Начну с конца) И студия и TFS используют MSBuild для компиляции проектов. Просто масштабы использования разные. В TFS обычно сборки строятся из множества проектов, каждый из который имеет свою специфику, поэтому я столкнулся с конфигурацией билда именно при работе с TFS. Для обычных проектов мне хватает и пре-, пост-билд событий.
    Код располагается в .proj файле билд проекта. Вроде бы это называется build definition

  3. Владимир
    26.01.2011 at 07:07

    значит, я все правильно понял)
    еще вот только такой вопрос остался: если все это располагается в .proj, значит публикация будет происходить и в том случае, когда разработчики будут билдить проект из студии на своих машинах? или только если будет запущен серверный билд в tfs?
    мне бы не хотелось, чтобы когда разработчики запускали билд своих локальных версий, то проект публиковался.. публикация должны быть только если это будет серверный билд tfs.

  4. darkaxe
    26.01.2011 at 23:53

    Есть ведь разные .proj) У разработчиков вообще есть солюшн, состоящий из (допустим они пишут на шарпе) .csproj проектов. Структура и назначения файла .csproj такая же, как и у .proj – это конфигурационный файл для мсбилда. Билд в тфс – это не что иное как поочередный билд каждого из проектов + предварительные действия + постбилдные действия, которые определяются уже в определении билда самого TFS.
    Надеюсь, что не запутал еще больше. Еще больше надеюсь, что сам понимаю правильно. 🙂
    Если будут еще вопросы, со мной можно связаться например по скайпу: celenia_oky1

  5. Владимир
    27.01.2011 at 08:46

    я может быть не очень удачно сформулировал свой вопрос)
    попробую по другому) на сервере находится одна версия проекта (как .proj, так и всех файлов с кодом, типа .cs)… билд в tfs настроен именно на серверный проект.
    Когда разработчики меняют что-то в проекте, они меняют что-то в своих локальных копиях проекта и только после того как сделают чек-ин измененных файлов, эти изменения попадут на сервер.
    если делать настройку так как у вас написано, то когда разработчики получат новую версию .proj файла, то каждый раз когда они будут билдить свою локальную версию проекта, то она будет публиковаться автоматически, что мне не надо (как минимум по тому, что будет довольно большая вероятность публикации не рабочей версии проекта, т.к. его еще никто протестить не успел)
    поэтому мне и надо, чтобы проект публиковался только после билда определенного в tfs.
    из того, что вы выше написали, я предполагаю, что надо доработать постбилдные действия, определяемые в tfs.. только вот где, как и что менять, до сих пор не понятно(( если я вообще в правильном направлении мыслю)
    з.ы. доступ к скайпу у меня пока сильно ограничен((

  6. darkaxe
    31.01.2011 at 15:27

    Мне кажется, что Вы все же что-то путаете. У разработчиков не может быть .proj, у них есть только .csproj и билдят они именно его. А тот, о котором я пишу находится в папке TeamBuildTypes солюшна в TFS. К сожалению, у меня нет сейчас доступак тфс (на текущей работе используется SVN), поэтому выложить скриншоты не могу. Но описанное решение как раз для таких случаев и подходит, потому что не затрагивает локальные установки разработчиков, которые собственно тоже могут публиковать приложение у себя локально.

  7. Владимир
    01.02.2011 at 10:27

    ааа… все, теперь понял) спасибо за помощь! надеюсь, что все получится)

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: