Link whole directories into Visual Studio Projects

You can include a file into a Visual Studio project in two ways: copy it into the project directory, or link it. Copying the file into the project directory is Visual Studio’s default option: it’s what happens when you drag a file from Explorer into VS’s project explorer, or when you use the dialog Project -> Add existing item. This is fine unless you wish to share files between multiple projects. An example where I needed to share a whole lot of files between different projects is NUnit for Silverlight: the same code is used by three projects for different versions of Silverlight and the Silverlight unit testing framework.

To link a file into Visual Studio, you can use the Project -> Add existing item dialog: the “Add” button has a dropdown menu, and the second option is “Add as link”. This will leave the file where it is and add a relative link to the Visual Studio project file. In the project file (which is BTW a MSBuild file), this looks like that:

    <Compile Include="..\..\..\src\VersionInfo.cs">
      <Link>VersionInfo.cs</Link>
    </Compile>

If you add multiple files, Visual Studio will create one item like this for each file. To add all files in a directory, mark all files in the the Project -> Add existing item dialog, and link them. This is fine as long as you don’t delete any files or add new ones – Visual Studio will not pick up these changes. New files in the directory won’t be in the project, and deleting/renaming files will cause the build to fail.

A solution is to modify the project file manually. The Include directive supports wildcards. So to link all C# code files in a directory, you can do this:

   <Compile Include="..\..\..\src\NUnit.Silverlight.Framework\*.cs" />

Nice, but in the case of NUnit for Silverlight, there were C# code files in subdirectories. Using the Compile directive as above, the files are included, but all appear in the root folder of the project – and if a file with the same name exists in the parent directory and the subdirectory, it won’t work. However, there is support for metadata keywords in MSBuild. For example, the %(Filename) keyword is a placeholder for each match in a wildcard. See this article for reference: MSBuild: By Example—Introducing Well-Known Metadata. Now we can include subdirectories:

 <Compile Include="..\..\..\src\NUnit.Silverlight.Framework\*.cs" />
    <Compile Include="..\..\..\src\NUnit.Silverlight.Framework\Attributes\*.cs">
      <Link>Attributes\%(FileName)</Link>
    </Compile> 
	<Compile Include="..\..\..\src\NUnit.Silverlight.Framework\Constraints\*.cs">
      <Link>Constraints\%(FileName)</Link>
    </Compile> 
    <Compile Include="..\..\..\src\NUnit.Silverlight.Framework\Exceptions\*.cs">
      <Link>Exceptions\%(FileName)</Link>
    </Compile> 

As you see, I need to link three subdirectories. Using a * placeholder in the directory name and the %(RecursiveDir) keyword should enable adding these with a single statement like this:

 
    <Compile Include="..\..\..\src\NUnit.Silverlight.Framework\*\*.cs">
      <Link>%(RecursiveDir)%(FileName)</Link>
    </Compile> 

… but that does not work. Never mind, including subdirectories one by one is enough for me right now.

About these ads
About

Christian is a software architect/developer. He lives in Germany, reads a lot, and likes cycling.

Tagged with:
Posted in Coding
8 comments on “Link whole directories into Visual Studio Projects
  1. Liam says:

    I had the same issue that you were having and came across this post via google; thanks very much as it helped a lot =)

    As explicit definitions for each directory was good enough for you I’m not sure you’ll be interested in this however…

    I think the issue you may have had is that you had a single * wildcard to specify that you want to recursively search subdirectories of NUnit.Silverlight.Framework, however I think you need two… i.e.

    %(RecursiveDir)%(FileName)

    That worked for me.

    Another thing I noticed is that I didn’t appear to need the %(RecursiveDir) keyword in the link section… so:

    %(FileName)

    worked for me =)

    • Liam says:

      hmm… code snippets didn’t work and I can’t edit my post; second try…

      #1

      %(RecursiveDir)%(FileName)

      #2

      %(FileName)

    • Liam says:

      eeek; oh well I guess none of those tags are enabled on comments, but I think you get the picture…

      i.e. the include being:
      Include=”..\..\..\src\NUnit.Silverlight.Framework\**\*.cs”
      and the link section only requiring %(FileName) for me

  2. EShy says:

    good post, only change I had to make was \**\*.cs
    This causes all directories and their child directories to be linked

  3. Ross says:

    Great technique!

    I have tried bastardising it to work on a web project using it to link content instead. Only problem is, when publishing the website the output for each folder only contains one entry – (%Filename).

    Does anyone know how to get the publish mechanism to pick up on the linked files?

  4. DK says:

    I needed to deal with the same problem: trying to extract common classes to use in multiple projects. This solution worked, although I saw a linked class in the root project directory. At the same time, I need to structure code in Subversion, and minimize changes on the CI server (Jenkins). The natural choice for the latter is Subversion Externals property. While doing that, I had a thought: Subversion Externals can work to solve this problem, plus they would present all shared (linked, external) folders within main projects with all that goes with it: refactoring, visibility, etc.
    All I have to do is this: set “svn:externals = ../../Common Common” property on the project folder.
    So, if you are using SVN, I think it is a better solution.

  5. Tim Berneiser says:

    Thank you very much, to all of you. ;-)
    The “\**\*.cs” works very well.
    It’s a riddle for me that Microsoft don’t add a button in the gui for that.
    Like “Link all files from a directory” (all subdirectories, with *.XXX, etc.)
    The function itself is there, but no way to use it with the gui. ;-)
    Microsoft, hiding functions since the beginning.

    Greetings from Germany

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: