How do you build your application?

The Hillbilly asks: How do you compile your application in your NAnt script for releases?

There are many options, you see, even though the <solution> task is no longer one of them. You could use the <msbuild> task to build the solution as it is built in VS (I think; the documentation is a little sketchy on this task if you don’t know the inner workings of msbuild). Or you could use the <csc> or <vbc> tasks to compile a group of code files into an assembly (similar to a .csproj or .vbproj file). Or, for the ultimate control, you could use the <exec> task to call out to msbuild.exe, csc.exe, or vbc.exe directly though I couldn’t tell you personally what that offers you that the imagecorresponding tasks don’t. Future-proofing maybe.

Usually, for testing purposes at least, this entails using <csc> (or <exec> with csc.exe) to compile all the application code into a single assembly that I can run tests against. It’s a practice I learned from JP Boodhoo and I’ve been happy with the results in the absence of anything better.

But I don’t care much about the unit tests. I’m more interested in your releases. Assuming you’ve automated them in a NAnt script, how do you build the application in preparation for them?

Historically, I’ve usually used the <msbuild>/msbuild.exe scenario mostly because I couldn’t wrap my head around writing a bunch of <csc> tasks to compile the code into the same structure I see in Visual Studio. For example, if your solution has a Domain project, a DataAccess project, and a UI project, I would need three <csc> tasks to create the individual assemblies. Or one <msbuild> task running against the solution. My reasoning was that it was the easiest way to build the application the same way I did in Visual Studio, which I assumed was how I wanted to deploy it.

Then I talked to Donald Belcham.

Conversations with Donald Belcham are always eye-opening (and I won’t expand on that comment this time; happy belated birthday, dude). And during our chat, he admitted to using <csc> to create his releases. I expressed my surprise: "But Iggy, what if your solution has a dozen projects in it? I don’t want to deal with the pain of a dozen <csc> tasks in the right order". To which he replied: "Dude, you don’t need to deploy the same way you build. The Visual Studio solution is a file management mechanism, not a deployment mechanism." And my mind awakened like it did during my last visit to Brian Head.

The basic idea Donald described was thus: Just because you build your application one way in Visual Studio during development, doesn’t mean you need to build your releases the same way. Especially if you are using NAnt to create your releases because then you can do it however you like.

And as if my mind wasn’t expanded enough, he typically shoots for one assembly per physical location. Not one assembly for the domain, one for the data layer, one for the common utilities, and so on and so forth. One. Total. If I were building a WinForms application with no other physical layers, I’d have one file: MyApp.exe. If it were a WinForms app calling a web service, there would be two assemblies. One for the client and one for the web service (plus corresponding .asmx files).image

This is the extreme position of course and there are caveats. If the application depends on third-party libraries, naturally we won’t bundle those up into the assembly. And if you’re using an internal client library that is common to more than one project, you’d treat that as you would any other third-party library and keep them separate. But if your application-specific domain code and data access code sits on the same server, that all gets munged into one assembly regardless of how it is organized in the .sln file. NAnt gives us that flexibility.

I’m kind of putting Donald out there for potential ridicule so I’ll expose myself ever so slightly and admit that I really like this idea. Why *do* we feel we have to ship a dozen assemblies for our application? If something changes in our domain, what difference does it make if we re-deploy MyCompany.MyApp.Domain.dll or MyCompand.MyApp.exe? And in speaking with Donald, he points out that even though we have the flexibility to deploy only parts of an application, rarely do we exercise it. In a typical scenario, new versions and hot fixes are deployed as an "all or nothing" in each physical layer anyway. This matches my experience. Even if nothing changed in certain assemblies, I typically re-deploy everything during an upgrade just to be safe.

Now, we still need to be practical about it. If the resulting executable is 300Mb, then ISDN line or not, you’ll want to break that into something more manageable. But for the average application, one assembly should function just as easily as five, yesno?

