Hello, you can probably tell from the title of this blog that I’ve had a frustrating morning trying to overcome a limitation in MSBuild when importing a project. The Import element imports the contents of one project file into another project file. When you run a build from Visual Studio using the build file type .proj files, it validates the properties and items of the build. It also does the same if you are importing a project using the Import element. This means that the project file you are importing has to exist locally on the build server or else you will get an error similar to this one:
The target “Guid” does not exist in the project. C:\Builds\5\Solution\Staging\TFSBuild.proj.
What this also means is that you cannot Import projects that are within Source Control ,like below. Well, you can, however you’re going to have to ensure that the file already exists in that location, which means that any changes you made aren’t picked up by the build this time…. you have to wait for the next build.
<Import Project="$(SolutionRoot)\**\Solution\Guid.configs.targets" />
If you could place the Import element within a target that you can customise (like “BeforeCompile”), this would not be a problem, as you could Import them post “Get”. Then you could be sure that the version you have is the version you have in Source Control. Unfortunately this is not possible to do as you cannot place an Import element inside any other MSBuild element other than ImportGroup or Project.
So, with the build server requiring the latest version of the file to exist locally, you can manually place them on the build server. Clearly this is a bad idea as we have no way to control what is on the build server.
Assuming you have Visual Studio installed on the build server, another solution would be to create a public Workspace on the server, and map to a local folder. You can then use this path in the build.proj file. You could use the MSBuild reserved property ‘MSBuildExtensionsPath’.
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TeamBuild\Guid.configs.targets" />
There is however a problem in using this. We use a basic dual branching strategy. So we have 4 active branches at any one time (dev, main, release candidate, and release.) Within these branches there can be changes made to the project files that we wish to Import as part of our build. Because there are changes to the libraries and the targets that are in the project files, we use TFS to source control these changes. And we keep these libraries/files in the relative branches. It just makes sense to do so. So when we reference a project to import, because it is in one place, and therefore can only possibly be from one branch, we can’t be certain that it is the correct version for that particular build.
The only way we can be sure that the build uses the correct version is to create a Workspace per branch, and to alter the import project location for that build.
This also implies that every release branch we have to alter the Workspace to be the correct branch for the release/release candidate builds. Then let’s factor in that we have 6 build servers which we will have to alter the Workspaces on… That’s a lot of re-configuring every month!
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TeamBuild\ReleaseCandidate\Guid.configs.targets" />
The solution provided here by the MSBuild team really does not scale and is totally at odds with having any sort of maturity in the branching strategy. Why there is this limitation on importing projects I have no idea. It assumes also that you have one or two build servers. I’ve mitigated the issue by grouping my builds to run on certain build servers. But it’s a major pain to have to go and edit the Workspaces: if you’re smart enough to source control the projects you want to import then you should be able to use those source controlled files and dlls that are retrieved as part of the Get in the build.