Ok, here is the challenge you have several project files that comprise a set of products. Now you want list every reference that each project file has. How do you do it? I came up with a pretty nifty way of doing it. If you look a project file for a managed project (I’ll be using C# projects for this example) you’ll see that you can see all the references listed in an Item named Reference. So there’s got to be a way to extract out that information for each project. I was thinking that it would be nice if each project had a target that would simply output that item. But that would involve changing each project to add the target to perform that. Which obviously is not an option. But what we can do is achieve the exact same thing, by using the Import element. Take a look at the simple targets file below:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(ProjectFile)"/>
<Target Name="GetReferences" Outputs ="@(ProjReferences)">
<Message Text="Getting references for project $(ProjectFile)"/>
<CreateItem Include="@(Reference)">
<Output ItemName="ProjReferences" TaskParameter="Include"/>
CreateItem>
Target>
Project>
What this will do is import an MSBuild project using the Import statement, this file is driven off of a property file. After that it will merge a new target GetReferences with the project declared in ProjectFile. Now we simply need a way to specify that ProjectFile. To achieve this we will actually change the above statements to include the condition Condition="$(ProjectFile)!=''". And we add the following statements to find all project files, enumerate references and write a file containing all of them.
<ItemGroup>
<ProjectFiles Include="**\*.csproj"/>
ItemGroup>
<PropertyGroup>
<RefFile>SolutionReferences.txtRefFile>
PropertyGroup>
<Target Name="SetupFindRef">
<Delete Files="$(RefFile)"/>
<WriteLinesToFile File="$(RefFile)" Lines="ProjectName%09Reference"/>
Target>
<Target Name="FindReferences" Outputs="%(ProjectFiles.FullPath)" DependsOnTargets="SetupFindRef">
<MSBuild Projects="$(MSBuildProjectFile)" Properties="ProjectFile=%(ProjectFiles.FullPath)" Targets="GetReferences">
<Output ItemName="ProjReferences" TaskParameter="TargetOutputs"/>
MSBuild>
<WriteLinesToFile File="$(RefFile)" Lines="%(ProjectFiles.FullPath)"/>
<WriteLinesToFile File="$(RefFile)" Lines="@(ProjReferences->'%09%(Identity)')"/>
Target>
If we execute the FindReferences target on this project file it will batch over each project file. It will then execute the same project file with the property ProjectFile defined, and execute the GetReferences target. So what is really happening is that we are effectively extending the existing MSBuild project file and injecting a new target to an existing project file. Pretty nifty.
Here is the entire project file so you can see the end result, also you can download it below.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="FindReferences">
<ItemGroup>
<ProjectFiles Include="**\*.csproj"/>
ItemGroup>
<PropertyGroup>
<RefFile>SolutionReferences.txtRefFile>
PropertyGroup>
<Target Name="SetupFindRef">
<Delete Files="$(RefFile)"/>
<WriteLinesToFile File="$(RefFile)" Lines="ProjectName%09Reference"/>
Target>
<Target Name="FindReferences" Outputs="%(ProjectFiles.FullPath)" DependsOnTargets="SetupFindRef">
<MSBuild Projects="$(MSBuildProjectFile)" Properties="ProjectFile=%(ProjectFiles.FullPath)" Targets="GetReferences">
<Output ItemName="ProjReferences" TaskParameter="TargetOutputs"/>
MSBuild>
<WriteLinesToFile File="$(RefFile)" Lines="%(ProjectFiles.FullPath)"/>
<WriteLinesToFile File="$(RefFile)" Lines="@(ProjReferences->'%09%(Identity)')"/>
Target>
<Import Project="$(ProjectFile)" Condition="$(ProjectFile)!=''"/>
<Target Name="GetReferences" Outputs ="@(ProjReferences)" Condition="$(ProjectFile)!=''">
<Message Text="Getting references for project $(ProjectFile)"/>
<CreateItem Include="@(Reference)">
<Output ItemName="ProjReferences" TaskParameter="Include"/>
CreateItem>
Target>
Project>
Here is what the result looks like (for the some of the Enterprise Library projects):
ProjectName Reference
C:\TEMP\EnterpriseLibrary\src\Caching\Caching.csproj
System
System.configuration
System.Configuration.Install
System.Data
System.Management
System.Xml
C:\TEMP\EnterpriseLibrary\src\Caching\Configuration\Design\Caching.Configuration.Design.csproj
System
System.configuration
System.Data
System.Design
System.Drawing
System.Windows.Forms
System.XML
C:\TEMP\EnterpriseLibrary\src\Caching\Cryptography\Caching.Cryptography.csproj
System
System.configuration
C:\TEMP\EnterpriseLibrary\src\Caching\Cryptography\Configuration\Design\Caching.Cryptography.Configuration.Design.csproj
System
System.configuration
System.Data
System.Drawing
System.Xml
The above project file will actually generate a tab delimited file, so you can open it up in Excel and take a closer look.
Sayed Ibrahim Hashimi
Comments are closed.