Profilo di StevenDevelopmentalBlogElenchi Strumenti Guida

Blog


20 gennaio

CreateFoldersItem Custom Task for MSBuild

Last year I did a fair amount of MSBuild stuff, and really enjoyed it. And when I recently wrote an MSBuild task to clean up TFS source bindings in Visual Studio solutions and projects, it rekindled my interest and reminded me how much I like MSBuild.

That’s why today I decided to write another task to add to my library. I wanted to be able to get an item group containing folders, in particular the last name token each directory. For example, for the path “C:\Program Files\BitLocker\en-US\” I wanted to get the “en-US”.

I did a quick look to see if there was a way to do it with the existing ‘CreateItem’ task, or if anyone else had tried to do this and had a solution. But to be honest I didn’t look TOO hard because I really wanted to tackle this problem myself!

Enter: Snagy.Tasks.CreateFoldersItem

I’m actually writing this post while I work on the task. Think of it as a step by step account, and I’ll be writing in a present tense, so forgive me if it sounds daft. But if you’ve never created a custom task for MSBuild before, I’ll cover off all the basics.

First, I created a class library project in C# (actually I already had one from the last task I wrote, so I am just adding to that). Up front you’ll want to add some assemblies to your references list:

Microsoft.Build.Utilities
Microsoft.Build.Framework

Next, add a new class, call it ‘CreateFoldersItem’. At the top, add using statements for the 2 above namespaces, and add a using statement for System.IO while you are at it. Now, make your class inherit from ‘Task’ which is in the Microsoft.Build.Utilities namespace. You can also choose to implement ITask instead, but then you need to provide a little more information in your class. Its easier just to inherit from Task instead. One final thing you need to do to make it build is override the ‘Execute’ method. Do this now but don’t provide any implementation yet other than to ‘return true’. Build your task to make sure its all hunky dory.

Next we want to provide attributes on our task that people can use to set information to be used by the task. I was thinking of a syntax something like this:

<CreateFoldersItem Include=”C:\Projects;C:\Program Files”                    
                              Exclude=”C:\Projects\MSBuild\;C:\Projects\Lisp”
                              Recursive=”true”>
     <Output ItemName=”AllFolders” TaskParameter=”Include” />
</CreateFoldersItem>

This would take semi-colon delimited folders as input and provide a single ItemGroup of all sub-folders. If Recursive is false, only look 1 level deep, otherwise hit the bottom. The Exclude is a little tricky but I think just a semi-colon delimited list of folder names will suffice. Also I’ve deliberately used a semi-colon delimited list for Include and Exclude so that we can use ItemGroups as inputs in both those cases. That way you could use this task to create an include list and an exclude list, and then create a third list which is a delta of the two.

Ok so back to our class. We need to define 3 properties here. For my task, only the ‘Include’ attribute is mandatory. Exclude will be empty by default, and Recursive will be false by default. We can easily create the properties with C#3.0 syntax:

[Required] [Output] public ITaskItem[] Include { get; set; }
public bool Recursive { get; set; }
public ITaskItem[] Exclude { get; set; }

The ‘Required’ attribute means the Task cannot be called without mention of that property. And the ‘Output’ attribute means that the property can be used in the ‘TaskParameter’ attribute of the <Output> element. The ITaskItem interface stores individual items for passing around. It is best practise to use an array of these for accepting and returning ItemGroups.

Now we need to focus on our core functionality. We need to iterate through each Include item and add each sub-folder to a list. Since each include item might be a child or parent of another include item, we need to think about duplication. Until now I hadn’t thought if I want my returned ItemGroup to include duplicates. This could easily be configurable for the user by adding a ‘bool Distinct’ property but I’ll just assume that we don’t want duplicates to make it easy.

One of the things I want to be able to do is get some metadata out of my returned folders, specifically the last location in the folder path token (the ‘en-us’ in the example earlier). I’ll call this the folder ‘Name’ (since this is the convention used by the DirecoryInfo class in System.IO). This lets us get just the folder name using metadata like this:

<Message Text=”Folder name = %(AllFolders.Name)” />

Let’s start by creating a simple method that takes the string path of a folder and returns an ITaskItem which includes the above described metadata:

