Tuesday, May 31, 2011

TFS Build Workflow

This will be a rather lengthy post. I've received several emails asking how to implement the TFS build scripts. Well, it's pretty easy actually.

First things first. The build scripts need to be available to your build agent, of course. To that end, you can either put them in a fixed location and call them from that fixed path. However, at Streamline Systems we actually sometimes switch between building on a dedicated build VM (per client - to support different versions of AX) to building on an AOS server where consultants can remote in to test.
To support our multiple scenarios, we actually put the build scripts IN the source tree, so that the build script workflow can run it, regardless of what server or where the scripts are located. They're always in the source's root. The added benefit is you're versioning your build scripts, in case you need something crazy for a particular project/branch.

Anyway, the following is a TFS project based on the CMMI template. I'm unsure if the Agile template has a different build template. I'm assuming it's slightly different, except for these essentials steps, regardless of your project management style.

First, locate the spot in the XAML (have fun scrolling) where it actually does the compiling. I've noticed there's more than one MSBuild statement in there, so locate this exact spot.



Now, we want to get rid of the MSBuild task (delete!) and add the calls to our build scripts instead. To get the console output, errors and warnings to show up in the logs, we need to capture the output. So, to that end, add a step (where the MSBuild used to be) and add the "InvokeProcess" workflow step. In fact, add two while you're at it.
Now, as you may have noticed, the build scripts have some optional parameters. The cool thing with TFS is, you can create "arguments". That way, even if you don't use the optional parameters NOW, you can still use them later. Also, we'll use arguments to make the AX configuration file a parameter to the build script.
As for the standard and error output, you can have it write to the actual output, and then use them to write as build output / build errors. Completed, it should look something like this.



As you can tell, the process you invoke, should be "powershell.exe". We use the SourcesDirectory as the working directory, since that is where our build scripts will be located (TFS will pull them from the source tree, along with the XPOs). As for the arguments, we pass in the main build script name along with the properties.



You can see we added an "argument" for the config file which you can then use in the parameters to the build script. As for the second InvokeProcess step you've added, that is used to get the compiler errors and warnings. Same setup for the workflow step, except this time, write the standard output to the writebuildwarning, and error output you write to writebuilderror.



Now you setup the build definition; you will need a blank solution file... you don't need anything in it. I'm sure you can find it in the workflow where it looks for it (and remove that step), but we never bothered.

I hope this will get you started with the scripts. Let me know how it works out for you!

Build scripts update

I have updated the Dynamics AX 2009 build scripts on CodePlex.

This update features a fix for the clean layer list, there was a powershell variable issue (the variable was declared as string, so couldn't be re-used as an array).
There's also an additional "feature" that contains a tweakable time-out for the calls to AX32, to avoid issues where the build process gets stuck due to a client dialog (can't connect to AOS etc).
A third addition/fix is to remove the read-only flag from the label file after copying it to the application folder.

I've associated a new release with all the scripts in one zip file.

Thursday, May 5, 2011

AX 2012 & .NET - Coding Business Logic in C#

AX 2012 has pushed the boundary of .NET interop, and - as Microsoft touts - makes managed code a "first-class citizen" in the application.
This is true - with some limitations on the eventing side in my personal opinion - but there are some caveats, which can easily be overcome if you understand the underlying workings of the proxies.

Consider the following example, in C#:


Query query = new Query();
query.addDatasource(Global.tableName2Id("CustTable"));

QueryRun queryRun = new QueryRun(query);
while(queryRun.next())
{
/* .... */
}



At first glance, this would work. And it compiles as well. However, a runtime error occurs complaining that object of type "Query" does not have a method called "Next".

So what's happening? Well, remember the classes in managed code are proxies. So, they can be instantiated by giving them a reference to an existing X++ object. When you pass in your query object to the constructor of QueryRun, it will try to wrap the managed QueryRun proxy around the X++ object reference, which is the Query.

As you can see in the IntelliSense, the other overload of the constructor is to pass in an object of type "object". Not much documentation there, but that is in fact the X++ New() parameter profile we would like to invoke. So, we change our C# code to:


query = new Query();
query.addDataSource(Global.tableName2Id("CustTable"));

queryRun = new QueryRun(query as object);
while (queryRun.next())
{
/* .... */
}



Now, this will work as expected.