Powershell MSBuild task

UPDATE: The source and installer for this task can now be found at Codeplex: 
http://powershellmsbuild.codeplex.com/ 

I have written an MSBuild task that hosts a Powershell runspace and allows you to embed Powershell scripts in your MSBuild build files. While MSBuild ships with a large number of useful tasks (and there are also a lot of third party tasks available), there is often something you want to do which doesn’t fit into the tasks provided. You can always write your own tasks to achieve what you want, but this can often become tedious to maintain since you will have to recompile and redeploy the custom task every time you need to make a little change. Having small snippets of script embedded in the MSBuild file itself can be a more easily maintainable solution, and what better language for this than Powershell?

As a custom task, the Powershell task needs to be loaded into your build file with the <UsingTask> element:

<UsingTask AssemblyFile="PowershellMSBuildTask.dll" TaskName ="Powershell"/>

Since tasks in MSBuild don’t have access to the contents of their tags, the scripts themselves need to be MSBuild properties, declared in a <PropertyGroup>:

<PropertyGroup> 
	<HelloWorldScript>Write-Host "Hello world"</HelloWorldScript> 
</PropertyGroup>

Now, to invoke this script, you will use the Powershell task and invoke it from within a target. You reference the script property the same way you reference any other MSBuild script property:

<Target Name="HelloWorld">
  <Powershell Script="$(HelloWorldScript)"/>
</Target>

The full MSBuild file will now be something like this:

<?xml version="1.0" encoding="utf-8" ?>
<Project DefaultTargets="HelloWorld" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	<UsingTask AssemblyFile="PowershellMSBuildTask.dll" TaskName ="Powershell"/>

	<PropertyGroup> 
		<HelloWorldScript>Write-Host "Hello world"</HelloWorldScript> 
	</PropertyGroup>

	<Target Name="HelloWorld">
		<Powershell Script="$(HelloWorldScript)"/>
	</Target>
</Project>

Save it as HelloWorld.proj, place PowershellMSBuildTask.dll in the same directory and run it like this:

msbuild HelloWorld.proj

The output should be as follows:

PS D:tmp> msbuild HelloWorld.proj
Microsoft (R) Build Engine Version 2.0.50727.42
[Microsoft .NET Framework, Version 2.0.50727.42]
Copyright (C) Microsoft Corporation 2005. All rights reserved.
Build started 13.09.2006 19:02:21.
__________________________________________________
Project "D:tmpHelloWorld.proj" (default targets):
Target HelloWorld:
    Hello world
Build succeeded.
    0 Warning(s)
    0 Error(s)
Time Elapsed 00:00:05.09

Not horribly exciting, but it works.

Of course, such a task would have limited usefullness if there wasn’t a way to pass arguments from MSBuild into the custom script. The Powershell task has two ways of doing this.

The first way is an attribute ‘Arguments’. This takes in named arguments on the form ‘varname1=value1;varname2=value2’. varname1 and varname2 will then be available as string variables inside the script. For example:

    <PropertyGroup>
        <HelloArgumentsScript><![CDATA[
        Write-Host ("{0}" -f (([int]$value1) + ([int]$value2)))
        ]]></HelloArgumentsScript>
    </PropertyGroup>

    <Target Name="HelloArguments">
        <Powershell Script="$(HelloArgumentsScript)" Arguments="value1=40;value2=2"/>
    </Target>
 

The output is as expected:

Project "D:tmpHelloWorld.proj" (HelloArguments target(s)):
Target HelloArguments:
    42
Build succeeded.
0 Warning(s)
0 Error(s)

Time Elapsed 00:00:02.78
 

The other way is through the task attribute ‘TaskItems’. As the name implies, this takes in a set of taskitem objects. They are available in the Powershell script as a collection of ITaskItem objects named $TaskItems:  

<PropertyGroup>
    <TaskItemsScript> <![CDATA[ Write-Host ("The answer is {0}" -f (([int]$taskitems[0].ItemSpec) + ([int]$taskitems[1].ItemSpec))) ]]> </TaskItemsScript>
