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.