How To: Install a custom target to a well-known location

This question came across our internal conversion alias today:

We are deploying our own VS project templates and they use a custom .targets file. Since the user can install our bits anywhere they want, we can’t hardcode the path to the targets file in our .csproj file. Do you have any suggestions on how to handle this?

The solution to this is to make use of the $(MSBuildExtensionPath) property. In a project file this will always resolve to <Program Files>\MSBuild on the installed machine. When you author your project template, the line that imports the custom .targets file should look like this:

<Import Project=$(MSBuildExtensionPath)\MyCompany\My.Custom.targets />

Then in your installer you just make sure to put your My.Custom.Targets file into the <Program Files>\MSBuild\MyCompany directory. You can use the same approach if you have custom tasks that need to be installed as well.

[ Author: Neil Enns ]

Comments (5)

  1. Keith Hill says:

    I’m a little confused because I thought the Microsoft.CSharp.Targets file was located in MSBuildBinPath which is the .NET Framework dir? BTW, I’ve also found that relative paths work quite nicely for importing a custom targets file.

  2. msbuild says:


    Good catch, my example is somewhat confusing because of a bad cut-and-paste. I’ve updated the post, the target file imported should have been "My.Custom.Targets" and it should have been stored in a subfolder called MyCompany. Hopefully this makes more sense.

    While it’s true that this is a different location than where our standard targets are kept, we’re pretty sure companies would rather install their custom tasks and targets under program files (the typical place to install things in general) rather than putting them in the very framework-specific directory that MSBuildBinPath resolves to.

    Relative paths do work quite nicely if you’re customising your own builds, and I use them frequently. They’re tricky, however, when you are building a custom task and target combination that you want to ship to the general public for use. I have one of those, a task for manipulating assemblyinfo files, that should go up on the web sometime next week, and it needs to install using the mechanism described in the post.

    Hope this helps!


  3. Matt Gollob says:

    The $(MSBuildExtensionPath) property does not exist.  The example should actually read $(MSBuildExtensionsPath).  The difference being the word Extensions, plural.