- | rssFeed | My book on MSBuild and Team Build | Archives and Categories Wednesday, November 21, 2007

MSBuild: How to get all generated outputs

MSBuild: How to get all generated outputs

Earlier today a reader of my book emailed me the following question

I am trying to get a list of all the files being generated when MSBuild build a  certain project (they should be all the files in .bin directory).

When I used the TargetOutputs in MSBuild task it gives me the main output only, which is the .exe or .dll. But any other files like dependency assemblies will not be listed in the TargetOutputs collection.

Any advice??

So it sounds like he is looking for a generic way of determining all the files that have been created (moved) during the build process. I’m assuming that he has a few projects and is using a build script to run the process, and then he wants to move the files to another location. If you have a few projects, then obviously the easiest way is to manually examine the output paths of each project and copy the file as necessary. This is not an ideal scenario, because if you add a project to your process you have to make sure to remember to perform the copy manually. Anywayz, this type of behavior would be useful in many different scenarios. Now let’s talk about how to achieve this.

I took another look at the Microsoft.Common.targets file and determined that there is not a straight forward method of performing this action. So, after thinking about this for a little while I think I have come up with a solution. It’s a little confusing, but actually quite simple.

The solution lies in what I’m calling “MSBuild Inheritance”, which I’ve blogged about previously in the entries at:

·        MSBuild Property & Item Overide Problems

·        MSBuild: Find Many Project References

This is where you take an existing MSBuild file and without modifying it you add behavior and or data to it. Now let’s see how this idea can be used to add the desired behavior described above. For this sample I have created a sample Windows Forms app, as well as an external build script, buildWrapper.proj. This build file is shown below.

<Project DefaultTargets="GetOutputs" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>

    <ProjectFile>$(MSBuildProjectDirectory)\WindowsApplication1.csproj</ProjectFile>

  </PropertyGroup>

 

  <Target Name="GetOutputs">

    <MSBuild Projects="$(MSBuildProjectFullPath)"

             Properties="ImportProjectFile=true" Targets="Build">

      <Output ItemName="ProjectOutputs" TaskParameter="TargetOutputs"/>

    </MSBuild>

    <Message Text="%0a%0dProjectOutputs:%0a%0d    @(ProjectOutputs,'%0a%0d    ')" />

  </Target>

 

  <!--

    These elements will only be processed when in the context of the

    above Target. This is because of the Condition constraint on these items

  -->

  <Import Project="$(ProjectFile)" Condition="'$(ImportProjectFile)'=='true'"/>

  <!--

    Here we need to override the Build target with our own that will

    generate the correct results.

    -->

  <Target Name="Build"

        Condition="'$(ImportProjectFile)'=='true'"

        DependsOnTargets="$(BuildDependsOn)"

        Outputs="@(AllOutputs->'%(FullPath)')">

   

    <CreateItem Include="$(OutputPath)\**\*">

      <Output ItemName="AllOutputs" TaskParameter="Include"/>

    </CreateItem>

   

    <Message Text="Custom build invoked!" Importance="high"/>

   

  </Target>

</Project>

What this does is twofold:

1.      Defines the GetOutputs target which will drive the process

2.      Extends the behavior of the normal build to create the proper outputs

If you take a look at the Microsoft.Common.targets you will see that the default definition of the Build target is defined as follows:

    <Target

        Name="Build"

        Condition=" '$(_InvalidConfigurationWarning)' != 'true' "

        DependsOnTargets="$(BuildDependsOn)"

        Outputs="$(TargetPath)"/>

The purpose of this target is to define what targets it depends on and to create the outputs, by itself it doesn’t actually perform any action. Because of this we can override it in our buildWrapper.proj file. So the bottom half of the buildWrapper.proj file will conditionally import the project file and then conditionally re-define the Build target. Inside the new Build target we simply examine the output directory and create an item that contains all files that lie underneath it. In the image below you will see the result of this, specifically take a look at the region highlighted in red.

 

 

Now we can see that we have successfully achieved our goals.

Every time I describe this process I feel like it is still un-clear. If you have questions please let me know.

 

Sayed Ibrahim Hashimi

You can download all the files at http://www.sedodream.com/content/binary/OutExample.zip


(Edit: Updated MSBuild task usage to include Targets="Build")


msbuild | Visual Studio Wednesday, November 21, 2007 6:29:57 AM (GMT Standard Time, UTC+00:00)  #     | 
Monday, November 19, 2007

How would you enhance MSBuild?

The MSBuild team is trying to prioritize their duties for the next release of MSBuild. Because of this they have asked for feedback on how their customers (us) would enhance MSBuild. They are doing this by distributing a virtual $100 across a possible 11 items. Their list actually ends in an item numbered 12, but there is no number 10. Here is a brief overview of their list of options.

1. Higher Preformance
2. VC Support
3. Support for other project types
4. Convert VS solution file to MSBuild format
5. Better dependency tracking
6. Distributed Build
7. Inline Tasks – ie you write a script right in your MSBuild file instead of a compilied .NET task
8. Scoping for items/properties – ie items/properties can exist in a defined scope
9. Extensible functions – ie being able to add items like the Exists function
11. MSBuild debugger
12. MSBuild visualization 

Here is how I chose to distribute my $100. 

4   - $40
6   - $10
7   - $10
11 - $30
12 - $10

About #4: In my experience, you have 2 scenarios that are the correct way to create build scripts for products. Those are

1.      Create a custom MSBuild file that acts like a solution file

2.      Use a solution file to drive the build process

The only reason to go with 1 is because the solution file is not an actual MSBuild file, otherwise that wouldn’t even be an option. As for option 2, this is not ideal either because it is difficult to inject steps into how the solution build process works. All you can do is really extend what happens before/after the process.

As for the debugger, this is a tool that I have actually wanted to write myself for a long time, but never had the time. If there was a debugger for MSBuild, I think that more people would begin to adopt MSBuild as well as get a much better understanding of how it works.

It’s not often that teams from Microsoft ask for straight customer feature feedback, if you’re interested in future versions of MSBuild then you should definetly participate in this at: http://blogs.msdn.com/msbuild/archive/2007/11/17/how-would-you-spend-100-on-msbuild.aspx. 

Sayed Ibrahim Hashimi

msbuild | Visual Studio Monday, November 19, 2007 6:40:30 AM (GMT Standard Time, UTC+00:00)  #     |