Platform specific Pre|PostBuildEvent in .csproj files

Xml configuration files have certainly been vilified, but they do have some lovely qualities, such as easy allowing you to stuff additional data into them without screwing things up. To be on the safe side this should be done with namespaces to avoid DTD validation issues, but often even that isn't necessary. Xml is simply, err--- extensible.

Of course, this makes a big presumption that the consuming end a) doesn't have some inflexible parser that pukes on valid but unexpected xml, and b) doesn't just import the xml into its own internal representation only write out just its known representation on save. If that's how you want to treat your xml data source, do us all a favor and stop using Xml already -- you're only invonveniencing people with angle brackets without letting them reap the benefits they could provide.

Anyway, this seems like a non-sequitor intro but I promise to explain its significance in a little bit. Now, on to the point of this post, that you can write pre- and post-build events in Visual Studio projects to target multiple platforms. This behavior is most welcome when you want to xbuild your code under mono on linux.

When you create a PostBuildEvent in Visual studio to copy some files like this:

copy $(TargetPath) $(TargetDir)MyExecutable.exe

Visual Studio actually emits this block into the .csproj Xml:

<PostBuildEvent>copy $(TargetPath) $(TargetDir)MyExecutable.exe</PostBuildEvent>

Sure, I could set up an alias from cp to copy on linux, but that's a hack sidestepping the real issue: I am likely to want different pre- and post-build behavior between windows and linux. I have to apologize for not recalling who pointed this out -- could have been on the mono-devel list or in the mono-devel irc chat -- but someone told me that Ican put a condition on <PreBuildEvent> and <PostBuildEvent> to control when it is to be executed:

<PostBuildEvent Condition=" '$(OS)' == 'Windows_NT' ">
  copy $(TargetPath) $(TargetDir)MyExecutable.exe
</PostBuildEvent>
<PostBuildEvent Condition=" '$(OS)' != 'Windows_NT' ">
  cp $(TargetPath) $(TargetDir)MyExecutable.exe
</PostBuildEvent>

This does mean I'm manually editing the .csproj, not some of the prettiest Xml around, but it establishes separate post-build steps for windows and not windows. I know it's a simplistic example, but works for the 99% use case of .NET vs. mono build environments.

Now, to resume my diatribe about Xml configuration and applications that use it: Well, the first thing that worried me about this solution was whether Visual Studio would puke once I made those changes and if it didn't puke whether it would clobber them. And I have to report, not a problem, on both accounts. Visual Studio is a good xml configuration file citizen, and only uses the parts it knows and uses the file as its data model, modifying it rather than overwriting it. Yay!