Build a Set of Dependent Projects
If you use
MSBuild to build a large application, you probably have your code divided into multiple projects. Some of those projects might be dependent on each other, with the output of one project providing the input for another project.
MSBuild provides the
'''MSBuild
''' task as a convenient way to coordinate the building of all projects in an application.
In this example, the
'''MSBuild
''' task is used to build both independent projects and dependent projects, where the outputs from one project are the inputs for another project.
MSBuild Task
The
'''MSBuild
''' task is used to build projects in an application. The attributes of the
'''MSBuild
''' task include:
*
Projects - specifies the project files to build. You can specify the project files as the collection of files listed in
Item elements (using the @ notation) or use a semicolon to separate a list of project files.
*
Targets - specifies the targets to build in the project files. Use a semicolon to separate a list of targets. If no targets are specified in the
'''MSBuild
''' task, the default targets specified in the project files are built.
The targets must exist in all the project files. If they do not, a build error occurs. *
TargetOutputs - identifies (in the project file that contains the
'''MSBuild
''' task) the outputs of the built targets from all the project files.
_If you want to identify the outputs from each project file or target separately, run the _
*'MSBuild** task separately for each project file or target. If you run the
**MSBuild*''' task only once to build all the project files, the outputs of all the targets are collected into one list.''
Building Independent Projects
You can use one project file to run the builds of several independent projects. If all the projects are in subdirectories in a hierarchical code base, create a project file, such as the following, in the highest-level directory:
<Project
xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
<ItemGroup> <Project
Include="
*\.proj"
Exclude="$(MSBuildProjectFile)" />
</ItemGroup> <Target Name="BuildAll">
<MSBuild Projects="@(Project)"
Targets="Build"/>
</Target>
</Project>
Note the following in this project file:
* The
Include attribute for the list of Project items uses wildcards to recursively include all files that have the .proj extension (Include="
*\.proj").
* The
Exclude attribute excludes this project file from the list of project files because this project file should not build itself (Exclude="$(
*MSBuildProjectFile*)"). This attribute is required only if this project file has the file extension .proj. The name of this project file is referenced using the reserved property
*MSBuildProjectFile*. For information about reserved properties, see
*QuickStart* Reference the Name or Location of The Project File in the Project File * If all the projects that you are building do not have a target named Build, omit the Targets attribute for the
MSBuild task so that the default targets in each project are built.
Building Dependent Projects
If there are explicit dependencies between two projects, for example, project App1.proj produces an assembly that project
*App2.proj* references, author project files such as:
*App1.proj*:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
<Target
Name="Library"
Outputs="app1.dll">
<Csc
Sources="app1.cs"
TargetType="library"
OutputAssembly="app1.dll"/>
</Target>
</Project>
*App2*.proj:
<Project
xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
<Target Name="Build">
<MSBuild Projects="app1.proj">
<Output TaskParameter="TargetOutputs"
ItemName="Reference"/>
</MSBuild> <Csc
Sources="app2.cs"
References="@(Reference)"
OutputAssembly="app2.exe"/>
</Target>
</Project>
Note the following in these project files:
* In project file
*App1*.proj, the
Outputs attribute of the Library target is required to inform the
*MSBuild* task about the expected outputs of the Library target.
* The
*MSBuild* task in project file
*App2*.proj does not specify which targets in
*App1*.proj to build so the default targets are built. However, the
Project element in project file
*App1*.proj does not contain a
DefaultTargets attribute so the first target in the project file — the Library target — is built.
* Project file
*App2*.proj uses the
Output element to identify the build outputs from the default target in
*App1*.proj — the Library target — as Reference (ItemName="Reference"). This list of output items is read from the
TargetOutputs attribute of the
*MSBuild* task (
*TaskParameter*="TargetOutputs"), which in turn reads the list from the
Outputs attribute of the Library target in
*App1*.proj (Outputs="app1.dll").
* In project file
*App2*.proj, the outputs from App1.proj, which are identified as the @(Reference) item list, are used directly as the inputs to the Csc task to build the assembly.
Project Files
These project files combine the examples above, but with an interesting variation. The project Master.proj builds all the projects recursively. It completely ignores any dependencies between the projects. For example, project Alpha.proj must be built before Beta.proj, but this does not cause build errors because the build engine automatically manages the dependencies. In the project file Master.proj, Alpha.proj might be before or after Beta.proj. If Alpha.proj is before Beta.proj, Alpha.proj is built then Beta.proj is built. If Alpha.proj is after Beta.proj, when
MSBuild attempts to build Beta.proj, it will automatically build Alpha.proj first and then Beta.proj. Then, when the build process reaches Alpha.proj, it is skipped because it has already been built.
Also note the following in these project files:
* The project Master.proj creates one directory where all the built assemblies are placed (
<MakeDir Directories="Built" /> ) and uses the Condition attribute to specify that this directory is created only if it does not exist.
* When you use a relative path to specify the location of a file, the location is interpreted relative to the current project. Therefore, the location of items that are outputs and inputs for different projects, such as @(Reference), should be specified using full paths. For example, in project files Alpha.proj and Delta.proj, the outputs of the Library target are specified using the full path (for example, Outputs="$(
*MSBuildProjectDirectory*)\..\Built\alpha.dll").
Visual C# example
Download Code - C# Master.proj:
<Project
xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
<ItemGroup>
<Project
Include="**\*.proj"
Exclude="$(MSBuildProjectFile)"/>
</ItemGroup>
<Target Name="BuildAll">
[<MakeDir]
Directories="Built"
Condition="!Exists('Built')"/>
[<MSBuild] Projects="@(Project)"/>
</Target>
</Project>
Alpha\Alpha.proj:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
<Target
Name="Library"
Outputs="$(MSBuildProjectDirectory)\..\Built\alpha.dll">
<Csc
Sources="alpha.cs"
TargetType="library"
OutputAssembly="..\Built\alpha.dll"/>
</Target>
</Project>
Beta\Beta.proj:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
<Target Name="Build">
[<MSBuild]
Projects="..\Alpha\alpha.proj; ..\Delta\delta.proj">
<Output
TaskParameter="TargetOutputs"
ItemName="Reference"/>
[</MSBuild>]
<Csc
Sources="beta.cs"
References="@(Reference)"
OutputAssembly="..\Built\beta.exe"/>
</Target>
</Project>
Gamma\Gamma.proj:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
<Target Name="Build">
<Csc Sources="gamma.cs"
OutputAssembly="..\Built\gamma.exe"/>
</Target>
</Project>
Delta\Delta.proj:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
<Target
Name="Library"
Outputs="$(MSBuildProjectDirectory)\..\Built\delta.dll">
<Csc
Sources="delta.cs"
TargetType="library"
OutputAssembly="..\Built\delta.dll"/>
</Target>
</Project>