private TaskItem CreateTaskItemFromFolder(string folder)
{
    DirectoryInfo di = new DirectoryInfo(folder);
    Hashtable metadata = new Hashtable();
    metadata.Add("Name", di.Name);
    return new TaskItem(folder, metadata);
}

So what’s happening here? Well we use the DirectoryInfo class to provide us the last token in the directory path, via the ‘Name’ property (ie. di.Name). Metadata needs to be added via a non-generic implementation of IDictionary, in this case I used a System.Collections.Hashtable. The best implementation of an ITaskItem is the TaskItem class (surprised?) and its constructor lets us provide the folder path and the metadata as parameters.

Now we can use this method to create a TaskItem for each folder that we find. We need a method that will accept a base folder as a parameter, and return a List of TaskItems based on all child folders (with consideration to the ‘Recursive’ option). Here’s a method that does just that:

        public IList<TaskItem> GetChildFolders(string folder, bool recursive)
        {
            SearchOption searchOption = (recursive ?
                                         SearchOption.AllDirectories :
                                         SearchOption.TopDirectoryOnly);
            string[] subfolders = Directory.GetDirectories(folder, "*", searchOption);           
            IEnumerable<TaskItem> children =
                  subfolders.Select(s => CreateTaskItemFromFolder(s));
            return children.ToList();
        }

We utilise the existing ‘GetDirectories’ static method of the System.IO.Directory class. This method has an overload that accepts the ‘SearchOption’ enum which pretty much means recursive or not. Note that when specifying SearchOption.AllDirectories, the search will recursively search your whole tree from that point, even following folder shortcuts. This means if you have a shortcut to a higher level folder, you could potentially get stuck in an infinite loop. So be careful: if this task is called with Recursive=true, then ensure its not called on a folder that nests to itself via shortcuts.

Finally with a little bit of lambda magic, we create a task item from each folder returned by GetDirectories. That’s all the hard work done, we just need to bring it together in our Execute method and overwrite the ‘Include’ folder with the new results. Here’s what the first bit of code in our Execute method looks like:

IList<TaskItem> results = new List<TaskItem>();

          foreach (ITaskItem item in Include)
          {               
                IList<TaskItem> children = GetChildFolders(item.ItemSpec, Recursive);
                results = results.Union(children).ToList();
          }

Essentially the above code just creates one big list of task items by unioning the result of each folder call. After the loop completes we have all the included subfolders in one list. We now need to remove any exclude folders from that list. The next bit of code looks like this:

            foreach (ITaskItem item in Exclude)
            {
                 results = results.Where(x => x.ItemSpec.ToLower() != item.ItemSpec.ToLower()).ToList();
            }

This code is not as difficult as it looks. Once again we use a lambda to specify a condition on which we want to filter results. That condition is based on matching the folder strings from the exclude list against those in the current results list. The output is a new list without the matching items, and we just overwrite Results with this new list.

The last piece of the puzzle is to push our output back into the Include list of task items:

var r = results.Cast<ITaskItem>();
Include = r.ToArray();

Simply put, we need to cast our list of TaskItem to a list of ITaskItem and then push it out as an array.

And that’s it! A couple things to mention though. In the final version of my code there are lots of checks around whether folders exist. Its also worth mentioning that instead of using .Select and .Where methods, you can use standard LINQ syntax instead. I just found the lambda syntax more concise and easier to read.

So what’s the final result? Well you can do funky things like this:

<CreateFoldersItem Include="C:\Projects\Secret"
                              Recursive="true"
                              Exclude="C:\Projects\Secret\NotReallySecret">
             <Output ItemName="SecretFolders" TaskParameter="Include" />
</CreateFoldersItem>
<CreateFoldersItem Include="C:\Projects"
                              Recursive="true"
                              Exclude="@(SecretFolders)">
             <Output ItemName="AllFolders" TaskParameter="Include" />
</CreateFoldersItem>

In the above example, we get an item group for all folders under C:\Projects except for all the secret projects. Note that AllFolders will include C:\Projects\Secret\NotReallySecret\ because it was excluded from the first list!

I hope you find this task useful. You can download the DLL and example usage here.

Commenti (4)