</PropertyGroup>
<PropertyGroup>
    <Argument1>12</Argument1>
    <Argument2>30</Argument2>
</PropertyGroup>
<Target Name="TaskItems">
    <Powershell Script="$(TaskItemsScript)" TaskItems="$(Argument1);$(Argument2)">
</Target>

It is also possible for a script to return values to the calling task. This happens through the Output taskparameter, and works like this:

<Powershell Script="$(ValueReturningScript)">
    <Output TaskParameter="Output" PropertyName="ScriptOutput"/>
</Powershell>

<Message Lines="$(ScriptOutput)"/>
 

The source code can be found in a Subversion repository here, and a downloadable zip including both source and the prebuilt task can be found here.



Posted in Powershell | 100 Comments

Losing faith

(Also posted as a reply to a post on our mailing list, users@ankhsvn.hm.hm)

Gah. I’m starting to lose my faith in my fellow programmers. A little background:

Ankh uses a couple of hash tables to map EnvDTE.Project and EnvDTE.ProjectItem objects (these are from the VS automation model) representing projects and (doh) their project items to our internal objects representing the same artifacts. These hash tables are used extensively when gathering SVN resources to determine whether commands should be enabled and to provide input to the execution of said commands. In other words, when a command is invoked, the hash tables are queried for our internal objects corresponding to the currently selected ProjectItem or Project.

So, if this mechanism fails, commands won’t be enabled in the first place, and if they were, they wouldn’t execute correctly since no resources had been harvested.

And this mechanism will obviously fail if the hash codes retrieved from the Project and ProjectItem objects are different each time they are retrieved using GetHashCode().

I’ll leave the rest of this horror story to the imagination of the reader. I will not reveal the particular type of project that is committing this atrocity; suffice it to say that the common name for it starts with an R and ends with -eport Server Project.

Posted in AnkhSVN | 57 Comments

Debugging more events

A couple of months ago, I posted a class that used the new DynamicMethod class in .NET to dynamically hook up events to all events declared on an object and display a string in VS’ output pane whenever that event is raised. While quite useful, it has some drawbacks; most importantly, if you are doing this to multiple objects of the same or similar type (think multiple controls, all of which share a large number of events), it’s very hard to see which particular object the event is coming from.

In some cases, you might also want to hook up only a particular event or set of events on a class, or exclude certain events. For example, all the Mouse-related events (MouseEnter, MouseMove, MouseLeave) can quickly clog up your output and hide the information you are looking for.

With this in mind, I decided to make a better version of this method. The new version is vastly improved:

  • It now supports a filter string which lets you specify a regex that has to match the name of the event(s) you are after
  • It now supports a custom format string which lets you output a lot of relevant information whenever the event is raised

The filter string can be used like this:

DebugUtils.DebugEvents(someControl, null, ".*Changed"); // only monitor the *Changed events on the control (think databinding events)

That’s pretty much all there is to the filter string, if you know your regular expressions. The format string is a little more involved, though. It supports the following special keywords (yeah, I stole some of these from the VS 2005 tracepoint syntax, so sue me…):

  • $EVENT – Replaced with the name of the event raised (you almost always want this one).
  • $TYPE – Replaced with the fully qualified type name of the object raising the event.
  • $TIMESTAMP – Replaced with the current time.
  • $CALLER – Replaced with the fully qualified method name (and possibly line number if symbols are available) of the method that raised the event.
  • $STACKTRACE – Replaced with a full stack trace of the code that eventually raised the event in question.

You probably don’t want to use the last one unless you also have some filtering on the events (or very few events on the object to begin with); it generates a lot of output. However, it can often be very useful if you need to see why a particular event is being raised. Line numbers and file names will be included if debug symbols are available.

In addition to the special keywords, it can also evaluate simple property and field expressions on the two arguments passed into the event. These are designated as "sender" and "e", respectively (as is the custom for naming event arguments in .NET). For example:

DebugUtils.DebugEvents(this.reportsListView, "$TIMESTAMP: $EVENT raised, e.InvalidRect is {e.Invalidrect}", "Invalidated");

Which prints the following output to VS’ Output Pane:

22:53:32.995125: Invalidated raised, e.InvalidRect is {X=0,Y=0,Width=627,Height=269}
22:53:33.010750: Invalidated raised, e.InvalidRect is {X=0,Y=0,Width=627,Height=269}
22:53:39.932625: Invalidated raised, e.InvalidRect is {X=0,Y=0,Width=627,Height=269}

(etc…)

The source for this file can be found in the SVN repository at http://ankhsvn.com/svn/Utils/trunk/Utils/Debugging/DebugUtils.cs, and unit tests: http://ankhsvn.com/svn/Utils/trunk/UtilsTest/Debugging/DebugUtilsTest.cs. To use it for yourself, either copy the file as is or check out the entire VS 2005 project from http://ankhsvn.com/svn/Utils/trunk/ and build it. The class is made available under a slightly modified MIT license (I removed the clause requiring derivatives to carry the notice).

UPDATE: I’ve removed the use of the ListUtils class so that people who want to copy the file in itself won’t need to also get the ListUtils class. I’ve also added XML doc comments to the various overloads.

Posted in .NET | 52 Comments

AnkhSVN 1.0 RC3 released

Following the release of Subversion 1.4 RC5, we have released a new release candidate of AnkhSVN linked against the Subversion 1.4 RC5 libraries. This Subversion release candidate fixes a bug in Subversion that was triggered by our "Add Solution To Subversion Repository" command and that caused Visual Studio to crash. Other than that, the only change to Ankh itself is support for VS 2005 web projects in the same command. Previously, they would not be detected and you would get the "The following projects are not under the solution root" message even if said web projects actually were under the solution root.

Download from http://ankhsvn.tigris.org/servlets/ProjectDocumentList?folderID=7315

Posted in Uncategorized | 45 Comments

Oops

/me sighs.

Ok, I screwed up. Due to a braino and a particularly brain dead "feature" of our build script, the MSI wasn’t built from the release branch. It was instead built from a short-lived branch I created to integrate our build process with Subversion 1.4. The released MSI is pretty close to what was supposed to have been released (which is of course why I didn’t discover this ;-), but lacks some bug fixes made in the last day.

An updated MSI can be found at the same URL as the other one.

Posted in AnkhSVN | 67 Comments

AnkhSVN 1.0 release candidate available

An AnkhSVN 1.0 release candidate is now available at http://ankhsvn.tigris.org/servlets/ProjectDocumentList?folderID=7315. This is linked to Subversion 1.4 rc4, Berkeley DB 4.4.20, OpenSSL 0.9.8a, Neon 0.25.5 and Zlib 1.2.3.

Note that any application linked to Subversion 1.4 (including the Ankh release candidate) will silently and irrevocably upgrade your working copies to a new working copy format. Use of such an application together with a pre-1.4 client will not work. The TortoiseSVN project makes a 1.4-compatible client available at http://tortoisesvn.net/downloads. 1.4-compatible versions of the svn binaries can be found with the AnkhSVN release candidate.

