Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MSBuild confusing behavior with Target Outputs & Returns

I'm trying to understand the logic behind Outputs & Returns parameters. And I found very confusing behavior in case of using Outputs & Returns attribute.

e.x. I created a project with 2 targets "Build" and "InternalTarget". The first target defines its output via Outputs attribute and the second defines output via Returns attribute. So If I call Build target via MSBuild task I can't get any output - it always empty! But if I comment out the second target (which is unused anyway), the output magically appears.

Why it happens???

example.proj

<Project ToolsVersion="4.0"
         DefaultTargets="Build"
         xmlns="http://schemas.microsoft.com/developer/msbuild/2003">    
    <Target Name="Build" 
            Outputs="Build executed">
        <Message Text="Build executing ..." />
    </Target>

    <Target Name="_InternalTarget"
            Returns="_InternalTarget executed">
        <Message Text="_InternalTarget executing ..." />
    </Target>
</Project>

build.proj

<Project ToolsVersion="4.0"
         DefaultTargets="Build"
         xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemGroup>
        <_ProjectsToBuild Include="example.proj" />
    </ItemGroup>

    <PropertyGroup>
        <_ProjectsToBuildProperties>
            Configuration=Debug;
        </_ProjectsToBuildProperties>
    </PropertyGroup>

    <Target Name="Build">
        <MSBuild Targets="Build"
                 Projects="@(_ProjectsToBuild)"
                 Properties="$(_ProjectsToBuildProperties)">
            <Output TaskParameter="TargetOutputs"
                    ItemName="_ProjectsToBuildOutput" />
        </MSBuild>

        <Message Text="Output: @(_ProjectsToBuildOutput)." />
    </Target>    
</Project>

Output

Build started 3/4/2015 12:41:21 PM.
Project "C:\Development\Sources\MSBuildReturnsExample\build.proj" on node 1 (default targets).
Project "C:\Development\Sources\MSBuildReturnsExample\build.proj" (1) is building "C:\Development\Sources\MSBuildReturn
sExample\example.proj" (2) on node 1 (Build target(s)).
Build:
  Build executing ...
Done Building Project "C:\Development\Sources\MSBuildReturnsExample\example.proj" (Build target(s)).

Build:
  Output: .
Done Building Project "C:\Development\Sources\MSBuildReturnsExample\build.proj" (default targets).


Build succeeded.
    0 Warning(s)
    0 Error(s)
like image 897
Andir Avatar asked Mar 04 '15 09:03

Andir


1 Answers

From Target element documentation, Remarks section:

Before MSBuild 4, Target returned any items that were specified in the Outputs attribute. To do this, MSBuild had to record these items in case tasks later in the build requested them. Because there was no way to indicate which targets had outputs that callers would require, MSBuild accumulated all items from all Outputs on all invoked Targets. This lead to scaling problems for builds that had a large number of output items.

If the user specifies a Returns on any Target element in a project, then only those Targets that have a Returns attribute record those items.

So based on the above, if you expect to see "Output: Build executed." you should modify the example.proj to be:

<Project ToolsVersion="4.0"
         DefaultTargets="Build"
         xmlns="http://schemas.microsoft.com/developer/msbuild/2003">    
    <Target Name="Build" 
            Returns="Build executed">
        <Message Text="Build executing ..." />
    </Target>

    <Target Name="_InternalTarget"
            Returns="_InternalTarget executed">
        <Message Text="_InternalTarget executing ..." />
    </Target>
</Project>
like image 81
Michal Hosala Avatar answered Oct 28 '22 23:10

Michal Hosala