We want to store our overridden build targets in an external file and include that targets file in the TFSBuild.proj. We have a core set steps that happens and would like to get those additional steps by simply adding the import line to the TFSBuild.proj created by the wizard.
<Import Project="$(SolutionRoot)/libs/my.team.build/my.team.build.targets"/>
We cannot have an import on any file in the $(SolutionRoot)
because at the time the Import statement is validated, the source has not be fetched from the repository. It looks like TFS is pulling down the TFSBuild.proj
first without any other files.
Even if we add a conditional import, the version in source control will not be imported if present. The previous version, already present on disk will be imported.
We can give up storing those build targets with our source, but it is the first dependency to move out of our source tree so we are reluctant to do it.
Is there a way to either:
Import
statements evaluate correctly?AfterCompile
in a manner besides the Import
?The Team Build has a "bootstrap" phase where everything in the Team Build Configuration folder (the folder with TFSBuild.proj) is downloaded from version control. This is performed by the build agent before the build agent calls MSBuild.exe telling it to run TFSBuild.proj.
If you move your targets file from under SolutionRoot and place it in your configuration folder alongside the TFSBuild.proj file you will then be able to import it in your TFSBuild.proj file using a relative import statement i.e.
<Import Project="myTeamBuild.targets"/>
If these targets rely on any additional custom MSBuild task assemblies then you can also have them in the same folder as your TFSBuild.proj file and you can reference them easily using a relative path.
Note that in TFS2008, the build configuration folder defaults to being under $/TeamProject/TeamBuildTypes however, it does not have to be there. It can actually live in a folder that is inside your solution - and can even be a project in your solution dedicated to Team Build. This has several advantages including making branching of the build easier. Therefore I typically have my build located in a folder like this:
$/TeamProject/main/MySolution/TeamBuild
Also note that by default, during the bootstrap phase of the build, the build agent will only download files that are in the build configuration folder and will not recurse down into any subfolders. If you wanted it to include files in subfolders during the bootstrap phase then you can set the following property in the appSettings of the tfsbuildserver.exe.config file on the build agent machines (located in %ProgramFiles%\Visual Studio 9.0\Common7\IDE\PrivateAssemblies)
<add key="ConfigurationFolderRecursionType" value="Full" />
Note that if you had multiple build agents you would have to remember to set this setting on all of the machines, and it would affect every build performed by that build agent - so really it is best just to keep the files in the root of the build configuration folder if you can.
Good luck,
Martin.
If the targets should only be run when TFS is running the build and not on your local development machines, you can put your targets file in the folder for the build itself and reference it with:
<Import Project="$(MSBuildProjectDirectory)\my.team.build.targets.proj" />
However, if you want the targets to run for all builds, you can set it up so that the individual projects reference it by adding something like:
<Import Project="$(SolutionRoot)/libs/my.team.build/my.team.build.targets" Condition="Exists('$(SolutionRoot)/libs/my.team.build/my.team.build.targets')" />
On my project we actually use both of these, the first allows us to customize the nightly builds so we can do extra steps before and after running the full solution compile, and the second allows project-by-project customization.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With