Also, the release candidate is linked to Berkeley DB 4.4. If you are using a local (file://) BDB repository with Ankh, it will need to be upgraded. Our general recommendation is to avoid the use of BDB for file:// access in favour of FSFS repositories.

Changes since 0.5.5:

  • Internal diff no longer shows duplicates

  • Fix a memory leak in the NSvn.Core.Pool class
  • Clicking the X in the commit dialog now performs a cancel, regardless of whether the most recent action was a cancel or not
  • NSvn exceptions are now serializable
  • Commit dialog now supports Ctrl-Enter for proceeding with a commit
  • Commit dialog text control now supports Ctrl-A to select the whole text
  • NSvn.Core.Client.Delete now supports the ‘force’ parameter… :-/
  • Status icons in the solution explorer are no longer overwritten by VSS ones
  • Configuration dialog has templates for the most common diff and merge tools
  • Individual project items can now be refreshed with the Ankh->Refresh command
  • The ‘?’ status no longer propagates up the solution explorer treeview
  • New projects are now automatically SVN-added if they are versionable
  • Add Solution To Subversion Repository now warns the user if some projects are not under the solution root
  • New configuration dialog
  • Config file changes are picked up without having to restart VS
  • Fix for a memory leak in the solution explorer
  • Redesigned delete support
  • Support for the VS refresh button
  • Fix for a problem in which a premature solution explorer refresh after a folder add terminates the rename of the newly added folder and forces it to be named "NewFolder1".
  • We now call svn_utf_initialize so that iconv SOs don’t have to be loaded for every SVN error message.
  • Workaround for Windows Workflow Foundations beta 2 bug
  • GDI handle leak plugged
  • All dialogs now invariably support standard Enter/Esc for OK/Cancel
  • Blame now works correctly on compound items
  • Use Visual Studio SDK for better VS integration
  • The commit dialog can now be suppressed with the Shift key, like other dialogs
  • Support for alternative names for the SVN administrative directory now uses the functionality offered by SVN itself
  • New "Working Copy Explorer" tool window
  • Better support for renames and moves
  • Redesigned VS integration to take advantage of the Visual Studio SDK
  • Better unicode support for log messages
  • Support for VS 2005 solution folders
  • Dialog tab order fixes
  • New installer using WiX instead of the VS.NET setup projects
  • Fix bug in which Ankh would refuse to load if VC++ wasn’t installed
  • Prevent .svn dir from being autoadded
  • Support rename on folders in projects
  • Prevent Ankh from autoadding ignored files
  • Prevent the solution explorer from repainting itself while Ankh is scanning the tree, improving solution load performance
  • Running of SVN commands from the VS command line is now streamy
  • New command that lets user send a suggestion or error report without actually experiencing an error
  • Diff/Patch uses path relative to the solution directory
  • Better support for third party project types
  • Support commits on working copies created from multiple repositories
  • Commit dialog is now "pseudomodal", allowing the user to diff or view files using VS directly
  • Ankh no longer loads on command line builds that use /rebuild or /clean
  • Support for blame
  • Diff/patch between arbitrary revisions
  • Use the correct name for the Tools menu in non-english versions of VS
  • Support for log
  • Better support for file deletions
  • More intelligent status cache, improving performance (especially in pathological cases)
  • Support for VS 2005
  • Error reports contains the DTE version (and thus implicitly the version of VS)
  • Support for lock/unlock
  • New design for the repository explorer
  • Support for Enterprise Template projects
  • Support for database projects
  • Lots of tweaks, bug fixes and performance enhancements.

Posted in AnkhSVN | 100 Comments

Ok, maybe not this weekend, then

I promised a release candidate this weekend, but it looks like that won’t happen. Integrating our build process with Subversion 1.4 and Neon 0.25 took a lot more effort than expected. We’re now probably looking at either Monday or Tuesday.

And we still haven’t quite decided on the version number…

Posted in AnkhSVN | 49 Comments

Ankh, how do I number thee?

We’ve decided to go ahead and make a new Ankh release. A release candidate will come this weekend, and then a proper release will follow in a week if there are no major problems found. I’ve closed the last of the issues marked 0.6 in our issue tracker, so this seems like a good time to do it

One problem remains, though: what version number to give it? The obvious choice would be 0.6, since our last “official” release was 0.5.5. However, we’re getting sort of tired of the 0.6 marker by now, and we’re considering to maybe give it a version number of 0.7 or even higher. One person I talked to on IRC yesterday suggested 0.9 or even 1.0.

There are already 35 different snapshots all marked as 0.6.0.* around, and the idea is that giving it a higher version number might give a much clearer message that this is indeed a new version.

What do you think?

Posted in AnkhSVN | 32 Comments

Is Ankh a dead project?

To avoid any potential confusion, I’ll start out this post by answering the question above: heck no!

I’m posting this because it is starting to dawn on me that there seems to be a pretty widespread perception that we are indeed a dead project. In particular, this thread on the Subversion users mailing list made this pretty clear.

And of course, the reason for this is obvious. Our tigris.org front page has the following text: “The latest stable release of AnkhSVN was 0.5.5, released on August 17, 2004.”

Ouch.

So it’s been nearly two years since our last “stable” release. Why no releases since then? It’s not like there hasn’t been active development on it since. We’ve added tons of new features, gained VS 2005 compatibility and fixed lots and lots of bugs. “Snapshot” releases have been made at various intervals (the last one was yesterday), exposing users to the new features.

As I see it, there are primarily two reasons we never made an “official” release. First, most of the users we interact directly with are those who use the snapshots. Whenever we talk to someone still using 0.5.5, the first thing we tell them is to try the latest snapshot instead. There’s been something of a feeling that having the snapshots available is “good enough”.

The second is that work on Ankh, while it has never been dormant, has always been something of a fire-and-movement exercise. Every now and then, life and work interferes with Ankh development, and by the time I get back to it, there’s always a feeling of “well, we should at least fix this bug before a release” or “this feature needs to go in before we can call it 0.6”. Halfway into fixing those bugs and implementing those features, life and/or work interferes again, and the cycle starts from the beginning.

I’m starting to think this cycle needs to end.

There is no doubt in my mind that the current snapshots are miles ahead of 0.5.5 both in features and stability. If we took yesterday’s snapshot, renamed the MSI and released it as 0.6, it would be a huge improvement over 0.5.5. And it would definitely shatter the impression that the project is abandoned.

Maybe we should do just that. What do other people think?

Posted in AnkhSVN | 54 Comments

Why Ankh isn’t an SCC provider

A question that comes up occasionally is why AnkhSVN wasn’t implemented using the Microsoft Source Code Control Interface (MSSCCI). This is a de-facto standard today for integrating source control into various IDEs and other application, so it would seem like a natural choice for integrating Subversion into the Visual Studio IDE. However, we chose to take another approach and implement AnkhSVN as a regular Visual Studio addin instead.

Initial planning on AnkhSVN started in December of 2002. At that time, it was a school project that was supposed to last throughout the spring semester of 2003 at Oslo University College. I had some clear ideas about what I wanted to do for my project: I wanted to make something open source, I wanted to relate it somehow to Subversion and I wanted to implement it using C#/.NET, since those were my interests at the time (and still are). Using MSSCCI conflicted with goals #1 and #3. In 2002, the MSSCCI API was under a strict Non-Disclosure Agreement, meaning we wouldn’t have been able to make something open source if we were to use it. There was also a rumor going around that getting hold of the right person inside Microsoft in order to obtain the documentation for the API and sign the NDA was something of a Kafkaesque quest. Since we didn’t attempt to go down this route, I never did find out whether this was true.

Another problem with MSSCCI is that it is essentially a specification for a number of exported C functions from a DLL. At that time I had dabbled with C#/.NET for a while and wanted to try it out for a major project. A .NET assembly cannot (easily, there are ways…) export regular functions, so implementing an MSSCCI provider using .NET would prove to be rather awkward. The technical problems here aren’t insurmountable, however, I can easily think of some ways in which we could have made Ankh using MSSCCI and still got to write most parts of it in .NET. If there hadn’t been other reasons for not using MSSCCI, this problem alone wouldn’t have caused us to make the choice we did. If MSSCCI had been the best choice, it’s also quite possible that we would have implemented the whole thing in C++ instead.

MSSCCI, unfortunately, has one more major problem: it’s not a very good API. It is more or less designed around the needs of Visual Sourcesafe, and source control systems that don’t subscribe to the same ideology as VSS plain don’t fit very well into it. Eric Sink (founder of Sourcegear, which makes the Vault source control system) , in his excellent series Source Control HOWTO, writes about MSSCCI’s deficiencies in Chapter 9: Source Control Integration with IDEs. Which is good, because then I don’t have to go on about it here.

We already knew about most of the criticisms leveraged at MSSCCI back when we were planning the project, so it proved to be the final and decisive argument against using it for Ankh.

Posted in AnkhSVN | 100 Comments