7

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)
AndreyR
  • 294
  • 4
  • 15
  • 3
    Did you read https://msdn.microsoft.com/en-us/library/t50z2hka.aspx? `outputs` should be file names, `returns` should be item names. You are just entering strings, space seperated. Not sure what you expect to happen? – stijn Mar 05 '15 at 11:25
  • It's just for illustration. Nothing will be changed if I use appropriate File Names or Item Names. Whole confusion here, that if I add any target in the project file (even unused at all) with Returns attribute I will not get any outputs. If I just remove or comment this unused target I will get the output as expected ("Build executed"). – AndreyR Mar 05 '15 at 14:08
  • Hmm, good point indeed. No idea what is going on; using Returns instead of outputs on the build target in example.proj fixes the problem, or using Outputs on the _InternalTarget, but mixing both doesn't work for some reason. Interesting :P – stijn Mar 05 '15 at 15:33
  • 2
    @stijn Remarks section of the MSDN page you've linked is the key :) – Michal Hosala Mar 26 '15 at 15:34

1 Answers1

8

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>
Community
  • 1
  • 1
Michal Hosala
  • 5,570
  • 1
  • 22
  • 49
  • Yes, that's it! The next paragraph also explicitly mention that specific behavior of target Returns. Thanks! – AndreyR Mar 27 '15 at 20:00