Sonntag, Mai 04, 2008

MSBuild directory listing / batching

Immer wenn ich mit den Standard Boardmitteln von MSBuild versuche alle Verzeichnisse aufzulisten, so könnte ich verzweifeln. Immer die Verzeichnisse mittels DIR-Befehl in eine Datei schreiben und anschließend mittels ReadLine-Task auslesen.

    1   <Exec Command = "dir &quot; $(RefItemsPath) &quot; /b /A:D /S> &quot; $(SourceFolder)\refFolders.bbb &quot; " Condition = "'$(RefItemsPath)'!='' and '$(ReferencePath)'==''" />

    2   <Exec Command = "echo $(RefItemsPath)>> &quot; $(SourceFolder)\refFolders.bbb &quot; " Condition = "'$(RefItemsPath)'!='' and '$(ReferencePath)'==''" />

    3   <CreateItem Include = "$(SourceFolder)\refFolders.bbb" Condition = "'$(ReferenceFolder)'!='' and '$(ReferencePath)'==''" >

    4       <Output TaskParameter = "Include" ItemName = "refs" />

    5   </CreateItem>

    6   <ReadLinesFromFile File = "@(refs)" Condition = "'$(ReferenceFolder)'!='' and '$(ReferencePath)'==''" >

    7       <Output TaskParameter = "Lines" PropertyName = "readedLines" />

     8   </ReadLinesFromFile>

Aus meiner Sicht nicht wirklich schön, wenn man nur Verzeichnisse haben möchte, daher habe ich (endlich) einen neuen Task erstellt, da ich immer noch keinen in den CommunityTools gefunden habe. Der Task ist so super einfach, erspart mir aber immer einige Hacks, außerdem werden Build-Files verständlicher.

    3   using System;

    4   using System . Collections . Generic;

    5   using System . Text;

    6   using Microsoft . Build . Framework;

    7   using Microsoft . Build . Utilities;

    8   using System . Xml;

    9   using System . IO;

   10   using System . Collections;

   11   using System . Diagnostics;

   12  

   13   namespace DE . CapeVision . MSBuild . Tasks

   14  {

   15      public class DiretoryListingTask : Task

   16      {

   17          private string baseFolder;

   18          public bool IncludeSubFolders { get ; set ; }

   19          public string SearchPattern { get ; set ; }

   20          [ Output ]

   21          public string [] FolderContent { get ; set ; }

   22          [ Required ]

   23          public string BaseFolder

   24          {

   25              get { return baseFolder; }

   26              set { baseFolder = value ; }

   27          }

   28          public override bool Execute()

   29          {

   30   //#if(DEBUG)

   31   //            Debugger.Launch();

   32   //#endif

   33              if ( ! Directory . Exists( this . baseFolder))

   34                  return true ;

   35              string [] folders = Directory . GetDirectories(

   36                  this . baseFolder

   37                  , string . IsNullOrEmpty( this . SearchPattern) ? "*" : this . SearchPattern

   38                  , this . IncludeSubFolders ? SearchOption . AllDirectories : SearchOption . TopDirectoryOnly

   39                  );

   40              this . FolderContent = folders;

   41              return true ;

   42          }

   43      }

   44  }

Das 2. Thema, was ich in dem Zuge angehen wollte, war das Batching mal zu überprüfen. MsBuild ist in der Lage Befehle die mit TaskItems (array) aufgerufen werden zu parallelisieren, bzw. den Befehl immer wieder auszuführen.  Das Batching wird mittels des „%“-Operators angekündigt und alles weitere und ggf. Typenkonvertierungen für den Task macht MsBuild. Man kann das sehr gut mit einem einfachen Message-Task testen und nachstellen. Leider hatte meine Ausgabe nie wirklich geklappt, immer wenn 2 Listen verknüpft werden sollten, so kam folgendes raus:

  iis () --- BROWSER_VIEW
  iis () --- LOG_VIEW   iis () --- FILE_VIEW
  iis () --- CHANGESET_VIEW
  iis () --- TICKET_ADMIN
  iis () --- MILESTONE_ADMIN
  iis () --- ROADMAP_ADMIN
  iis () --- REPORT_ADMIN
  iis () --- WIKI_ADMIN
  iis () --- TIMELINE_VIEW
  iis () --- SEARCH_VIEW
  iis () --- TRAC_ADMIN
  iis (D:\Cache\Msdn\AdvancedBasics) ---
  iis (D:\Cache\Msdn\ContinuousIntegration) ---
  iis (D:\Cache\Msdn\DataPoints) ---
  iis (D:\Cache\Msdn\DependencyInjection) ---
  iis (D:\Cache\Msdn\ExtremeASPNET) ---
  iis (D:\Cache\Msdn\Foundations) ---
  iis (D:\Cache\Msdn\MVCFramework) ---
  iis (D:\Cache\Msdn\OfficeSpace) ---
  iis (D:\Cache\Msdn\TestRun) ---

   12   <Message Importance = "high" Text = "iis (%(folder.FullPath)) --- %(permission.Identity)" ></Message>

Nach etwas recherche fand ich auch ähnlich Probleme und irgendwo auch eine Lösung (Gute Erklärung http://blogs.msdn.com/aaronhallberg/archive/2006/09/05/msbuild-batching-generating-a-cross-product.aspx). Man muss die Ausgabe mittels MsBuild-Task in 2 Targets teilen, so dass der MsBuild-Tasks als Batch durchlaufen wird und anschließend der 2. Taks. Mein Skript sieht komplett so aus:

    1   <?xml version = "1.0" encoding = "utf-8" ?>

     2   <Project DefaultTargets = "Test" xmlns = "http://schemas.microsoft.com/developer/msbuild/2003" >

    3     <UsingTask AssemblyFile = "DE.CapeVision.MSBuild.Tasks.dll" TaskName = "DE.CapeVision.MSBuild.Tasks.DiretoryListingTask" />

    4     <ItemGroup>

    5       <permission Include = "BROWSER_VIEW;LOG_VIEW;FILE_VIEW;CHANGESET_VIEW;TICKET_ADMIN;MILESTONE_ADMIN;ROADMAP_ADMIN;REPORT_ADMIN;WIKI_ADMIN;TIMELINE_VIEW;SEARCH_VIEW;TRAC_ADMIN" />

    6     </ItemGroup>

    7     <Target Name = "Test" >

    8       <DiretoryListingTask BaseFolder = "D:\Cache\Msdn" >

    9         <Output TaskParameter = "FolderContent" ItemName = "folder" />

   10       </DiretoryListingTask>

   11       <Message Importance = "high" Text = "iis (%(folder.FullPath)) --- %(permission.Identity)" ></Message>

   12       <Message Importance = "high" Text = "second:" ></Message>

   13       <MSBuild Projects = "$(MSBuildProjectFile)" Targets = "InternalAddPermission" Properties = "Folder=%(folder.FullPath)" />

   14     </Target>

   15     <Target Name = "InternalAddPermission" >

   16       <Message Importance = "high" Text = "third:" ></Message>

   17       <Message Importance = "high" Text = "trac-admin $(folder) permission add tester %(permission.Identity)" ></Message>

    18     </Target>

   19   </Project>

Gebraucht habe ich beides um die Berechtigungen an den TRAC-Repositories anzupassen, die wir haben. (Dann natürlich nicht mit Message-Task ;))

Keine Kommentare: