TFS Team Foundation Build: Customize Build Output

Jan Holan       22. 8. 2011       TFS       6557 zobrazení

Součástí TFS (Team Foundation Server) jsou automatizované buildy Team Foundation Build (někdy se také nazývají Team Builds nebo Build Server) využívající technologie MSBuild a Windows Workflow Foundation, které jsou připravené na úpravy a rozšiřování build procesu. Právě takovou úpravou jsem nedávno strávil několik nemálo večerů, abych připravil automatizovaný buildu u složitější aplikace tak, aby jeho výstup byl v požadované podobě. V tomto článku je uveden postup, jak je možné upravit výstup automatizovaného buildu změnou build template souboru a MSBuild scriptů.

Hlavním problémem výchozího nastavení automatizovaného buildu je to, že jeho výstup není stejný jako při klasickém buildu (přímo z VS), a výstupní adresář Drop Folder pak po kompilaci více projektů / solutions obsahuje výstupy ze všech projektů dohromady.

Pozn.: Veškeré uvedené postupy a změny jsou pro verzi TFS 2010.

Úpravy Build Template

MSBuildu se při spuštění v procesu automatizovaných buildů k VS solution chová jako k jednomu projektu. Výstup se přitom směřuje do jednoho výstupního adresáře build agenta (např. C:\Builds\1\<jmeno projektu>\Binaries) a odsud se poté kopíruje do Drop Location. Po provedení buildu tedy skončí v Drop Location všechny assembly smíchány dohromady, což rozhodně není požadováno. (V mém případě u solution, která obsahuje Web projekt, Silvelight aplikací a WCF Windows Service, obsahoval Drop Location navíc i Silverlight assembly, které jsou normálně jen součástí xap balíčku.)

První část řešení je úprava Build Template souboru, ve které změníme parametry spouštění MSBuildu.
Provedeme následující kroky:

  1. V definici automatizovaného buildu na záložce process vytvoříme pomoci voleb Details a New kopii DefaultTemplate.xaml template souboru.
  2. Vytvořený XAML build template otevřeme, zobrazí se nám ve Workflow designeru.
  3. Vyhledáme aktivitu “Run MSBuild for Project” (v “Try Compile, Test, and Associate Changesets and Work Items”  / “Try Compile and Test” / “Compile and Test” / “For Each Configuration in BuildSettings.PlatformConfigurations” / “Compile and Test for Configuration” / “If BuildSettings.HasProjectsToBuild” / “Compile the Project”).
    BuildTemplate
  4. Nastavíme vlastnost OutDir na prázdnou hodnotu.
    Tím MSBuild při spuštění použije pro výstup cestu nastavenou v projektech – OutputPath.
  5. Změníme vlastnost CommandLineArguements na tento výraz:
    String.Format("/p:SkipInvalidConfigurations=true;TeamBuildOutDir=""{0}"" {1}", BinariesDirectory, MSBuildArguments)
    Tím bude mít MSBuild při spuštění nastavenou proměnnou TeamBuildOutDir na cestu výstupu build agenta.

Úpravu template XAML souboru máme hotovou, soubor zavřeme a uložíme a operaci Check In musíme soubor uložit na server.

Změna projekt souborů

Provedli jsme úpravu spouštění MSBuildu, tím máme k dispozici nastavenou proměnnou TeamBuildOutDir. Nyní upravíme soubory projektů tak, aby hodnotu této proměnné využily k nastavení cíle kompilace. Následující změnu provedeme v těch projektech, které jsou v buildovaných solution navolené jako spouštěcí (tj. ne u projektů, které jsou pouze referencované v jiných projektech).

Projekty otevřeme v XML editoru tak, že na projektu ve Visual Studiu zvolíme volbu Unload Project a následně volbu Edit <jméno souboru .csproj>. V souboru je několik sekcí PropertyGroup pro jednotlivé platformy např.:
PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' " ve kterých je určen OutputPath. Za všechny takovéto sekce přidáme následující MSBuild element:

<PropertyGroup Condition="$(TeamBuildOutDir) != '' ">
  <OutputPath>$(TeamBuildOutDir)\$(MSBuildProjectName)</OutputPath>
</PropertyGroup>

Tím bude v případě našeho automatizovaného buildu OutputPath přenastaven na cestu buildu rozšířenou o adresář podle názvu projektu.

Ještě provedeme jednu nezávislou úpravu, součástí výstupu buildu jsou i xml dokumentační soubory od referencovaných assembly. Protože ty v našem release buildu nechceme, přidáme ještě do projektu MSBuild příkaz na odstranení .xml souborů po ukončení buildu. Přidáme novou sekci target DeleteXmlFiles a přidáme nebo rozšíříme sekci AfterBuild o volání DeleteXmlFiles. 

<Target Name="DeleteXmlFiles" Condition=" '$(Configuration)' == 'Release' ">
  <Message Text="Deleting xml documentation files." Importance="normal" />
  <ItemGroup>
    <FilesToDelete Include="$(OutputPath)\*.xml" />
  </ItemGroup>
  <Delete Files="@(FilesToDelete)" />
</Target>

<Target Name="AfterBuild">
  ...
  <CallTarget Targets="DeleteXmlFiles" />
</Target>

Pozn.: Všimněte si podmínky u target DeleteXmlFiles na mazání pouze pokud se jedná o Release konfiguraci. Pokud potřebujeme provést více akcí v sekci AfterBuild, jednu např. vždy a druhou podle takovéto podmínky, nemůžeme k existujcí akci AfterBuild bez podmínky přidat druhou sekci AfterBuild s podmínkou (např. <Target Name="AfterBuild" Condition=" '$(Configuration)' == 'Release' ">). Target sekce se stejným jménem totiž může být pouze jednou (v opačném případě by se volala pouze jedna z těchto sekcí). Místo zavedení nové sekce se jménem AfterBuild tedy novou sekci pojmenujeme jiným jménem a v sekci AfterBuild jí voláme pomoci elementu CallTarget.

Výsledkem buildu tedy nyní bude, že se v Drop Location vytvoří pro každý projekt samostatný adresář a v něm budou výsledné assembly (navíc bez souborů xml). Pro jiné než webové projekty tímto máme hotovo, ale pro webové projekty musíme ještě provést další úpravy build scriptu.

Provedení změn výstupu pro webové projekty

Při kompilaci webového projektu automatizovaným buildem dochází navíc k jeho publikaci do adresáře _PublishedWebsites\<jmeno projektu>. Webový projekt totiž navíc používá MSBuild script soubor Microsoft.WebApplication.targets (pomoci elementu import), ve kterém je publikace do tohoto adresáře definována.

V Drop Folder nám nyní vzniká adresářová struktura takto:
(První podadresář <jmeno projektu> vznikl změnou OutputPath v projekt souboru, jinak by popsaná adresářová struktura byla přímo v Drop Folder.)

<jmeno projektu> Obsahuje zkompilované assembly webového projektu (tj. jen obsah bin adresáře)
<jmeno projektu>\
_PublishedWebsites\<jmeno projektu>
Výstup publikovaného webu
<jmeno projektu>\
_PublishedWebsites\<jmeno projektu>
\Bin
Bin adresář webu, obsahuje zkompilované assembly a dokumentační xml soubory.

Aby se nám výstup buildu webových projektů choval stejně jako v jiných projektech, provedeme úpravy přímo v souboru Microsoft.WebApplication.targets. Soubor je umístěn v cestě
c:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\WebApplications.
Modifikovanou verzi, můžete stáhnout zde a na build serveru přehrát.

Popíšeme si jednotlivé provedené změny souboru:

Původní řádek definující výstupní cestu:

<WebProjectOutputDir Condition="!$(WebProjectOutputDirInsideProject)">$(OutDir)_PublishedWebsites\$(MSBuildProjectName)</WebProjectOutputDir>

je změněn na:

<WebProjectOutputDir Condition="!$(WebProjectOutputDirInsideProject)">$(OutDir)</WebProjectOutputDir>

OutDir máme v projektu nastaven na <jmeno projektu>, nyní jsme nastavili výstup publikace webu také do tohoto adresáře. Aby nám ale v něm nezůstali i zkompilované assembly (ty chceme mít jen ve složce bin), provedeme před publikací odstranění tohoto adresáře a jeho nové vytvoření, aby byl prázdný.

To se provádí následujícími elementy, které jsou přidány na začátek sekce _CopyWebApplicationLegacy:

<Message Text="Copying Web Application Project Files for $(MSBuildProjectName)" />

<RemoveDir Directories="$(WebProjectOutputDir)" ContinueOnError="true" />
<MakeDir Directories="$(WebProjectOutputDir)" />

