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?
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.
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.
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.
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With