- Posted by justin on February 21, 2007
Good post on how to use nant to update the version information in the assemblyinfo.cs the way that Microsoft does it.
Versioning the Microsoft way... with NAnt
I was inspired by a recent blog entry by Jeff Atwood here about how Microsoft versions their products and how the build number is significant. I thought it would be good to post a walkthrough of how to build your own versioning system ala Microsoft but using NAnt. I'm sure some budding geek out there could convert this to MSBuild, but you know my love of that tool so NAnt it is.
First off, NAnt has a great facility for generating that AssemblyInfo.cs file that every project has. It's the asminfo task and basically looks like this:
<?xml version="1.0"?>
<project name="Test" default="UpdateAssemblyInfo">
<target name="UpdateAssemblyInfo">
<asminfo output="AssemblyInfo.cs" language="CSharp">
<imports>
<import namespace="System.Reflection" />
<import namespace="System.Runtime.InteropServices" />
</imports>
<attributes>
<attribute type="AssemblyTitleAttribute" value="ClassLibrary1" />
<attribute type="AssemblyDescriptionAttribute" value="" />
<attribute type="AssemblyConfigurationAttribute" value="" />
<attribute type="AssemblyCompanyAttribute" value="" />
<attribute type="AssemblyProductAttribute" value="ClassLibrary1" />
<attribute type="AssemblyCopyrightAttribute" value="Copyright (c) 2007" />
<attribute type="AssemblyTrademarkAttribute" value="" />
<attribute type="AssemblyCultureAttribute" value="" />
<attribute type="ComVisibleAttribute" value="false" />
<attribute type="GuidAttribute" value="f98c8021-fbf1-44ff-a484-946152cefdb8" />
<attribute type="AssemblyVersionAttribute" value="1.0.0.0" />
<attribute type="AssemblyFileVersionAttribute" value="1.0.0.0" />
</attributes>
</asminfo>
</target>
</project>
This will product a default AssemblyInfo.cs file that looks like this:
using System.Reflection;
using System.Runtime.InteropServices;
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.42
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[assembly: AssemblyTitleAttribute("ClassLibrary1")][assembly: AssemblyDescriptionAttribute("")][assembly: AssemblyConfigurationAttribute("")][assembly: AssemblyCompanyAttribute("")][assembly: AssemblyProductAttribute("ClassLibrary1")][assembly: AssemblyCopyrightAttribute("Copyright (c) 2007")][assembly: AssemblyTrademarkAttribute("")][assembly: AssemblyCultureAttribute("")][assembly: ComVisibleAttribute(false)]
[assembly: GuidAttribute("f98c8021-fbf1-44ff-a484-946152cefdb8")][assembly: AssemblyVersionAttribute("1.0.0.0")][assembly: AssemblyFileVersionAttribute("1.0.0.0")]
Notice however a few things. First is the Guid. We had to hard code that which might be okay, but lets dig into NAnt scripting by replacing it with a real Guid. NAnt also has the ability to let you write embedded code (C#, VB.NET, etc.) via the <script> task, so let's write a small task to do that. We'll just have it generate a new Guid and set a new custom property in the NAnt script that we'll use in our asminfo task. Create a property in the NAnt script to hold our Guid:
<property name="project.guid" value="f98c8021-fbf1-44ff-a484-946152cefdb8" />
Then use that property in our GuidAttribute:
<attribute type="GuidAttribute" value="${project.guid}" />
Finally here's the task to generate a Guid via NAnt (make the default UpdateAssemblyInfo task dependent on this one):
<target name="CreateUniqueGuid">
<script language="C#">
<code>
<![CDATA[
public static void ScriptMain(Project project) { project.Properties["project.guid"] = Guid.NewGuid().ToString();
}
]]>
</code>
</script>
</target>
Great. We now have a NAnt script that will generate a new version file with a unique Guid everytime. Next we want to tackle the versioning issue.
As described by Jensen Harris here, the Microsoft Office scheme is pretty simple:
- Take the year in which a project started. For Office "12", that was 2003.
- Call January of that year "Month 1."
- The first two digits of the build number are the number of months since "Month 1."
- The last two digits are the day of that month.
Using this we'll need to setup a couple of properties. One is to hold the year the project starts, the other is the build version we want to set:
<property name="project.year" value="2003" />
<property name="build.version" value="1.0.0.0" />
Now we could write a lot of NAnt code as there are functions to manipulate dates, but it's much easier using the <script> task and some C#. Here's the NAnt task to generate the build number using the Microsoft Office approach:
<target name="GenerateBuildNumber">
<script language="C#">
<imports>
<import name="System.Globalization" />
<import name="System.Threading" />
</imports>
<code>
<![CDATA[
public static void ScriptMain(Project project) { Version version = new Version(project.Properties["build.version"]);
int major = version.Major;
int minor = version.Minor;
int build = version.Build;
int revision = version.Revision;
int startYear = Convert.ToInt32(project.Properties["project.year"]);
DateTime start = new DateTime(startYear, 1, 1);
Calendar calendar = Thread.CurrentThread.CurrentCulture.Calendar;
int months = ((calendar.GetYear(DateTime.Today)
- calendar.GetYear(start)) * 12)
+ calendar.GetMonth(DateTime.Today)
- calendar.GetMonth(start);
int day = DateTime.Now.Day;
build = (months * 100) + day;
version = new Version(major, minor, build, revision);
project.Properties["build.version"] = version.ToString();
}
]]>
</code>
</script>
We get the version in the NAnt script as a starter (since we're only replacing the build number) and then assign values to it (they're read-only in .NET). Then this is written back out to the property as a string.
If this is run today (February 17, 2007) it's been 49 months since the start of 2003 and today is the 17th day. So the build number is 4917.
Here's the finaly output from this NAnt script:
using System.Reflection;
using System.Runtime.InteropServices;
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.42
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[assembly: AssemblyTitleAttribute("ClassLibrary1")][assembly: AssemblyDescriptionAttribute("")][assembly: AssemblyConfigurationAttribute("")][assembly: AssemblyCompanyAttribute("")][assembly: AssemblyProductAttribute("ClassLibrary1")][assembly: AssemblyCopyrightAttribute("Copyright (c) 2007")][assembly: AssemblyTrademarkAttribute("")][assembly: AssemblyCultureAttribute("")][assembly: ComVisibleAttribute(false)]
[assembly: GuidAttribute("a6e7ff79-63ba-443f-8bc3-0c4b43f43ffe")][assembly: AssemblyVersionAttribute("1.0.4917.0")][assembly: AssemblyFileVersionAttribute("1.0.4917.0")]
And here's the full NAnt script:
1 <?xml version="1.0"?>
2 <project name="Test" default="UpdateAssemblyInfo">
3
4 <property name="project.guid" value="f98c8021-fbf1-44ff-a484-946152cefdb8" />
5 <property name="project.year" value="2003" />
6 <property name="build.version" value="1.0.0.0" />
7
8 <target name="UpdateAssemblyInfo" depends="CreateUniqueGuid, GenerateBuildNumber">
9 <asminfo output="AssemblyInfo.cs" language="CSharp">
10 <imports>
11 <import namespace="System.Reflection" />
12 <import namespace="System.Runtime.InteropServices" />
13 </imports>
14 <attributes>
15 <attribute type="AssemblyTitleAttribute" value="ClassLibrary1" />
16 <attribute type="AssemblyDescriptionAttribute" value="" />
17 <attribute type="AssemblyConfigurationAttribute" value="" />
18 <attribute type="AssemblyCompanyAttribute" value="" />
19 <attribute type="AssemblyProductAttribute" value="ClassLibrary1" />
20 <attribute type="AssemblyCopyrightAttribute" value="Copyright (c) 2007" />
21 <attribute type="AssemblyTrademarkAttribute" value="" />
22 <attribute type="AssemblyCultureAttribute" value="" />
23
24 <attribute type="ComVisibleAttribute" value="false" />
25
26 <attribute type="GuidAttribute" value="${project.guid}" /> 27
28 <attribute type="AssemblyVersionAttribute" value="${build.version}" /> 29 <attribute type="AssemblyFileVersionAttribute" value="${build.version}" /> 30 </attributes>
31 </asminfo>
32 </target>
33
34 <target name="CreateUniqueGuid">
35 <script language="C#">
36 <code>
37 <![CDATA[
38 public static void ScriptMain(Project project) { 39 project.Properties["project.guid"] = Guid.NewGuid().ToString();
40 }
41 ]]>
42 </code>
43 </script>
44 </target>
45
46 <target name="GenerateBuildNumber">
47 <script language="C#">
48 <imports>
49 <import name="System.Globalization" />
50 <import name="System.Threading" />
51 </imports>
52 <code>
53 <![CDATA[
54 public static void ScriptMain(Project project) { 55 Version version = new Version(project.Properties["build.version"]);
56 int major = version.Major;
57 int minor = version.Minor;
58 int build = version.Build;
59 int revision = version.Revision;
60
61 int startYear = Convert.ToInt32(project.Properties["project.year"]);
62 DateTime start = new DateTime(startYear, 1, 1);
63 Calendar calendar = Thread.CurrentThread.CurrentCulture.Calendar;
64 int months = ((calendar.GetYear(DateTime.Today)
65 - calendar.GetYear(start)) * 12)
66 + calendar.GetMonth(DateTime.Today)
67 - calendar.GetMonth(start);
68 int day = DateTime.Now.Day;
69 build = (months * 100) + day;
70
71 version = new Version(major, minor, build, revision);
72 project.Properties["build.version"] = version.ToString();
73 }
74 ]]>
75 </code>
76 </script>
77 </target>
78
79 </project>
Enjoy!
- Posted by justin on January 31, 2007
I have to say that I completely agree with the blog post below. I have experienced many developers that could code but were not good developers.
Original Blog Post From: http://codebetter.com/blogs/jeremy.miller/archive/2007/01/31/Being-a-Better-Programmer.aspx
There's a flurry of great posts and comment discussions going on right now about the divide between good developers and bad developers
The Great Divide
Like Jeff Atwood, I do believe there is an almost binary switch between effective and ineffective developers. I'm not exactly sure why this is, but if you'll indulge me, I've got a couple pet theories:
- People that are good coders are able to do make code work faster, which leads to getting more experience than their slower , and spare more thought cycles for how to do things better next time. The innately talented developer is able to get better and faster each time, which gives him or her more time to invest in getting better. From there I think it's simply a snowball effect.
- To Phil Haack's point that it's not entirely innate ability, any degree of contemplative thoughtfulness on the part of a developer will make that developer much better over time. The smartest guy in the world can still be intellectually lazy (but I don't see how you *could* be smart without intellectual inquisitively). Thoughtful people gain more valuable experience. People who aren't thoughtful may only be practicing their typing skills.
- Joannes added this as a prerequisite for being a great developer: "… a passionate curiosity for software related matter …." Every very good or great developer I've ever worked with enjoyed software development. I've been interviewing developer candidates lately, and a passion and intellectual curiosity about software development counts far more with me than Trivial Pursuit Q&A exchanges.
- Software development takes a very strong ability for abstract thought. I don't think that concrete thinkers have an easy time with software that's by definition a mental model of something else. I'd say visualization is important, but I've worked with good developers who had zero visualization ability. All the great developers I've worked with did though. People do think differently and have different modes of learning.
You can get Better
Like everyone else I believe that your best bet is to hire the best and brightest, but in reality you have who you have. Maybe the hiring process gets you the wide swings in productivity, but wringing out a 10-30% productivity gain out of journeyman developers is still a substantial gain. Training, coaching, discipline, better practices, communication techniques -- they can all contribute to productivity. "It's not the methodology, it's the man" is a hackneyed and cheap way to get some sort of imaginary high ground in any discussion, but it's partially BS. Anybody can get better, even if it's only in the way they interract with other people in the team. Besides, it's extremely unlikely that you can instantly go and replace poorly performing team members with better developers at will. You're largely stuck with the folks that you have. Making them better is probably your only option.
Following a tangent, when any debate about software practices or processes is going on some clown always pulls out this phrase: "you should just pick the best practices from waterfall, XP, Agile, RUP, whatever..." Hah! I've now seized the high ground you might think. No you haven't, you've simply indulged in the intellectual equivalent of empty calories. It's a worthless statement. The question "what *is* the best way to work" is still on the table. If we knew what the best of everything was, without a shred of doubt, we wouldn't be having the debate, now would we? Plus it's even harder than that because mix and match practices might not work well because many practices reinforce each other. I think you could happily add TDD and CI to any methodology and get some benefits, but they shine a bit more in an adaptive process. On the other hand, doing continuous design or adaptive project management on a codebase without automated tests and CI sounds dangerous to me.
- Posted by justin on January 19, 2007
Great Post on Test Types
from: http://blogs.msdn.com/ploeh/archive/2006/08/27/727211.aspx
There's not a single clear definition of what a unit test is and the fact that you can use unit testing frameworks (such as Visual Studio 2005 Team System or NUnit) to drive not only unit tests, but also other types of tests, confounds the issue.
Although some people find such categorization semantic (or even pedantic), I distinguish between the following test types:
- Unit tests subject a single unit to various stimuli in isolation from its volatile dependencies. In my terminology, when talking about .NET, a unit corresponds to an assembly, although other people view a unit as corresponding to a specific type. Viewing a unit as a single type is, in my opinion, too specific, as it effectively prohibits scenario-driven (or agile) development methods, since you will not be able to test the interaction between several types in the same assembly.
Isolation from volatile dependencies is essential, because it enables you to develop your unit without having to rely on its dependencies.
- Integration tests cover a wide variety of scenarios where you connect two or more units into an integrated subsystem. This covers the simple case where two units are used together in the same process; e.g. if you want to test the interaction between a UI controller unit and a domain logic unit.
Integration tests can also span process or even machine boundaries, which is the case if you test a data access component against a relational database system, or if you test a web service by making requests against it using a proxy class.
Sometimes I wish the terminology would allow us to distinguish between the in-proc and out-of-process scenarios, but they are all integration tests.
Since integration tests involve the interaction of several moving parts, they are typically more difficult to set up to run as fully automated tests, although unit testing frameworks such as Visual Studio 2005 Team System are well suited also for this task.
- System tests are by some also called acceptance tests. These test a system in its entirety, by using its intended interfaces - typically its user interface.
Since the entire application, including user interfaces, is being tested, it is typically very difficult to run system tests in a fully automated fashion, because there is often much deployment and configuration to be done.
As you can see from the comments to my earlier post, people sometimes feel they have problems with unit tests, when in fact they have problems with integration tests. Integration tests are more difficult to configure for full automation, which is why I recommend that you try to test as much of your code as possible with unit tests.
For example, instead of testing a web service with an integration test where you make requests against the service using a proxy class, you should impement the service logic in a testable unit (i.e. a library), and then unit test that logic. Exposing the service as a web service will then be a simple case of implementing the Remote Façade design pattern, and you probably will not need to test the web service at all, because its implementation will be very simple.
That is not to say that I don't recommend integration or system tests - these can provide valuable insights into the interaction of multiple components that would be difficult to reproduce otherwise. Because integration and system tests are more difficult to automate than unit tests, you can get more value for your effort early in the development process with unit tests, but as you approach project completion, you need to begin testing the complex interactions.