Tím je tedy zajištěno, že po spuštění automatizovaného buildu bude výstup webového projektu vytvořen v Drop Folder\<jmeno projektu> a assembly v Drop Folder\<jmeno projektu>\Bin.

V sekci _CopyWebApplicationLegacy jsou dále provedeny další úpravy:

  • Assembly <jméno projektu>.XmlSerializers.dll je při buildu špatně nahrána do adresáře webu a ne do adresáře bin (chyba je popsána např. zde). Provedeme úpravu řádku:
<Copy SourceFiles="$(IntermediateOutputPath)$(_SGenDllName)"
      DestinationFolder="$(WebProjectOutputDir)\%(Content.SubFolder)%(Content.RecursiveDir)"
      SkipUnchangedFiles="true"
      Condition="'$(_SGenDllCreated)'=='true'"
      Retries="$(CopyRetryCount)"
      RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"/>

kde změníme cílový adresář:

<Copy SourceFiles="$(IntermediateOutputPath)$(_SGenDllName)"
      DestinationFolder="$(WebProjectOutputDir)\bin"
      SkipUnchangedFiles="true"
      Condition="'$(_SGenDllCreated)'=='true'"
      Retries="$(CopyRetryCount)"
      RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"/>
  • Také se při publikaci neprovádí transformace web configů, to napravíme přidáním jejího volání na konec sekce _CopyWebApplicationLegacy:
<!-- Transform Web.config -->
<TransformXml Source="Web.config"
              Transform="Web.$(Configuration).config"
              Destination="$(WebProjectOutputDir)\Web.config"/>

<Delete Files="$(WebProjectOutputDir)\Web.Debug.config"></Delete>
<Delete Files="$(WebProjectOutputDir)\Web.Release.config"></Delete>

a na začátek souboru doplníme deklaraci tasku pro XML transformace:

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>
  • Poslední úpravou je přidání mazání dokumentačních xml souborů podobně jako to děláme v jiných newebových projektech. Na konec sekce _CopyWebApplicationLegacy proto umístíme příkaz pro jejich vymazání z bin adresáře:
<Message Text="Delete xml documentation files." Importance="normal"/>
<ItemGroup>
  <FilesToDelete Include="$(WebProjectOutputDir)\bin\*.xml" />
</ItemGroup>
<Delete Files="@(FilesToDelete)"></Delete>

Tím máme všechny úpravy hotové, soubor Microsoft.WebApplication.targets nesmíme zapomenout nahrát na počítač build serveru.

Výsledkem je tedy, že se nám výstup webového projektu chová podobně jako u ostatních projektů, takže v Drop Location je pro každý projekt samostatný adresář, výsledné assembly jsou bez souborů xml a u webu je navíc správně provedena transformace souboru web.config.

Popsané řešení není samozřejmě jediné možné, článek hlavně demonstruje flexibilitu MSBuild 4.0 a možnosti úprav build procesu, který je ve verzi TFS Build serveru 2010 řešen technologií WF 4.0.


Odkazy na popsané řešení změn výstupu automatizovaného buildu:
http://social.msdn.microsoft.com/Forums/en-US/tfsgeneral/thread/be6005a7-d9e9-4905-b31d-c3c308e29cc9 http://stackoverflow.com/questions/2784335/how-can-i-get-tfs-2010-to-build-each-project-to-a-separate-directory http://msdn.microsoft.com/en-us/library/ff977206.aspx
http://social.msdn.microsoft.com/Forums/en/tfsbuild/thread/a62a6f98-ec44-46c1-a0d0-7f441f0db973
http://archpulse.wordpress.com/2010/11/24/tfs-2010-customize-build-output-changes-and-ms-tests

Úprava Microsoft.WebApplication.targets:
http://social.msdn.microsoft.com/Forums/en-NZ/tfsbuild/thread/1b07e771-9e45-4b6b-af2f-338874676f78 http://forums.asp.net/p/1043621/1534682.aspx

Volání XML Transformace:
http://stackoverflow.com/questions/2905151/msbuild-script-and-vs2010-publish-apply-web-config-transform http://geekswithblogs.net/EltonStoneman/archive/2010/08/20/using-msbuild-4.0-web.config-transformation-to-generate-any-config-file.aspx

O technologii Windows Workflow Foundation se také můžete dočíst v naší sérii zde.

 

hodnocení článku

0 bodů / 1 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback