codeflood logo

Optional project output copying using MSBuild

Different people have different approaches for structuring their projects. This is quite evident when working with open source software such as that found in the Sitecore shared source library where the project is normally setup with the preferences of the maintainer of the project.

There are 2 major approaches to project structure. The first keeps the code under the webroot of your solution and the other keeps it separated from the webroot. Both have advantages and disadvantages and a lot of the time it comes down to personal preference which structure you go with.

My preference is to keep the code out of the webroot so I don’t have any cs or proj files muddying up my website. I use MSBuild and the AfterBuild target to copy the output and resource files from my solution into the correct locations.

Recently when I was updating EviBlog I ran into a situation where Mark Van Aalst had setup the module project structure to be developed under a Sitecore webroot. Now it would be extremely arrogant of me to update the project structure to match my preferences especially as I’m not the owner of the module. Luckily I was able to come up with an approach that would satisfy Mark’s preference for code under the webroot and my preference for code outside the webroot.

Mark doesn’t require any AfterBuild tasks performed while I require some tasks to run to copy the files to the Sitecore instance I’m using. MSBuild contains conditional operators for checking the existence of files and folders. You can use this to determine if a particular task should be run. I could leverage this to conditionally copy the files if a particular file exists.

One of the things I wanted to do with this configuration was to make it as simple as possible for other developers to easily redirect the output based on their own preferences. I didn’t want them to have to go changing MSBuild parameters. If they needed to update the AfterBuild target that would require them to update the project file and when they checked in their preferences would overwrite what others may have in place.

Another task MSBuild contains out-of-the-box is the ReadLinesFromFile task. This task allows reading text from a text file. So I could simply have the developer enter the desired target path in a file and MSBuild would take care of the rest.

To do this I created the following target in the project file. This target finds all the config patch files, binary files, layouts, sublayouts, sitecore modules files (such as the WCF SVC file, various CSS files, etc) and excludes all subversion control files and copies them to the path specified in the deploy.txt file which exists in the same location as the project file.

<Target Name="DeployToWeb">
  <ReadLinesFromFile File="deploy.txt">
    <Output PropertyName="DeployPath" TaskParameter="Lines" />
  </ReadLinesFromFile>
  <CreateItem Include="App_Config\**\*.*;
    bin\*.dll;layouts\**\*.aspx;
    layouts\**\*.ascx;sitecore modules\**\*.*"
    Exclude="**\.svn\**\*.*">
      <Output ItemName="DeployFiles" TaskParameter="Include" />
  </CreateItem>
  <Copy SourceFiles="@(DeployFiles)"
    DestinationFiles="@(DeployFiles->'$(DeployPath)\%(RelativeDir)%(Filename)%(Extension)')"
    SkipUnchangedFiles="true" />
</Target>

I conditionally invoke this target from the AfterBuild target if the deploy.txt file exists.

<Target Name="AfterBuild">
  <CallTarget Targets="DeployToWeb" Condition="Exists('deploy.txt')" />
</Target>

And that’s it. So if someone wants to check out the source code to under their webroot, they can do that. If someone wants to check out the source code outside the webroot, all they have to do to have the project files deployed to your Sitecore solution is to create a text file called deploy.txt in the same location as the project (csproj) file and fill in the path to their Sitecore installation on a single line.

Comments

Hey Al,
That's great just might want to add some support for 64bit with Sitecore, as your aware if you deploy on a 64 bit system you need the correct SQL DLL. I use this in my MSBuild version (which i need to explain to people one day when i get time :))

Hope this helps :), Chris

Ok, lets try that again;
false true

Hi Alistair,
Great post! It's always nice to see other people's approaches to solution structure. I'm in the same camp as you in preferring to keep projects and code outside of the webroot and using MSBuild to copy files after build.
My approach is slightly different in that I create a separate .proj file to handle all my post-build actions and then conditionally "build" that project with the AfterBuild target, like so:

The 'AfterBuild.proj' file is simply an MSBuild project file containing various post-build tasks (e.g. copying files, substituting values in the web.config file). This gives me even more flexibility when executing post-build tasks while keeping the main project file mostly tidy.
Cheers! adam

Alistair Deneys

Thanks Chris, I think you'll need to post that code on your blog cause the comments on this one keep pulling out the source... Yes, for some versions of Sitecore you need to be mindful of the x64 bin folder containing the 64 bit version of SQLite, but more recent versions of Sitecore have removed SQLite support and so there is no longer a bin_x64 folder.

Alistair Deneys

Thanks Adam, It’s good to see you creating reusable assets for MSBuild so you don’t have to keep creating the script by hand each time. I’ve done a similar thing on the projects at work where I’ve packaged up common tasks into a targets file which is just a project file with the extension targets. That file can then be imported into your project file and the target called directly from your target without the need to call the MSBuild executable.
At the top of the file:

Then in AfterBuild:

CopyToSitecore is a target in my Sitecore.targets file.

I use to use the structure that Al described and that worked reasonably fine with WinXP but with the introduction of UAC in Vista I found that it was providing way too much of an overhead to working with a project.
Since I run my day-to-day Windows user as a Standard user, not an Administrator having to run as an admin to debug a web project is an overhead I don't want to/ shouldn't have to deal with. The next issue I have with it is that every time you change something you need to compile. If you're doing UI refactoring (editing CSS, writing JavaScript, etc) you have to compile the project, since the files aren't "deployed" unless you compile. Sure this isn't a real problem if you have a small solution, but once you get to half a dozen projects or more you have overhead. Sure if there's nothing changed in each of the projects nothing will be compiled, but VS has to check each project regardless.
I don't understand the argument against having cs/ csproj files in your site when it's running, Cassini doesn't use them it follows the directive of the usercontrol/ page/ etc and looks for the type in the ~/bin which in turn resolved an assembly. And if you have a decent CI strategy (is there anyone still who does not have CI strategies?) your first shared environment will not include those files anyway.

Alistair Deneys

That was the whole point of this MSBuild script. I like to separate the code files out from my website so I can create the 'deploy.txt' file, but you like to run everything in the same folder so you wouldn't create that file and the project will work for both of us without modification.

But in what scenario would you want to have both options in a single solution? For collaborative development you'll end up with duplication, the subsystem (Sitecore in this instance) would then reside twice in the CMS, bloating the download...

Alistair Deneys

Well the scenario I gave in the post was precisely the reason I created this script. It's useful when you have multiple developers and they all don't want to use the same placement of project files, either inside the webroot or outside.
And you don't have to put the system in source control. In fact, you shouldn't really put the system in source control as you don't control the source of it. At Next Digital we put the CMS in source control as a means of speeding up bringing new devs onto a project but I much prefer to not put the CMS in source control at all.

Super userful, thanks Alistair!

[...] files to your Sitecore folder is up to you. I favour the build-outside-webroot model, which I’ve covered before, but on this machine lacking IIS it’s much easier to use the build-inside-webroot [...]

Leave a comment

All fields are required.