At work we are currently converting from NAnt to MSBuild scripts as we convert from CruiseControl.NET to TeamCity. Why are we changing from NAnt to MSBuild as we change CI servers? Nobody really knows for sure besides the guy that is running the project basically says, "I've mandated it." That's the only reason. Seems like a silly reason to be to go through all the trouble to rewrite all the scripts.
After working with NAnt and MSBuild I have come to one conclusion: I don't like XML based scripting languages. That's right, you heard it here. They are both a pain. Way too verbose and too much of a PITA. My personal belief is that we should just wrap all the functionality with a perl script and be done with it. :-) Just think about it: Simple if/then/else statements and simple loops without all the XML filler junque and all the dynamic data structures you want without a bunch more XML filler stuff. It's beautiful! Back in my day, you darn whipper snappers, we had .CSV files and liked 'em! And like Ant/NAnt, perl works under both Unix and Win32 (I've been using Win32 for a gazillion years but now that I run a 64-bit OS should I start saying Win32/64? That's a discussion for another time! Now back to my MSBuild and NAnt rant!) so you have multiple platform functionality.
MSBuild is taking me longer to wrap my head around. I don't like that I can create a property inside of a target, but that property is not available until the target where it was created as finished. Makes it a pain to attempt to modularize common calls for database work. And why doesn't MSBuild have a foreach construct? Sure, you can use the metabase accessing `%` to invoke multiple calls but how non-intuitive is that? Argh! I shouldn't have to write a helper function to make multiple calls with the % operator.
Yeah. I stand by my convictions on this one. Junque both of 'em and write a perl wrapper and be happy.
Showing posts with label NAnt. Show all posts
Showing posts with label NAnt. Show all posts
Saturday, October 11, 2008
Tuesday, September 2, 2008
Using NAnt custom task to wrap SDC Task to create VDir
I got my VDir code working, but there are some caveats that have to be watched, I've found.
A caveat that I've found though is that even though the invoked SDC Task might run into a problem during the .Execute call, .Execute still returns a true so my NAnt task doesn't know if the process has ran into a problem.
In the example below, I'm trying to create a VDir with an AppPool that doesn't exist:
8<-------------------------------------------
NAnt 0.85 (Build 0.85.2478.0; release; 10/14/2006)
Copyright (C) 2001-2006 Gerry Shaw
http://nant.sourceforge.net
Buildfile: file:///C:/customtask/default.build
Target framework: Microsoft .NET Framework 2.0
Target(s) specified: test-task
[loadtasks] Scanning assembly "Auswipe.NAntTasks.VDirTasks" for extensions.
[loadtasks] Scanning assembly "Auswipe.NAntTasks.AppPoolTasks" for extensions.
test-task:
[echo] Creating VDir
Auswipe.NAntTasks.VDirTasks : The following VDir will be created: 'someVDir'
Creating virtual directory "someVDir".
A task error has occured.
Message = Exception has been thrown by the target of an invocation.
VirtualDirectoryName = someVDir
Path = c:\somewebsite
MachineName = Auswipel-rfs2we
WebSiteName = Default Web Site
WebAppName =
AppPoolId = AppPoolDoesNotExist
AppCreate = True
AnonymousUserName =
AnonymousUserPassword =
UncUserName =
UncPassword =
AuthFlags =
AccessFlags =
at System.DirectoryServices.DirectoryEntry.Invoke(String methodName, Object[] args)
at Microsoft.Sdc.Tasks.Configuration.Web.VirtualDirectory.AppCreate3() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\Configuration\Web\VirtualDirectory.cs:line 324
at Microsoft.Sdc.Tasks.Web.WebSite.CreateVirtualDirectory.InternalExecute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\Web\WebSite\CreateVirtualDirectory.cs:line 315
at Microsoft.Sdc.Tasks.TaskBase.Execute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\TaskBase.cs:line 66
Element not found. (Exception from HRESULT: 0x80070490)
PseudoEngineException
at Microsoft.Sdc.Tasks.PseudoBuildEngine.LogErrorEvent(BuildErrorEventArgs eventArgs) in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\PseudoBuildEngine.cs:line 77
at Microsoft.Build.Utilities.TaskLoggingHelper.LogError(String subcategory, String errorCode, String helpKeyword, String file, Int32 lineNumber, Int32 columnNumber, Int32 endLineNumber, Int32 endColumnNumber, String message, Object[] messageArgs)
at Microsoft.Build.Utilities.TaskLoggingHelper.LogError(String message, Object[] messageArgs)
at Microsoft.Sdc.Tasks.TaskBase.Execute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\TaskBase.cs:line 95
Auswipe.NAntTasks.VDirTasks : VDir creation failed with the resulting exception:
System.ApplicationException: PseudoEngineException
at Microsoft.Sdc.Tasks.PseudoBuildEngine.LogErrorEvent(BuildErrorEventArgs eventArgs) in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\PseudoBuildEngine.cs:line 77
at Microsoft.Build.Utilities.TaskLoggingHelper.LogError(String subcategory, String errorCode, String helpKeyword, String file, Int32 lineNumber, Int32 columnNumber, Int32 endLineNumber, Int32 endColumnNumber, String message, Object[] messageArgs)
at Microsoft.Build.Utilities.TaskLoggingHelper.LogErrorFromException(Exception exception, Boolean showStackTrace, Boolean showDetail, String file)
at Microsoft.Build.Utilities.TaskLoggingHelper.LogErrorFromException(Exception exception, Boolean showStackTrace)
at Microsoft.Sdc.Tasks.TaskBase.Execute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\TaskBase.cs:line 99
at Auswipe.NAntTasks.VDirTasks.CreateVDir.ExecuteTask()
BUILD SUCCEEDED - 2 non-fatal error(s), 0 warning(s)
Total time: 0.2 seconds.
------------------------------------------->8
You would think that .Execute should return a False for the call but it doesn't. Notice the verbage of "2 non-fatal error(s)" so it appears that the SDC Task does not treat the non-existant Applicaiton Pool as a fatal error. Good to know!
Going back and looking at the IIS Manager we see this for the create VDir:

