Copying linked content files at each build using MSBuild

by Matt Perdeck 9. May 2013 13:36

Sometimes we need to share JavaScript, CSS or images files amongst several web applications. This post shows how to do using Visual Studio's linked files feature, combined with a simple MSBuild target to ensure the shared content files can be found by the browser.

Sharing files with Visual Studio's linked files

You may have a number of web applications that share one or more content files. For example, JavaScript libraries, or an image with the corporate logo. When such a shared file changes, you don't want to have to remember to copy the file to all the web applications.

A solution is to store the shared files in a central location, and then in Visual Studio add links to those files, rather than copying them. That way, Visual Studio always sees the latest version.

To create a link in Visual Studio:

  1. Right click on the directory where you want to add the link.
  2. Choose Add | Existing Item.
  3. In the file dialog, navigate to the directory with shared files and select the one you want.
  4. Open the drop down on the Add button (click the little down arrow) and choose Add As Link.

The shared file now appears in the Solution Explorer, with a "link" icon.

When you now edit a shared file, you'll find that the change is immediately visible in all web applications. If you keep the file in source control in TFS, TFS will automatically check out the file when you change the file from any of the web applications.

Moreover, when you publish a web application, the shared files will be copied along with the other files of the site. Sweet!

Using an MSBuild target to physically copy the linked files

There is one problem though. When you hit F5 to debug the site, the browser can't find your linked shared files. This is because Visual Studio keeps a link to the shared file - it doesn't copy it to the directory where you placed the link in Solution Explorer.

The challenge now is to extend the build, so each time the project is built (by hitting F5 or otherwise), all linked files are copied to the directories where you placed the links. This way, you still automatically pick up changes to the shared files, while also making them available to the browser.

 To see how this is going to work, we'll need to have a look at how Visual Studio stores file links:

  1. Add a link to a file to your project if you haven't already done so.
  2. Open the .csproj file with your favorite text editor (Notepad will do).
  3. Find the file that you linked to - search for its name.

You'll find that whilst most content files have entries like this:

<Content Include="Scripts\jsnlog_test.js" />

File links look like this:

<Content Include="..\..\JSNLog\Scripts\jsnlog.js">

In other words, their distinguishing feature is that they have a Link meta with the location where the file would have been had it been physically copied over.

This means that we can copy the linked files at each build by adding the following at the end of the .csproj file, just before the final </Project> tag:

  <Target Name="CopyLinkedContentFiles" BeforeTargets="Build">
    <Copy SourceFiles="%(Content.Identity)" 
          Condition="'%(Content.Link)' != ''" />

This target uses BeforeTargets to ensure it is executed each time a build happens, even when no files in the project need to be built.

 The Copy task uses a Condition to filter out all items in the Content item group that do not have a Link meta. This way, the copy is only applied to links. To speed up the process, it sets SkipUnchangedFiles to true, so if a linked file has not been changed, it won't be copied.




Book: ASP.NET Site Performance Secrets

ASP.NET Site Performance Secrets

By Matt Perdeck

Details and Purchase

About Matt Perdeck

Matt Perdeck PresentingMatt has written extensively on .Net and client side software development.

more >>