Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MSBuild Property Scope

Once again I'm battling MSBuild. I want to have a property value defined with a root path. As part of the build, the path will get updated with version information. However, MSBuild seems to have its own scoping rules that seem completely backwards. Take this first example:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">

  <PropertyGroup>
    <MyPath>\\server\folder</MyPath>
  </PropertyGroup>

  <Target Name="Main">
    <Message Text="In Main Before - MyPath = $(MyPath)"/>
    <CallTarget Targets="Task1" />
    <CallTarget Targets="Task2" />
    <CallTarget Targets="Task3" />
    <Message Text="In Main After - MyPath = $(MyPath)"/>
  </Target>

  <Target Name="Task1">
    <PropertyGroup>
        <MyPath>$(MyPath)\version5</MyPath>
    </PropertyGroup>
    <Message Text="In Task1 - MyPath = $(MyPath)"/>
  </Target>

  <Target Name="Task2">
    <Message Text="In Task2 - MyPath = $(MyPath)"/>
  </Target>

  <Target Name="Task3">
    <Message Text="In Task3 - MyPath = $(MyPath)"/>
  </Target>

</Project>

Here's the output with this command line: msbuild PropertyScopeTest1.proj /target:Main

Project "C:\Temp\PropertyScopeTest1.proj" on node 1 (Main target(s)).
Main:
  In Main Before - MyPath = \\server\folder
Task1:
  In Task1 - MyPath = \\server\folder\version5
Task2:
  In Task2 - MyPath = \\server\folder\version5
Task3:
  In Task3 - MyPath = \\server\folder\version5
Main:
  In Main After - MyPath = \\server\folder
Done Building Project "C:\Temp\PropertyScopeTest1.proj" (Main target(s)).

Now, here's a slightly different version setting the MyPath variable in the Main target:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">

  <PropertyGroup>
    <MyPath>\\server\path</MyPath>
  </PropertyGroup>

  <Target Name="Main">
    <Message Text="In Main Before - MyPath = $(MyPath)"/>
    <PropertyGroup>
        <MyPath>$(MyPath)\version5</MyPath>
    </PropertyGroup>
    <Message Text="In Main After PropertyGroup - MyPath = $(MyPath)"/>
    <CallTarget Targets="Task1" />
    <CallTarget Targets="Task2" />
    <CallTarget Targets="Task3" />
    <Message Text="In Main After - MyPath = $(MyPath)"/>
  </Target>

  <Target Name="Task1">
    <Message Text="In Task1 - MyPath = $(MyPath)"/>
  </Target>

  <Target Name="Task2">
    <Message Text="In Task2 - MyPath = $(MyPath)"/>
  </Target>

  <Target Name="Task3">
    <Message Text="In Task3 - MyPath = $(MyPath)"/>
  </Target>

</Project>

Here's the output with this command line: msbuild PropertyScopeTest2.proj /target:Main

Project "C:\Temp\PropertyScopeTest2.proj" on node 1 (Main target(s)).
Main:
  In Main Before - MyPath = \\server\path
  In Main After PropertyGroup - MyPath = \\server\path\version5
Task1:
  In Task1 - MyPath = \\server\path
Task2:
  In Task2 - MyPath = \\server\path
Task3:
  In Task3 - MyPath = \\server\path
Main:
  In Main After - MyPath = \\server\path\version5
Done Building Project "C:\Temp\PropertyScopeTest2.proj" (Main target(s)).

I've looked at other links on this site that are similar, but all seem to be calling the MSBuild task from within the MSBuild project file. All I want to do is update the path and have it available everywhere in the project. Any ideas?

like image 765
dprice Avatar asked Sep 23 '11 20:09

dprice


People also ask

How do I set MSBuild properties?

MSBuild lets you set properties on the command line by using the -property (or -p) switch. These global property values override property values that are set in the project file. This includes environment properties, but does not include reserved properties, which cannot be changed.

Where to put Directory build props?

You can add a new property to every project by defining it in a single file called Directory. Build. props in the root folder that contains your source. When MSBuild runs, Microsoft.

What is MSBuild target?

A target element can have both Inputs and Outputs attributes, indicating what items the target expects as input, and what items it produces as output. If all output items are up-to-date, MSBuild skips the target, which significantly improves the build speed. This is called an incremental build of the target.

What is build props file?

The “build. prop” file is a system file that contains build properties and settings. Some of the contents are specific to your device or your device's manufacturer, others vary by version of the operating system, but some are generic to all devices running the same version of Android as you are.


2 Answers

Building on sll's answer, making the target that sets the new path a dependency instead of using CallTarget will yield the expected behaviour:

<?xml version="1.0" encoding="utf-8"?> <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">    <PropertyGroup>     <MyPath>\\server\folder</MyPath>   </PropertyGroup>    <Target Name="Main" DependsOnTargets="SetMyPathProperty">     <Message Text="In Main Before - MyPath = $(MyPath)"/>     <CallTarget Targets="Task1" />     <Message Text="In Main After - MyPath = $(MyPath)"/>   </Target>    <Target Name="SetMyPathProperty">     <PropertyGroup>       <MyPath>$(MyPath)\version5</MyPath>     </PropertyGroup>   </Target>    <Target Name="Task1">     <Message Text="In Task1 - MyPath = $(MyPath)"/>   </Target>  </Project> 

Build Output:

Main:   In Main Before - MyPath = \\server\folder\version5 Task1:   In Task1 - MyPath = \\server\folder\version5 Main:   In Main After - MyPath = \\server\folder\version5 

Making SetMyPathProperty a dependency of Task1 instead of Main will result in identical behaviour to your PropertyScopeTest1.proj.

like image 107
Thomas Gerstendörfer Avatar answered Oct 01 '22 03:10

Thomas Gerstendörfer


This is a very interesting question which is investigated deeply with examples in following article: Scope of properties and item in an MSBuild script

Basically there are tricks with a local and global context switches across a target executions:

  • One instance of the Project class is created for the script and contains all the values of the properties and items in a global context.
  • When a target is executed, the global context is copied in a local context which will be used by the target.
  • A the target execution end, the local context updates are merged back to the global context.
  • Until a target execution is finished the local updates are not accessible to targets called using CallTarget or MSBuild tasks
like image 28
sll Avatar answered Oct 01 '22 05:10

sll