Notice how the Application Pool is set to "<Invalid Application Pool>."
So, the VDir was created, despite the lack of a valid Application Pool. That's something to be aware of when testing out NAnt tasks that wrap SDC Task functionality.
1: using System;
2: using System.Text;
3: using NAnt.Core;
4: using NAnt.Core.Attributes;
5: using Microsoft.Sdc.Tasks;
6: using Microsoft.Build.Utilities;
7: using Microsoft.Build.Framework;
8: namespace Auswipe.NAntTasks.VDirTasks {
9: # region Delete VDir10: [TaskName("deletevdir")]
11: public class DeleteVDir : NAnt.Core.Task {
12: private string vDirToDelete;
13: private string machineName;
14: private string webSiteName;
15: [TaskAttribute("vdirtodelete", Required = true)]
16: [StringValidator(AllowEmpty = false)]
17: public string VDirToDelete {
18: get { return vDirToDelete; }
19: set { vDirToDelete = value; }
20: }21: [TaskAttribute("machinename", Required = true)]
22: [StringValidator(AllowEmpty = false)]
23: public string MachineName {
24: get { return machineName; }
25: set { machineName = value; }
26: }27: [TaskAttribute("websitename", Required = true)]
28: [StringValidator(AllowEmpty = false)]
29: public string WebSiteName {
30: get { return webSiteName; }
31: set { webSiteName = value; }
32: }33: protected override void ExecuteTask() {
34: Project.Log(Level.Info, "Auswipe.NAntTasks.VDirTasks : The following AppPool will be deleted: '" + vDirToDelete + "'");
35: try {
36: Microsoft.Sdc.Tasks.Web.WebSite.DeleteVirtualDirectory delVDirObject = new Microsoft.Sdc.Tasks.Web.WebSite.DeleteVirtualDirectory();
37: delVDirObject.MachineName = machineName; 38: delVDirObject.VirtualDirectoryName = vDirToDelete; 39: delVDirObject.WebSiteName = webSiteName;40: if (delVDirObject.Execute()) {
41: Project.Log(Level.Info, "Auswipe.NAntTasks.VDirTasks : The VDir '" + vDirToDelete + "' was successful deleted.");
42: } else {
43: Project.Log(Level.Error, "Auswipe.NAntTasks.VDirTasks : ERROR: The VDir '" + vDirToDelete + "' was NOT deleted but an exception was not thrown.");
44: };45: } catch (Exception e) {
46: Project.Log(Level.Error, "Auswipe.NAntTasks.VDirTasks : VDir deletion failed with the resulting exception:");
47: Project.Log(Level.Error, e.ToString()); 48: }; 49: } 50: }51: #endregion
52: # region Create VDir53: [TaskName("createvdir")]
54: public class CreateVDir : NAnt.Core.Task {
55: private string vDirName;
56: private string webSiteName;
57: private string appPoolName;
58: private string machineName;
59: private string fullPath;
60: [TaskAttribute("apppoolname", Required = true)]
61: [StringValidator(AllowEmpty = false)]
62: public string AppPoolName {
63: get { return appPoolName; }
64: set { appPoolName = value; }
65: }66: [TaskAttribute("vdirname", Required = true)]
67: [StringValidator(AllowEmpty = false)]
68: public string VDirName {
69: get { return vDirName; }
70: set { vDirName = value; }
71: }72: [TaskAttribute("website", Required = true)]
73: [StringValidator(AllowEmpty = false)]
74: public string WebSite {
75: get { return webSiteName; }
76: set { webSiteName = value; }
77: }78: [TaskAttribute("machinename", Required = true)]
79: [StringValidator(AllowEmpty = false)]
80: public string MachineName {
81: get { return machineName; }
82: set { machineName = value; }
83: }84: [TaskAttribute("path", Required = true)]
85: [StringValidator(AllowEmpty = false)]
86: public string FullPath {
87: get { return fullPath; }
88: set { fullPath = value; }
89: }90: protected override void ExecuteTask() {
91: Project.Log(Level.Info, "Auswipe.NAntTasks.VDirTasks : The following VDir will be created: '" + vDirName + "'");
92: try {
93: Microsoft.Sdc.Tasks.Web.WebSite.CreateVirtualDirectory createVDirObject = new Microsoft.Sdc.Tasks.Web.WebSite.CreateVirtualDirectory();
94: createVDirObject.AppCreate = true;
95: createVDirObject.AppPoolId = appPoolName; 96: createVDirObject.MachineName = machineName; 97: createVDirObject.VirtualDirectoryName = vDirName; 98: createVDirObject.WebSiteName = webSiteName; 99: createVDirObject.Path = fullPath;100: if (createVDirObject.Execute()) {
101: Project.Log(Level.Info, "Auswipe.NAntTasks.VDirTasks : The VDir '" + vDirName + "' was successfuly created.");
102: } else {
103: Project.Log(Level.Error, "Auswipe.NAntTasks.VDirTasks : ERROR: The VDir '" + vDirName + "' was NOT created but an exception was not thrown.");
104: };105: } catch (Exception e) {
106: Project.Log(Level.Error, "Auswipe.NAntTasks.VDirTasks : VDir creation failed with the resulting exception:");
107: Project.Log(Level.Error, e.ToString()); 108: }; 109: } 110: }111: #endregion
112: }A caveat that I've found though is that even though the invoked SDC Task might run into a problem during the .Execute call, .Execute still returns a true so my NAnt task doesn't know if the process has ran into a problem.
In the example below, I'm trying to create a VDir with an AppPool that doesn't exist:
8<-------------------------------------------
NAnt 0.85 (Build 0.85.2478.0; release; 10/14/2006)
Copyright (C) 2001-2006 Gerry Shaw
http://nant.sourceforge.net
Buildfile: file:///C:/customtask/default.build
Target framework: Microsoft .NET Framework 2.0
Target(s) specified: test-task
[loadtasks] Scanning assembly "Auswipe.NAntTasks.VDirTasks" for extensions.
[loadtasks] Scanning assembly "Auswipe.NAntTasks.AppPoolTasks" for extensions.
test-task:
[echo] Creating VDir
Auswipe.NAntTasks.VDirTasks : The following VDir will be created: 'someVDir'
Creating virtual directory "someVDir".
A task error has occured.
Message = Exception has been thrown by the target of an invocation.
VirtualDirectoryName = someVDir
Path = c:\somewebsite
MachineName = Auswipel-rfs2we
WebSiteName = Default Web Site
WebAppName =
AppPoolId = AppPoolDoesNotExist
AppCreate = True
AnonymousUserName =
AnonymousUserPassword =
UncUserName =
UncPassword =
AuthFlags =
AccessFlags =
at System.DirectoryServices.DirectoryEntry.Invoke(String methodName, Object[] args)
at Microsoft.Sdc.Tasks.Configuration.Web.VirtualDirectory.AppCreate3() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\Configuration\Web\VirtualDirectory.cs:line 324
at Microsoft.Sdc.Tasks.Web.WebSite.CreateVirtualDirectory.InternalExecute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\Web\WebSite\CreateVirtualDirectory.cs:line 315
at Microsoft.Sdc.Tasks.TaskBase.Execute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\TaskBase.cs:line 66
Element not found. (Exception from HRESULT: 0x80070490)
PseudoEngineException
at Microsoft.Sdc.Tasks.PseudoBuildEngine.LogErrorEvent(BuildErrorEventArgs eventArgs) in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\PseudoBuildEngine.cs:line 77
at Microsoft.Build.Utilities.TaskLoggingHelper.LogError(String subcategory, String errorCode, String helpKeyword, String file, Int32 lineNumber, Int32 columnNumber, Int32 endLineNumber, Int32 endColumnNumber, String message, Object[] messageArgs)
at Microsoft.Build.Utilities.TaskLoggingHelper.LogError(String message, Object[] messageArgs)
at Microsoft.Sdc.Tasks.TaskBase.Execute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\TaskBase.cs:line 95
Auswipe.NAntTasks.VDirTasks : VDir creation failed with the resulting exception:
System.ApplicationException: PseudoEngineException
at Microsoft.Sdc.Tasks.PseudoBuildEngine.LogErrorEvent(BuildErrorEventArgs eventArgs) in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\PseudoBuildEngine.cs:line 77
at Microsoft.Build.Utilities.TaskLoggingHelper.LogError(String subcategory, String errorCode, String helpKeyword, String file, Int32 lineNumber, Int32 columnNumber, Int32 endLineNumber, Int32 endColumnNumber, String message, Object[] messageArgs)
at Microsoft.Build.Utilities.TaskLoggingHelper.LogErrorFromException(Exception exception, Boolean showStackTrace, Boolean showDetail, String file)
at Microsoft.Build.Utilities.TaskLoggingHelper.LogErrorFromException(Exception exception, Boolean showStackTrace)
at Microsoft.Sdc.Tasks.TaskBase.Execute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\TaskBase.cs:line 99
at Auswipe.NAntTasks.VDirTasks.CreateVDir.ExecuteTask()
BUILD SUCCEEDED - 2 non-fatal error(s), 0 warning(s)
Total time: 0.2 seconds.
------------------------------------------->8
You would think that .Execute should return a False for the call but it doesn't. Notice the verbage of "2 non-fatal error(s)" so it appears that the SDC Task does not treat the non-existant Applicaiton Pool as a fatal error. Good to know!
Going back and looking at the IIS Manager we see this for the create VDir:

Notice how the Application Pool is set to "<Invalid Application Pool>."
So, the VDir was created, despite the lack of a valid Application Pool. That's something to be aware of when testing out NAnt tasks that wrap SDC Task functionality.
Sunday, August 31, 2008
Hey, whaddya know? It works!
I finished the first part of my task to create a custom NAnt task that utilizes the SDC Tasks that were originally built for MSBuild. What I am interested in is deleting and creating custom AppPools for IIS as well as deleting and creating vdirs bound to custom AppPools and not DefaultAppPool.
I did some quick and dirty coding today and came up with this first pass:
After I created my Auswipe.NAntTasks.AppPoolTasks.dll I had to copy both Auswipe.NAntTasks.AppPoolTasks.dll and Microsoft.Sdc.Tasks.dll to the subdir where I put my default.build that I've been playing with so that the code could properly execute and create my AppPool.
Here is my default.build script for NAnt:
8<-----------------------------------------
----------------------------------------->8
And here is the output from the execution:
8<-----------------------------------------
----------------------------------------->8
Here we see a screen shot of the system before NAnt execution:

Now I execute the NAnt script and re-fresh the IIS manager view of AppPools and lo-and-behold! It worked!

I checked the properties of the newly created AppPool and we see that it has the credentials I specified in the default.build. Hazzah!

