Welcome to Atalasoft Community Sign in | Help

Custom functions in NAnt

It's been a while, and my handy new environment script is working well with most of our current builds (there are over 100 CCNet projects currently).

Today I'm going to touch briefly on something that helped me, and was really easy to setup.  First, some background, as usual.

At Atalasoft we have a lot of our functionality in DotImage split into different libraries.  Those libraries build independently of one-another, but have very similar setups.  Up until a few weeks ago, they each had their own NAnt build script, which contained some form or another of the build environment script I wrote about previously.  I decided it was not only possible, but necessary for our sanity, to pull the builds of all of those libraries into one, coherent build file and utilize the environment build script I wrote.  Finally, I needed to make it so our new library binaries are checked into source control, and merged over to the main DotImage project, alieviating the hassle and potential forgetfulness of engineers (I'm guilty of forgetting once, myself).

If you find yourself in this situation, a number of builds and one build script that will _do_it_all_ for you, you may run into a problem like I did.  I put the Library.build script in a central location (C:\build\tools) next to where everything is built (C:\build\<project>).  Once I did that, all the relative paths in the Library.build were to that of its location, not the working directory!  This was surprising, but also something I had a bit of trouble getting around.  In the build script I needed to know the current working directory, not the directory of the script.  If anyone out there has any better ideas, let me know.

First, it's easy to find your working directory in C#:

System.IO.Directory.GetCurrentDirectory();

and it's easy to call C# from within NAnt (check out the script task)

      <script language="C#" prefix="test" >
<code>
<![CDATA[
[Function("test-func")]
public static string Testfunc( ) {
return "some result !!!!!!!!";
}
]]>
</code>
</script>
<echo message='${test::test-func()}'/>

 As you can see, you define the function, and give it a simple attribute to locate it with.  Anywhere after that script block is defined you can call that test::test-func() in your NAnt build script.  The only downside to this method is the overhead.  Each time NAnt uses this build script it'll generate a class with your function, and then compile that to an assembly, load it up and call that function!  Like I said, we have over 100 projects and a good chunk are libraries using this!  It's better if you can incorporate this into your custom task/type dll, as described in one of mine or Jake's previous blog posts.

I've personally created a new project for our company custom tasks and types and followed the NAnt convention of foldering it out.  I have

Atalasoft.NAnt

->Functions

-->Functions.cs

->Tasks

--> FileRegex.cs, RemoveTFSBindings.cs, etc.

->Types

--> AtalaRegex.cs, AtalaRegexCollection.cs, etc.

So you can see, there's a Functions.cs file in a Functions folder.  Perhaps later if we have more custom functions we'll end up with more files, for now there's just the one.

Now, just add the GetCurrentDirectory() function into the Functions.cs file

using System;
using System.IO;
using NAnt.Core;
using NAnt.Core.Attributes;

namespace Atalasoft.NAnt.Functions
{
    [FunctionSet("Atala", "Atala")]
    class Functions : FunctionSetBase
    {
        public Functions(Project project, PropertyDictionary properties) : base(project, properties) {
        }

        [Function("GetCWD")]
        public static String GetCWD()
        {
            return Directory.GetCurrentDirectory();
        }
    }
}

(Notice the attributes; these are what we'll use in the build script to access this function).

Build your DLL as before and place it next to the NAnt.exe and when NAnt runs the next time, your new function will be available to use without the compiling overhead.

I use it for my library build script like this:

<exec program="devenv.com" commandline="&quot;${Atala::GetCWD()}\${Dir.Source}\${sln.filename}&quot; /UseEnv /build ${Build.Config}        /out &quot;${Atala::GetCWD()}\buildRelease.log&quot;" verbose="true" />

Now, all our libraries, no matter where they are, can use this build script, and all I have to do in CCNet is fill in the blanks depending on the particular library.

The side effect of having one build script for all projects is you have the ease of editing one file.  Add a new CCNet project that checks source control for changes in your build script and all your build servers will be updated when it changes!  The other side effect is if you make a small change to the Library script for one library, it has to work with all of them.

 

Now, if only with my 100 projects I had an easy interface with which to manage them all!

Published Wednesday, June 25, 2008 9:16 AM by dterrell

Comments

Friday, June 27, 2008 12:28 AM by DotNetKicks.com

# Custom functions in NAnt

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Anonymous comments are disabled