Introduction
Do you automate your build process? I didn't, until found that one of my ASP.NET projects required too much
handwork on every release: build, copy production files, update config, etc.
Ever heard of makefiles? Me too. Endless text files stuffed with ciphered spells, dashes and dots that do all magic
for your build process. Forget about this — now we have NAnt.
Born in Java world and brought to .NET, NAnt let us build our programs with easy using simple human-readable
XML build scripts.
Warming Up
After spending several days on learning NAnt's documentation, I got the following build script for my project:
="1.0"
<project name="MyProject" default="production" basedir="..">
<description>MyProject NANT build script</description>
<target name="build"
description="Compile MyProject using Release configuration">
<solution solutionfile="MyProject.sln" configuration="Release">
<webmap>
<map url="http://localhost/MyProject/Web.csproj"
path="Web\Web.csproj" />
<map url="http://localhost/AchService/AchService.csproj"
path="AchService\AchService.csproj" />
</webmap>
<excludeprojects>
<includes name="Tests\Tests.csproj"/>
</excludeprojects>
</solution>
</target>
<target name="produce" description="Copy production files">
-->
<delete dir="Build\Production" failonerror="false" />
<copy todir="Build\Production">
<fileset basedir="Web">
<includes name="**.aspx" />
<includes name="**.ascx" />
<includes name="**.config" />
<includes name="**.gif" />
<includes name="**.jpg" />
<includes name="**.mdb" />
</fileset>
</copy>
-->
<attrib normal="true">
<fileset basedir="Build\Production">
<includes name="**" />
</fileset>
</attrib>
-->
<xmlpoke file="Build\Production\Web.config"
xpath="/configuration/MyProjectConfiguration/add
[@key='ConnectionString']/@value"
value="Provider=Microsoft.Jet.OLEDB.4.0;
Data Source=D:\inetpub\wwwroot\Dev.MyProject.com\db\MyProject.mdb;
User ID=Admin; Password=" />
-->
<xmlpoke file="Build\Production\Web.config"
xpath="/configuration/MyProjectMailerConfiguration/add
[@key='TemplatesFolder']/@value"
value="D:\inetpub\wwwroot\Dev.MyProject.com\EmailTemplates" />
-->
<xmlpoke file="Build\Production\Web.config"
xpath="/configuration/MyProjectConfiguration/add
[@key='ReportsTemplatesFolder']/@value"
value="D:\inetpub\wwwroot\Dev.MyProject.com\
landlord\Reports\Templates" />
</target>
<target name="production" depends="build, produce"
description="Build MyProject and copy production files">
-->
</target>
</project>
The script builds the project, copies it to production folder and changes some key values in *.config files.
I was happy enough, but the project was growing and UI design was changing. I noticed that my script doesn't perform
as expected. The problem was in the 'copy' part of the script. Take a look: to build the project, it's enough to
specify solution file and map web projects to local path — the rest NAnt does itself. But when I copy
cooked files, I have to specify each possible type in copy routine.
...
<solution solutionfile="MyProject.sln" configuration="Release">
<webmap>
<map url="http://localhost/MyProject/Web.csproj"
path="Web\Web.csproj" />
<map url="http://localhost/AchService/AchService.csproj"
path="AchService\AchService.csproj" />
</webmap>
<excludeprojects>
<includes name="Tests\Tests.csproj"/>
</excludeprojects>
</solution>
...
<copy todir="Build\Production">
<fileset basedir="Web">
<includes name="**.aspx" />
<includes name="**.ascx" />
<includes name="**.config" />
<includes name="**.gif" />
<includes name="**.jpg" />
<includes name="**.mdb" />
</fileset>
</copy>
...
Guess what happens when my designer adds some Flash animation to the UI?
Do It Yourself
Fortunately, NAnt was designed with extensibility in mind. And the solution is simple — develop your own task
for the problem you faced.
NAnt's task is simply a descendant of NAnt.Core.Task
class with overridden ExecuteTask
method. The only trick is to name your assembly properly: the name should look like YourAssemblyNameTasks.dll.
The assembly should be placed to %nant%\bin folder. The rest will be done by NAnt using .NET reflection magic.
I developed extensions for various software in the past, but seems task development for the NAnt was the easiest.
Effortless Copy
After spending couple of days on spare time coding, I got the task that makes 'production' copy of any ASP.NET
project. Now my script looks like this:
="1.0"
<project name="MyProject" default="production" basedir="..">
<description>MyProject NANT build script</description>
<target name="build"
description="Compile MyProject using Release configuration">
<solution solutionfile="MyProject.sln" configuration="Release">
<webmap>
<map url="http://localhost/MyProject/Web.csproj"
path="Web\Web.csproj" />
<map url="http://localhost/AchService/AchService.csproj"
path="AchService\AchService.csproj" />
</webmap>
<excludeprojects>
<includes name="Tests\Tests.csproj"/>
</excludeprojects>
</solution>
</target>
<target name="produce" description="Copy production files">
-->
<delete dir="Build\Production" failonerror="false" />
<copywebproject project="Web\Web.csproj"
todir="Build\Production" configuration="Release" />
<copywebproject project="AchService\AchService.csproj"
todir="Build\Production\AchService" configuration="Release" />
-->
<attrib normal="true">
<fileset basedir="Build\Production">
<includes name="**" />
</fileset>
</attrib>
-->
<xmlpoke file="Build\Production\Web.config"
xpath="/configuration/MyProjectConfiguration/add
[@key='ConnectionString']/@value"
value="Provider=Microsoft.Jet.OLEDB.4.0;
Data Source=D:\inetpub\wwwroot\Dev.MyProject.com\db\MyProject.mdb;
User ID=Admin; Password=" />
-->
<xmlpoke file="Build\Production\Web.config"
xpath="/configuration/MyProjectMailerConfiguration/add
[@key='TemplatesFolder']/@value"
value="D:\inetpub\wwwroot\Dev.MyProject.com\EmailTemplates" />
-->
<xmlpoke file="Build\Production\Web.config"
xpath="/configuration/MyProjectConfiguration/add
[@key='ReportsTemplatesFolder']/@value"
value="D:\inetpub\wwwroot\Dev.MyProject.com\
landlord\Reports\Templates" />
</target>
<target name="production" depends="build, produce"
description="Build MyProject and copy production files">
-->
</target>
</project>
The only thing you have to care of now, is to have all production files included in the project. You can do this
right-clicking on the file name in Solution Explorer and selecting 'Include in Project' command.
<copywebproject> Syntax
<copywebproject project="PATH TO PROJECT" todir="TARGET PATH"
configuration="CONFIGURATION OF INTEREST" />
PATH TO PROJECT
is an absolute or relative path to the web project you want to copy.
TARGET PATH
is an absolute or relative path to the target directory.
The task uses CONFIGURATION OF INTEREST
attribute to obtain path to compiled project files.
Note that <copywebproject> task supports only web project written in C#, VB.NET or any compatible .NET language.
History