Attendere...
Il commento immesso è troppo lungo. Immetti un commento più breve.
Immissione non effettuata. Riprova.
Impossibile aggiungere il commento al momento. Riprova più tardi.
Per aggiungere un commento è necessaria l'autorizzazione di un genitore. Chiedi autorizzazione
I tuoi genitori hanno disattivato i commenti.
Impossibile eliminare il commento al momento. Riprova più tardi.
Hai raggiunto il numero massimo di commenti pubblicabili giornalmente. Riprova tra 24 ore.
Impossibile lasciare commenti. La funzionalità è stata disattivata perché i sistemi hanno rilevato una possibile attività di spamming dal tuo account. Se ritieni che il tuo account è stato disattivato per errore, contatta il supporto tecnico di Windows Live.
Esegui il seguente controllo di protezione per completare la pubblicazione del commento.
I caratteri digitati nel controllo di protezione devono corrispondere ai caratteri dell'immagine o della riproduzione audio.

Per aggiungere un commento, accedi con il tuo Windows Live ID (se utilizzi Hotmail, Messenger o Xbox LIVE possiedi già un Windows Live ID). Accedi


Non hai ancora un Windows Live ID? Registrati

13 Ago.
Senza nomeha scritto:
http://www.batterygoshop.co.uk/toshiba/satellite-3000.htm toshiba satellite 3000 battery ,
http://www.batterygoshop.co.uk/toshiba/pa3128u.htm toshiba pa3128u battery ,
http://www.batterygoshop.co.uk/toshiba/pa3191u.htm toshiba pa3191u battery ,
http://www.batterygoshop.co.uk/toshiba/portege-4000.htm toshiba portege 4000 battery ,
http://www.batterygoshop.co.uk/toshiba/portege-m200.htm toshiba portege m200 battery ,
http://www.batterygoshop.co.uk/toshiba/portege-m205.htm toshiba portege m205 battery ,
http://www.batterygoshop.co.uk/toshiba/portege-m400.htm toshiba portege m400 battery ,
http://www.batterygoshop.co.uk/toshiba/pa3128u-grey.htm toshiba pa3128u grey battery ,
http://www.batterygoshop.co.uk/toshiba/pa3191u-grey.htm toshiba pa3191u grey battery ,
http://www.batterygoshop.co.uk/toshiba/te2000-grey.htm toshiba te2000 grey battery ,
http://www.batterygoshop.co.uk/toshiba/pa3209u.htm toshiba pa3209u battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-1100.htm toshiba satellite 1100 battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-1110.htm toshiba satellite 1110 battery ,
http://www.batterygoshop.co.uk/toshiba/pa3210u.htm toshiba pa3210u battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-1115.htm toshiba satellite 1115 battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-p20.htm toshiba satellite p20 battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-p25.htm toshiba satellite p25 battery ,
http://www.batterygoshop.co.uk/toshiba/pa3399u-1bas.htm toshiba pa3399u-1bas battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-a100.htm toshiba satellite a100 battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-m100.htm toshiba satellite m100 battery ,
http://www.batterygoshop.co.uk/toshiba/pa3399u-1bas.htm toshiba pa3399u-1bas battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-a100.htm toshiba satellite a100 battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-a105.htm toshiba satellite a105 battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-a80.htm toshiba satellite a80 battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-m100.htm toshiba satellite m100 battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-m105.htm toshiba satellite m105 battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-m110.htm toshiba satellite m110 battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-m115.htm toshiba satellite m115 battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-m40.htm toshiba satellite m40 battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-m45.htm toshiba satellite m45 battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-m50.htm toshiba satellite m50 battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-m55.htm toshiba satellite m55 battery ,
http://www.batterygoshop.co.uk/toshiba/pa3356u.htm toshiba pa3356u battery ,
http://www.batterygoshop.co.uk/toshiba/a50.htm toshiba a50 battery ,
http://www.batterygoshop.co.uk/toshiba/a55.htm toshiba a55 battery ,
http://www.batterygoshop.co.uk/toshiba/portege-m300.htm toshiba portege m300 battery ,
http://www.batterygoshop.co.uk/toshiba/portege-m500.htm toshiba portege m500 battery ,
http://www.batterygoshop.co.uk/toshiba/portege-s100.htm toshiba portege s100 battery ,
http://www.batterygoshop.co.uk/toshiba/qosmio-f20.htm toshiba qosmio f20 battery ,
http://www.batterygoshop.co.uk/toshiba/qosmio-f25.htm toshiba qosmio f25 battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-a50.htm toshiba satellite a50 battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-a55.htm toshiba satellite a55 battery ,
http://www.batterygoshop.co.uk/toshiba/pa3356u-1bas.htm toshiba pa3356u-1bas battery ,
http://www.batterygoshop.co.uk/toshiba/satellite-a50.htm toshiba satellite a50 battery ,
21 Apr.
Senza nomeha scritto:
http://www.batterygoshop.co.uk/laptop-ac-adapter/compaq/compaq-19V-3.16A-60w-5.5mm-2.5mm.htm F4814A ADP-75FB compaq 19V 3.16A 60w 5.5mm*2.5mm adapter laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/compaq/compaq-19V-4.74A-90w-5.5mm-2.5mm.htm compaq 19V 4.74A 90w 5.5mm*2.5mm adapter laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/dell/dell-19.5V-3.34A-65w-7.4mm-5.0mm-with-pin-pa-12.htm PA-1650-05D2 AD-90195D dell 19.5V 3.34A 65w 7.4mm*5.0mm with pin pa-12 adapter laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/dell/dell-19.5V-4.62A-90w-7.4mm-5.0mm-with-pin-pa-10.htm PA-1650-05D2 PA-1900-02D dell 19.5V 4.62A 90w 7.4mm*5.0mm with pin pa-10 adapter laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/dell/dell-19.5V-6.7A-130w-7.4mm-5.0mm-with-pin-pa-13.htm dell 19.5V 6.7A 130w 7.4mm*5.0mm with pin pa-13 adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/dell/dell-19.5V-7.7A-150w-7.4mm-5.0mm-with-pin-pa-15.htm PA-1151-06D dell 19.5V 7.7A 150w 7.4mm*5.0mm with pin pa-15 adapter laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/dell/dell-19V-3.16A-60w-5.5mm-2.5mm.htm PA-1600-06D1 PA-1600-06D2 dell 19V 3.16A 60w 5.5mm*2.5mm adapter laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/dell/dell-20V-3.5A-70w-horseshoe-style-special-for-dell-pa-6.htm ADP-70E ADP-70EB dell 20V 3.5A 70w horseshoe style special dell pa-6 adapter laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/delta/delta-19V-3.42A-65w-5.5mm-2.5mm.htm ADP-65DB delta 19V 3.42A 65w 5.5mm*2.5mm adapter laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/delta/delta-19V-4.74A-90w-5.5mm-2.5mm.htm ADP-90FB delta 19V 4.74A 90w 5.5mm*2.5mm adapter laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/fujitsu/fujitsu-19V-3.16A-60w-5.5mm-2.5mm.htm A4190 fujitsu 19V 3.16A 60w 5.5mm*2.5mm adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/fujitsu/fujitsu-19V-4.74A-90w-5.5mm-2.5mm.htm C2110 fujitsu 19V 4.74A 90w 5.5mm*2.5mm adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/fujitsu/fujitsu-19V-4.22A-80w-5.5mm-2.5mm.htm CA01007-0920 FMV-AC314 fujitsu 19V 4.22A 80w 5.5mm*2.5mm adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/hp/hp-19V-4.74A-90w-5.5mm-2.5mm.htm PPP009X PW-AC003 hp 19V 4.74A 90w 5.5mm*2.5mm adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/hp/hp-18.5V-3.5A-65w-4.8mm-1.7mm.htm DC359A DL606A hp 18.5V 3.5A 65w 4.8mm*1.7mm adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/hp/hp-19V-4.74A-90w-4.8mm-1.7mm.htm hp 19V 4.74A 90w 4.8mm*1.7mm adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/hp/hp-18.5V-4.9A-90w-5.5mm-2.5mm.htm hp 18.5V 4.9A 90w 5.5mm*2.5mm adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/hp/hp-18.5V-4.9A-90w-flat-dc.htm hp 18.5V 4.9A 90w flat dc adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/hp/hp-18.5V-6.5A-120w-5.5mm-2.5mm.htm PA-1121-02H PA-1121-12H hp 18.5V 6.5A 120w 5.5mm*2.5mm adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/hp/hp-18.5V-6.5A-120w-flat-dc.htm hp 18.5V 6.5A 120w flat dc adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/hp/hp-19V-7.1A-135w-flat-dc.htm hp 19V 7.1A 135w flat dc adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/hp/hp-19V-9.5A-180w-flat-dc.htm hp 19V 9.5A 180w flat dc adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/hp/hp-19V-3.16A-60w-5.5mm-2.5mm.htm AC-C10 CQPS1200 hp 19V 3.16A 60w 5.5mm*2.5mm adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/ibm/ibm-16V-3.5A-56w-5.5mm-2.5mm.htm 02K6553 02K655 ibm 16V 3.5A 56w 5.5mm*2.5mm adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/ibm/ibm-20V-3.25A-65w-7.9mm-5.5mm-DC-with-pin-inside.htm 92P1153 92P1154 ibm 20V 3.25A 65w 7.9mm*5.5mm DC with pin inside adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/ibm/ibm-20V-4.5A-90w-7.9mm-5.5mm-grey-DC-with-pin-inside.htm PA-1900-171 ibm 20V 4.5A 90w 7.9mm*5.5mm grey DC with pin inside adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/liteon/liteon-19V-3.42A-65w-5.5mm-2.5mm-white.htm liteon 19V 3.42A 65w 5.5mm*2.5mm white adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/liteon/liteon-19V-3.16A-60w-5.5mm-2.5mm.htm liteon 19V 3.16A 60w 5.5mm*2.5mm adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/liteon/liteon-19V-3.95A-75w-5.5mm-2.5mm.htm PA-1750-01 liteon 19V 3.95A 75w 5.5mm*2.5mm adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/liteon/liteon-19V-4.74A-90w-5.5mm-2.5mm.htm PA-1900-15 liteon 19V 4.74A 90w 5.5mm*2.5mm adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/liteon/liteon-19V-6.3A-120w-5.5mm-2.5mm.htm PA-1121-08 liteon 19V 6.3A 120w 5.5mm*2.5mm adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/liteon/liteon-19V-6.3A-120w-4pin-round.htm liteon 19V 6.3A 120w 4 pin round adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/liteon/liteon-20V-6A-120w-5.5mm-2.5mm.htm liteon 20V 6A 120w 5.5mm*2.5mm adapter laptop laptop battery ,
http://www.batterygoshop.co.uk/laptop-ac-adapter/liteon/liteon-19V-4.74A-90w-5.5mm-1.7mm.htm PA-1900-04 liteon 19V 4.74A 90w 5.5mm*1.7mm adapter laptop laptop battery ,
13 Apr.
Senza nomeha scritto:
When the <a href="http://www.game4power.com/">Wow Gold </a> wolf finally found the <a href="http://www.game4power.com/">Buy Wow Gold</a>hole in the chimney he crawled <a href="http://www.game4power.com/buy-gold/">wow gold cheap </a> down and KERSPLASH right into that kettle of water and that was <a href="http://www.wowgoldone.com/"> cheapest wow gold </a> the end of his troubles with the big bad wolf.

<a href="http://www.game4power.com/">game4power</a>,<a href="http://www.game4power.com/buy-gold/">buy cheap wow gold</a>

The next day the <a href="http://www.wowgoldone.com/"><strong> cheap wow gold </strong></a><a href="http://www.game4power.com/">buy gold wow</a> little pig invited his mother over . She said "You see it is just as I told you. The way to <a href="http://itemchannel.com">wow gold</a>get along in the world is to do <a href="http://www.itemchannel.com/">world of warcraft gold</a> things as well as you can." Fortunately for that little pig, he <a href="http://www.game4power.com/buy-gold">cheapest wow gold</a> learned that lesson. And he just lived happily ever after!
3 Apr.

Riferimenti (4)

L'URL di riferimento per questo intervento è:
http://stevennagy.spaces.live.com/blog/cns!B2EFDBF0964586B3!360.trak
Blog che fanno riferimento a questo intervento