Now the next step is to go forth and code the deletion and creation of vdirs with the SDC Tasks.
Nifty!
I did some quick and dirty coding today and came up with this first pass:
1: using System;
2: using System.Text;
3: using NAnt.Core;
4: using NAnt.Core.Attributes;
5: using Microsoft.Sdc.Tasks;
6: using Microsoft.Build.Utilities;
7: using Microsoft.Build.Framework;
8: namespace Auswipe.NAntTasks.AppPoolTasks {
9: # region DeleteAppPool10: [TaskName("deleteapppool")]
11: public class DeleteAppPool : NAnt.Core.Task {
12: private string appPoolName;
13: private string machineName;
14: [TaskAttribute("apppoolname", Required = true)]
15: [StringValidator(AllowEmpty = false)]
16: public string AppPoolName {
17: get { return appPoolName; }
18: set { appPoolName = value; }
19: }20: [TaskAttribute("machinename", Required = true)]
21: [StringValidator(AllowEmpty = false)]
22: public string MachineName {
23: get { return machineName; }
24: set { machineName = value; }
25: }26: protected override void ExecuteTask() {
27: Project.Log(Level.Info, "Auswipe.NAntTasks.AppPoolTasks : The following AppPool will be deleted: '" + appPoolName + "'");
28: try {
29: Microsoft.Sdc.Tasks.Web.DeleteAppPool.DeleteAppPool delAppPoolObject = new Microsoft.Sdc.Tasks.Web.DeleteAppPool.DeleteAppPool();
30: delAppPoolObject.AppPoolName = appPoolName; 31: delAppPoolObject.MachineName = machineName;32: if (delAppPoolObject.Execute()) {
33: Project.Log(Level.Info, "Auswipe.NAntTasks.AppPoolTasks : The AppPool '" + appPoolName + "' was successful deleted.");
34: } else {
35: Project.Log(Level.Error, "Auswipe.NAntTasks.AppPoolTasks : ERROR: The AppPool '" + appPoolName + "' was NOT deleted but an exception was not thrown.");
36: };37: } catch (Exception e) {
38: Project.Log(Level.Error, "Auswipe.NAntTasks.AppPoolTasks : AppPool deletion failed with the resulting exception:");
39: Project.Log(Level.Error, e.ToString()); 40: }; 41: } 42: }43: #endregion
44: # region Create AppPool45: [TaskName("createapppool")]
46: public class CreateAppPool : NAnt.Core.Task {
47: private string appPoolName;
48: private string userName;
49: private string passWord;
50: private string userDomain;
51: private string machineName;
52: [TaskAttribute("apppoolname", Required = true)]
53: [StringValidator(AllowEmpty = false)]
54: public string AppPoolName {
55: get { return appPoolName; }
56: set { appPoolName = value; }
57: }58: [TaskAttribute("username", Required = true)]
59: [StringValidator(AllowEmpty = false)]
60: public string UserName {
61: get { return userName; }
62: set { userName = value; }
63: }64: [TaskAttribute("password", Required = true)]
65: [StringValidator(AllowEmpty = false)]
66: public string PassWord {
67: get { return passWord; }
68: set { passWord = value; }
69: }70: [TaskAttribute("machinename", Required = true)]
71: [StringValidator(AllowEmpty = false)]
72: public string MachineName {
73: get { return machineName; }
74: set { machineName = value; }
75: }76: protected override void ExecuteTask() {
77: Project.Log(Level.Info, "Auswipe.NAntTasks.AppPoolTasks : The following AppPool will be created: '" + appPoolName + "'");
78: try {
79: Microsoft.Sdc.Tasks.Web.AppPool.Create createAppPoolObject = new Microsoft.Sdc.Tasks.Web.AppPool.Create();
80: createAppPoolObject.AppPoolName = appPoolName;81: createAppPoolObject.IdentityType = "SpecifiedUserAccount";
82: createAppPoolObject.Identity = userName; 83: createAppPoolObject.Password = passWord; 84: createAppPoolObject.MachineName = machineName;85: if (createAppPoolObject.Execute()) {
86: Project.Log(Level.Info, "Auswipe.NAntTasks.AppPoolTasks : The AppPool '" + appPoolName + "' was successfuly created.");
87: } else {
88: Project.Log(Level.Error, "Auswipe.NAntTasks.AppPoolTasks : ERROR: The AppPool '" + appPoolName + "' was NOT created but an exception was not thrown.");
89: };90: } catch (Exception e) {
91: Project.Log(Level.Error, "Auswipe.NAntTasks.AppPoolTasks : AppPool creation failed with the resulting exception:");
92: Project.Log(Level.Error, e.ToString()); 93: }; 94: } 95: }96: #endregion
97: }After I created my Auswipe.NAntTasks.AppPoolTasks.dll I had to copy both Auswipe.NAntTasks.AppPoolTasks.dll and Microsoft.Sdc.Tasks.dll to the subdir where I put my default.build that I've been playing with so that the code could properly execute and create my AppPool.
Here is my default.build script for NAnt:
8<-----------------------------------------
1: 2: <?xml version="1.0"?>
3: <project name="Task Test" default="test-task" basedir="." xmlns="http://nant.sf.net/release/0.86-beta1/nant.xsd">
4: 5: <loadtasks assembly="Auswipe.NAntTasks.AppPoolTasks.dll" />
6: 7: <target name="test-task">
8: <echo message="Deleting AppPool."/>
9: <deleteapppool apppoolname = "AuswipeAppPool"
10: machinename = "Auswipel-rfs2we"
11: />
12: 13: <echo message="Create AppPool."/>
14: <createapppool apppoolname = "AuswipeAppPool"
15: username = "AuswipeAppPoolUser"
16: password = "supersekrit"
17: machinename = "Auswipel-rfs2we"
18: />
19: </target>
20: 21: </project>
----------------------------------------->8
And here is the output from the execution:
8<-----------------------------------------
1: C:\customtask>nant /f:default.build test-task
2: NAnt 0.85 (Build 0.85.2478.0; release; 10/14/2006) 3: Copyright (C) 2001-2006 Gerry Shaw 4: http://nant.sourceforge.net 5: 6: Buildfile: file:///C:/customtask/default.build 7: Target framework: Microsoft .NET Framework 2.0 8: Target(s) specified: test-task 9: 10: [loadtasks] Scanning assembly "Auswipe.NAntTasks.AppPoolTasks" for extensions. 11: 12: test-task: 13: 14: [echo] Deleting AppPool. 15: Auswipe.NAntTasks.AppPoolTasks : The following AppPool will be deleted: 'AuswipeAppPool' 16: Auswipe.NAntTasks.AppPoolTasks : The AppPool 'AuswipeAppPool' was successful deleted. 17: [echo] Create AppPool. 18: Auswipe.NAntTasks.AppPoolTasks : The following AppPool will be created: 'AuswipeAppPool' 19: Creating app pool "AuswipeAppPool". 20: Auswipe.NAntTasks.AppPoolTasks : The AppPool 'AuswipeAppPool' was successfuly created. 21: 22: BUILD SUCCEEDED 23: 24: Total time: 0.1 seconds.----------------------------------------->8
Here we see a screen shot of the system before NAnt execution:
Now I execute the NAnt script and re-fresh the IIS manager view of AppPools and lo-and-behold! It worked!
I checked the properties of the newly created AppPool and we see that it has the credentials I specified in the default.build. Hazzah!
Now the next step is to go forth and code the deletion and creation of vdirs with the SDC Tasks.
Nifty!
Friday, August 29, 2008
Creating my own custom NAnt tasks in C#
One of the things that I find myself doing when not crunching numbers as a performance enginerd is learning the ways of the Buildmiester. We use NAnt for deploying our code from the build server to a target test machine and UAT test environment.
During the process we use NAnt to delete and create vdirs under IIS with mkiisdir which is handy dandy and all but it doesn't fully get the job done as some of the vdirs we need should not belong to DefaultAppPool. And also unfortunately it appears that NAntContrib doesn't have all the tools that I need to delete and create a custom AppPool that I need to create with a special local username/password.
I found the command line code to create the AppPool that I want and I was just going to wrap the execution of the command line stuff with an <exec> task but I still couldn't find how to create a vdir on the command line and have it binding to my custom AppPool that I wanted.
This morning after Scrum another coder working on a sister project that uses MSBuild instead of NAnt suggested that I take a gander at the SDC Tasks for MSBuild up on CodePlex. After pulling down the latest and greatest SDC Tasks it appears that I can delete, then create my custom AppPool and then delete/create a customer vdir bound to my custom AppPool. How great is that?
Except that SDC Tasks was designed for MSBuild and we are already heavily into NAnt. Bummer, right? Well. I know that NAnt has a <msbuild> task but going from NAnt into MSBuild that would reference a custom MSBuild script and pass the params around seemed like a real PITA, not to mention a documentation nightmare. But then again, I really want this functionality of the SDC Tasks.
What do I do? What do I do?
After consulting the Great Google, I decided that it was time to "Man up, Nancy Boy!" as one of the female trainers at my gym would say (actually shout during a middle of a grueling spin class with the grunting and sweating and the pain!) and learn how to code my own custom NAnt task and build a custom wrapper for the SDC stuff so that I could invoke the functionality from within my NAnt script.
After consulting some blogs and some pain and suffering I've been able to create a basic useless NAnt task. I haven't written the wrapper code yet, but I think that'll be simple compared to actually getting a custom NAnt task up and running.
This is what I did to create my very own useless NAnt task.
First, I created a .NET Class project by the name of "NAntCustomAuswipeTasks." It seems to be very important that the assembly name postfix be "Tasks.dll." The second thing is that it is important to use class decorators for specifying various things that NAnt needs to get the job done. Two of those being "TaskName" and "TaskAttribute."
Another important thing that I learned is that when calling the task from inside the NAnt script that the task name is case sensitive to the decorator string.
Below is my code for my useless NAnt task:
Take notice of the TaskName, TaskAttribute and StringValidator decorators. Very important or otherwise your attempts to call your custom NAnt task will fail. The blog entry that I was learning on didn't have the TaskName decorator and so my call to my task was failing. Very frustrating.
After compiling into my assembly of "NAntCustomAuswipeTasks.dll" I copied the DLL into the same subdir where my NAnt build file resides. In my case, this is a folder off of my Desktop subdir where I am playing around checking to see if it really works. I'm using a default.build file right now just to make things simple:
8<--------------------------------
C:\Users\Auswipe\Desktop\NAntTask>dir
Volume in drive C has no label.
Volume Serial Number is A463-3DC5
Directory of C:\Users\Auswipe\Desktop\NAntTask
08/29/2008 11:44 PM <DIR> .
08/29/2008 11:44 PM <DIR> ..
08/29/2008 11:44 PM 404 default.build
08/29/2008 11:45 PM 4,608 NAntCustomAuswipeTasks.dll
2 File(s) 5,012 bytes
2 Dir(s) 332,392,656,896 bytes free
-------------------------------->8
8<--------------------------------
-------------------------------->8
And here I am testing out my useless NAnt task:
8<--------------------------------
C:\Users\auswipe\Desktop\NAntTask>nant /f:default.build test-task
NAnt 0.85 (Build 0.85.2478.0; release; 10/14/2006)
Copyright (C) 2001-2006 Gerry Shaw
http://nant.sourceforge.net
Buildfile: file:///C:/Users/auswipe/Desktop/NAntTask/default.build
Target framework: Microsoft .NET Framework 2.0
Target(s) specified: test-task
[loadtasks] Scanning assembly "NAntCustomAuswipeTasks" for extensions.
test-task:
[echo] Here goes nothing....
[AuwipeTask] The parameter passed is 'Killroy was here.'.
BUILD SUCCEEDED
Total time: 0 seconds.
-------------------------------->8
Notice that I go ahead and specify the default.build file on the command line along with the target despite having the test-task as the defacto target of default.build and knowing full well that NAnt looks for default.build. Why'd I do that? Just because I hate seeing demonstrations of some technology where the author assumes that the readers know all the ins and out of the subject. Sure, I can get the same results with `nant` as I did with `nant /f:default.build test-task` but having been in the RTFM mode before, I can gleam more information from `nant /f:default.build test-task` and learn the rest later. But that is just my own opinion. YMMV, IANAL, ROFLWTFBBQ.
So, now that I've created my useless NAnt task successfully I can work on wrapping the SDC Tasks that I need to make my world (or at least my deploy) a better place. And the less time I'm spending cleaning up after a deploy changing settings is more time I can be sitting on my butt surfing the web, or something like that.
During the process we use NAnt to delete and create vdirs under IIS with mkiisdir which is handy dandy and all but it doesn't fully get the job done as some of the vdirs we need should not belong to DefaultAppPool. And also unfortunately it appears that NAntContrib doesn't have all the tools that I need to delete and create a custom AppPool that I need to create with a special local username/password.
I found the command line code to create the AppPool that I want and I was just going to wrap the execution of the command line stuff with an <exec> task but I still couldn't find how to create a vdir on the command line and have it binding to my custom AppPool that I wanted.
This morning after Scrum another coder working on a sister project that uses MSBuild instead of NAnt suggested that I take a gander at the SDC Tasks for MSBuild up on CodePlex. After pulling down the latest and greatest SDC Tasks it appears that I can delete, then create my custom AppPool and then delete/create a customer vdir bound to my custom AppPool. How great is that?
Except that SDC Tasks was designed for MSBuild and we are already heavily into NAnt. Bummer, right? Well. I know that NAnt has a <msbuild> task but going from NAnt into MSBuild that would reference a custom MSBuild script and pass the params around seemed like a real PITA, not to mention a documentation nightmare. But then again, I really want this functionality of the SDC Tasks.
What do I do? What do I do?
After consulting the Great Google, I decided that it was time to "Man up, Nancy Boy!" as one of the female trainers at my gym would say (actually shout during a middle of a grueling spin class with the grunting and sweating and the pain!) and learn how to code my own custom NAnt task and build a custom wrapper for the SDC stuff so that I could invoke the functionality from within my NAnt script.
After consulting some blogs and some pain and suffering I've been able to create a basic useless NAnt task. I haven't written the wrapper code yet, but I think that'll be simple compared to actually getting a custom NAnt task up and running.
This is what I did to create my very own useless NAnt task.
First, I created a .NET Class project by the name of "NAntCustomAuswipeTasks." It seems to be very important that the assembly name postfix be "Tasks.dll." The second thing is that it is important to use class decorators for specifying various things that NAnt needs to get the job done. Two of those being "TaskName" and "TaskAttribute."
Another important thing that I learned is that when calling the task from inside the NAnt script that the task name is case sensitive to the decorator string.
Below is my code for my useless NAnt task:
1: using System;
2: using NAnt.Core;
3: using NAnt.Core.Attributes;
4: namespace NAntCustomAuswipeTasks {
5: [TaskName("auswipetask")]
6: public class AuswipeTask : Task {
7: private string customData;
8: [TaskAttribute("customdata", Required = true)]
9: [StringValidator(AllowEmpty = false)]
10: public string CustomData {
11: get { 12: return customData;
13: } 14: set { 15: customData = value;
16: } 17: }18: protected override void ExecuteTask() {
19: Project.Log(Level.Info, "[AuwipeTask] The parameter passed is '" + customData + "'.");
20: } 21: } 22: }Take notice of the TaskName, TaskAttribute and StringValidator decorators. Very important or otherwise your attempts to call your custom NAnt task will fail. The blog entry that I was learning on didn't have the TaskName decorator and so my call to my task was failing. Very frustrating.
After compiling into my assembly of "NAntCustomAuswipeTasks.dll" I copied the DLL into the same subdir where my NAnt build file resides. In my case, this is a folder off of my Desktop subdir where I am playing around checking to see if it really works. I'm using a default.build file right now just to make things simple:
8<--------------------------------
C:\Users\Auswipe\Desktop\NAntTask>dir
Volume in drive C has no label.
Volume Serial Number is A463-3DC5
Directory of C:\Users\Auswipe\Desktop\NAntTask
08/29/2008 11:44 PM <DIR> .
08/29/2008 11:44 PM <DIR> ..
08/29/2008 11:44 PM 404 default.build
08/29/2008 11:45 PM 4,608 NAntCustomAuswipeTasks.dll
2 File(s) 5,012 bytes
2 Dir(s) 332,392,656,896 bytes free
-------------------------------->8
8<--------------------------------
1: <?xml version="1.0"?>
2: <project name="Custom NAnt Task Test" default="test-task" basedir="." xmlns="http://nant.sf.net/release/0.86-beta1/nant.xsd">
3: <loadtasks assembly="NAntCustomAuswipeTasks.dll" />
4: <target name="test-task">
5: <echo message="Here goes nothing...." />
6: <auswipetask customdata="Killroy was here." />
7: </target>
8: </project>
-------------------------------->8
And here I am testing out my useless NAnt task:
8<--------------------------------
C:\Users\auswipe\Desktop\NAntTask>nant /f:default.build test-task
NAnt 0.85 (Build 0.85.2478.0; release; 10/14/2006)
Copyright (C) 2001-2006 Gerry Shaw
http://nant.sourceforge.net
Buildfile: file:///C:/Users/auswipe/Desktop/NAntTask/default.build
Target framework: Microsoft .NET Framework 2.0
Target(s) specified: test-task
[loadtasks] Scanning assembly "NAntCustomAuswipeTasks" for extensions.
test-task:
[echo] Here goes nothing....
[AuwipeTask] The parameter passed is 'Killroy was here.'.
BUILD SUCCEEDED
Total time: 0 seconds.
-------------------------------->8
Notice that I go ahead and specify the default.build file on the command line along with the target despite having the test-task as the defacto target of default.build and knowing full well that NAnt looks for default.build. Why'd I do that? Just because I hate seeing demonstrations of some technology where the author assumes that the readers know all the ins and out of the subject. Sure, I can get the same results with `nant` as I did with `nant /f:default.build test-task` but having been in the RTFM mode before, I can gleam more information from `nant /f:default.build test-task` and learn the rest later. But that is just my own opinion. YMMV, IANAL, ROFLWTFBBQ.
So, now that I've created my useless NAnt task successfully I can work on wrapping the SDC Tasks that I need to make my world (or at least my deploy) a better place. And the less time I'm spending cleaning up after a deploy changing settings is more time I can be sitting on my butt surfing the web, or something like that.
Subscribe to:
Posts (Atom)