Looking forward to hearing the counter-arguments because I haven’t quite thought this through too much except from a maintenance point of view. In particular, are there performance differences with loading several smaller assemblies vs. one "larger" (but still manageable) one? What about issues with unloading an assembly from an application pool in IIS?

Enquiring minds want to know!

Kyle the Inquisitive

This entry was posted in Coding Style, Featured, NAnt. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://kyle.baley.org Kyle Baley

    Tracked it down and moved it somewhere that may or may not be more permanent.

  • Whatifwedigdeeper

    Looks like your code sample from MIX is no longer available.  It would be nice to update.  Enjoyed your presentation even 1.5 years later.

  • John

    Can anyone please send me the entire (sample) build script for a web application project at my email id johvicky@gmail.com.

  • http://weblogs.asp.net John

    You could always build the solution file using the msbuild task and then call ILMerge to merge certain assemblies into one.

    John

  • http://codebetter.com/blogs/kyle.baley Kyle Baley

    Yes, but that was my crappy implementation. I shouldn’t have tried mimicking the VS structure via tasks. That was a bit of madness that I can’t rightly justify now that I look back. To do it the way Donald suggests, I would have one task to compile all the WinForm code (i.e. all the Infrastructure projects plus the domain plus the data access) into a single executable (not including Windsor, CAB, etc, etc). Plus there would be one assembly for each of the web services. So five tasks total, if I remember correctly, which is one per physical location.

    The good part about this is that as you mention, we were already compiling the app code for testing into single assemblies using . We just needed to expand that idea and compile them based on physical architecture. That way, we could have used the same task to compile the code for testing *and* to compile the code for releases.

    But you bring up another interesting point. This doesn’t take into consideration ClickOnce deployments which, frankly, I still don’t understand completely. They seem to be more msbuild-oriented so if that’s the deployment mechanism, this might not work as smoothly.

    I’m hoping Donald will expand on this himself and maybe fill in the gaps because I think something may have got lost in my translation.

  • http://weblogs.asp.net/bsimser Bil Simser

    Kyle, we went through this last gig didn’t we? We were compiling all the app code into one assembly, and another into a test one. I thought that was good for testing and when it came to deployment you didn’t need the test assembly. Maybe one is too big and I would favor breaking it up for a pure Smart Client solution where you can trickle down individual assemblies for updating (rather than the big bang we would do). I personally thought creating csc tasks to mimic the VS structure was overkill and made things complicated.

  • http://www.dotnettricks.com Craig Bowes

    Kyle,

    Yeah I can see how CI would definitely catch that kind of stuff early on, rather than right before you go to production or testing. We don’t use Ci at my work, but we integrate fairly regularly. Right now our stuff is mall enough and our team is small enough that it doesn’t matter as much, but we’re looking into getting CruiseControl going.

    Craig

  • http://igorbrejc.net Igor Brejc

    Currently I use the same method you described – exec msbuild on solution file, but now I have encountered a situation when I want to target my build separately for .NET and Mono. I want Mono to use different versions of some 3rd party libraries (log4net, for example), but I don’t see a way to do this using the VS project structure.

    One way of dealing with this would be to XSLT-translate VS project files into NAnt csc targets. This way you still get the benefits of maintaining your projects in one place (VS), but you can modify certain aspects of the build procedure (changing references, for example). The whole thing would have to be executed within the script, off course.

  • http://codebetter.com/blogs/kyle.baley Kyle Baley

    @Craig

    Yes, you’re right. That’s an issue if you do exclude files from VS. On the other hand, I like the fact that NAnt includes everything because in the past, it’s helped me locate stray classes that are either in the wrong physical location or that have been long-abandoned and just not deleted.

    Also, I’m not talking about wild variations in my build process. Remember, this is a CI environment. As long as you use the same build process as my CI server before you check-in, surprises should be minimal. Besides which, surprises aren’t a bad thing. After all, that *is* why you have a CI process: to catch them early.

    Keep in mind that I’m not totally convinced yet either. I’m basing this solely on conversations with someone who I know would not use the practice if it didn’t reduce a great deal of pain for him. My final judgement is still reserved but Donald’s a smart guy. Which is probably why he left me to blog about this instead of doing it himself…

  • http://www.dotnettricks.com Craig Bowes

    @Kyle,

    I wondered about the possibility of compiling a whole folder, and indeed what you mentioned sounds better, but i’m not entirely convinced–yet. For example, you still run into issues if you’ve excluded a file in VS.NET but its still in the folder for the nant script to pick up. I do this at times to remove code i don’t want to compile at the moment or is going to worked on later. Now you have to have some way of also excluding it from the nant build. Not AS BIG of an issue, but still an issue. The fact is, there’s a chance that the behavior or your app is going to very slightly, from one build process to another. Then you get surprises. I don’t like surprises.

    Craig

  • http://codebetter.com/blogs/kyle.baley Kyle Baley

    @Josh: Yes, you’re absolutely right. If you are planning to re-use anything in the very near future. But that’s another topic. The summary: Building re-usable components before you know how they’ll be re-used will make them that much less re-usable.

    @Fregas: You can set up NAnt to compile all .cs or .vb files in a folder into an assembly (including all subfolders). If I had to update my build file every time I added a class, I sure wouldn’t be recommending this practise. And yes, you’re right, doing this with the hopefully-now-defunct project-less web sites is likely to cause a world of hurt. But then, doing anything with project-less web sites is plenty painful. Let’s not discard potentially useful practises because of a minor lapse in reasoning in Redmond.

    @Ayende and Alberto: If you were to compile all your C# files into a single assembly, does that alter the structure of the underlying IL compared to if they were in different assemblies? That’s not a rhetorical question because I honestly don’t know. I.E. If you have three projects, each with a different namespace and a single C# file in them, and you compile them into three separate assemblies, are there any scenarios where that will act differently than if you were to compile all three files into a single assembly?

  • http://agilology.blogspot.com/ Jeff Tucker

    I like the idea of one assembly per physical location, although there is a specific need for a single module in my application to be upgradeable (there’s a really good reason, just trust me). I may modify my production build to just make a single assembly after reading this.

    I hate relying on Visual Studio projects and solutions to tell me how to build also. It’s nice if ctrl-shift-b will build my project so I can debug it easily. However, projects do not necessarily show all the files that are physically in the directory, so it’s easy to have leftover files that are no longer in use but not part of the project. A build script will detect these as long as it doesn’t rely on the projects or solutions to tell it what to build. Also, a build script is your environmental documentation and it’s much easier to show how your application is structured and what is referenced in a build script (since you have to do it explicitly) than in Visual Studio (because then I have to open each project and click “references” and it doesn’t show me where they are without clicking properties, etc.).

  • http://sharpbites.blogspot.com alberto

    I’m all for the single-assembly-per-location approach. But as Ayende says, you want to test your build under the same circumstances. That’s why I use nant to build (and test) before commiting and in the CI server. I just use msbuild(VS) when I am making quick changes and want to check that the thing compiles. But I always end up with a nant build.

  • http://www.ayende.com/Blog/ Ayende Rahien

    Kyle,
    There are good reasons to want to have the same structure in both scenarios. Repeatability and consistency is one of them.
    I don’t want to have to deal with different release version when I need to solve a problem.
    That said, I am beginning to lean toward the two projects system. One for the app, one for the tests, and that is it.
    And creating a build script is usually a matter of few minutes.

  • http://www.dotnettricks.com/ Fregas

    No, I don’t do what you’re suggesting. Here’s my response:

    http://dotnettricks.com/blogs/craigbowesblog/archive/2008/02/11/704.aspx

  • http://undev.spaces.live.com Josh

    My argument for breaking code up into assemblies. Is based off of reuse scenarios. If you will always need the full sebang your argument rings true. If you ever want to ship just parts than splitting them up maybe a good idea.

  • http://adamv.com/ Adam Vandenberg

    The formatting here will probably get hosed, but for what it’s worth: