MSBuild: how to create a global property? - msbuild

When running MSBuild, anything passed in on the command line using /p:MyProp=value is accessible from every MSBuild script invoked (via the MSBuild task) from the main script. How can I define a property that is similarly accessible from every script, in a task?
For example:
Script1.proj:
[...]
<Target Name="Test">
<MSBuild Projects="Script2.proj"/>
<Message Text="Script1, GlobalProp1=$(GlobalProp1)"/>
<Message Text="Script1, GlobalProp2=$(GlobalProp2)"/>
</Target>
Script2.proj:
[...]
<Target Name="Test">
<!-- ??? Set GlobalProp2 = VALUE2 ??? -->
<Message Text="Script2, GlobalProp1=$(GlobalProp1)"/>
<Message Text="Script2, GlobalProp2=$(GlobalProp2)"/>
</Target>
If run like this: msbuild Script1.proj /p:GlobalProp1=VALUE1 the above scripts produce the following output:
Script2, GlobalProp1=VALUE1
Script2, GlobalProp2=
Script1, GlobalProp1=VALUE1
Script1, GlobalProp2=
I'd like Script2.proj to set GlobalProp2 somehow to produce the following output:
Script2, GlobalProp1=VALUE1
Script2, GlobalProp2=VALUE2
Script1, GlobalProp1=VALUE1
Script1, GlobalProp2=VALUE2
Is this possible, and if so, how?
I've seen suggestions to use environment variables, but it seems that the only situation in which that helps is when the variable is set in the parent script, and the child script is invoked using the Exec task (otherwise the env.variable change has no effect).

Since you are using the MSBuild task you have to pass the desired properties into the call using the Properties attribute so you should change you example to.
<Target Name="Test">
<MSBuild Projects="Script2.proj"
Properties="GlobalProp1=$(GlobalProp1);GlobalProp2=$(GlobalProp2)"
/>
<Message Text="Script1, GlobalProp1=$(GlobalProp1)"/>
<Message Text="Script1, GlobalProp2=$(GlobalProp2)"/>
</Target>
The properties must be explicitly passed, this is by design.
If you want a tighter integration then instead of using the MSBuild task you should just import the file(s) which will create 1 logical script.

You may use rsp file to define global properties. These properties are visible to child projects.

Related

Pass list item to Properties when calling reusable msbuild target

I'm trying to create a reusable Target in msbuild, following the basic model outlined in How to invoke the same msbuild target twice?
I'm stuck trying to pass a property that I want interpreted as a list. I haven't found an example online that deals with this situation. As I understand it, the problem is that Properties is already treated as a list item, so it doesn't like having a list item passed in as well. Is there a way to get msbuild to pack and unpack the list correctly here?
Msbuild is complaining with:
error MSB4012: The expression "FilesToZip=#(Scripts)" cannot be used in this context. Item lists cannot be concatenated with other strings where an item list is expected. Use a semicolon to separate multiple item lists.
Here's an example caller:
<Target Name="BuildMigrationZip">
<MSBuild Projects="BuildZip.msbuild"
Targets="BuildZip"
Properties="FilesToZip=#(Scripts);OutputZipFile=$(MigrationPackageFilePath);OutputFolder=$(MigrationPackagePath);Flatten=true"/>
<Message Text="Created database migration zip: $(MigrationPackageFilePath)" Importance="high"/>
</Target>
And the base target:
<Target Name="BuildZip">
<MakeDir Directories="$(OutputFolder)"/>
<Zip Files="#(FilesToZip)"
ZipFileName="$(OutputZipFile)"
Flatten="$(Flatten)"
ParallelCompression="false" />
</Target>
I'm basically at the point of just going back to cut and paste for these, although I want to package up a number of zips here.
UPDATE: The same issue applies to setting Inputs on the reusable target. My question up to this point addresses the raw functionality, but it would be nice to keep dependencies working. So for example:
<Target Name="BuildZip"
Inputs="#(FilesToZip)"
Outputs="$(OutputZipFile)">
<MakeDir Directories="$(OutputFolder)"/>
<Zip Files="#(FilesToZip)"
ZipFileName="$(OutputZipFile)"
Flatten="$(Flatten)"
ParallelCompression="false" />
</Target>
They key is to pass the list around as a property. So when your Scripts list is defined as
<ItemGroup>
<Scripts Include="A"/>
<Scripts Include="B"/>
<Scripts Include="C"/>
</ItemGroup>
then you first convert it into a property (which just makes semicolon seperated items, but msbuild knows how to pass this via the Properties of the MSBuild target) then pass it to the target:
<Target Name="BuildMigrationZip">
<PropertyGroup>
<ScriptsProperty>#(Scripts)</ScriptsProperty>
</PropertyGroup>
<MSBuild Projects="$(MSBuildThisFile)" Targets="BuildZip"
Properties="FilesToZip=$(ScriptsProperty)" />
</Target>
(note I'm using $(MSBuildThisFile) here: you don't necessarily need to create seperate build files for every single target, in fact for small targets like yours it's much more convenient to put it in the same file)
Then in your destination target you turn the property into a list again:
<Target Name="BuildZip">
<ItemGroup>
<FilesToZipList Include="$(FilesToZip)"/>
</ItemGroup>
<Message Text="BuildZip: #(FilesToZipList)" />
</Target>
Output:
BuildZip: A;B;C
Update
When working with Inputs, you cannot pass #(FilesToZip) since that expands to nothing because is not a list: it's a property - which happens to be a number of semicolon-seperated strings. And as such, it is usable for Inputs you just have to expand it as the property it is i.e. $(FilesToZip):
<Target Name="BuildZip"
Inputs="$(FilesToZip)"
Outputs="$(OutputZipFile)">
...
</Target>
Output of second run:
BuildZip:
Skipping target "BuildZip" because all output files are up-to-date with respect to the input files.

MSBuild script default property best practices explanation

In building an MSBuild script, I need to define a series of properties that are default but can be overridden when running the script. According to the following article, you should use a Conditional to default the property:
http://msdn.microsoft.com/en-us/library/ee240983.aspx
How Microsoft recommends:
<PropertyGroup>
<MyProperty Condition="'$(MyProperty)' == '' ">Default Value</MyProperty>
</PropertyGroup>
However, this behaves exactly the same way:
<PropertyGroup>
<MyProperty>Default Value Without Conditional</MyProperty>
</PropertyGroup>
So, if I have this Target and invoke it with either of the above, it has the same behavior:
<Target Name="DefaultsTest">
<Message Text="$(MyProperty)"></Message>
</Target>
Invocation:
msbuild build.xml /t:DefaultsTest /p:MyProperty="Overridden value"
Please explain the benefits of using the Condition attribute if you are only defaulting the same property that can be overridden from the invocation?
Update:
Here is a full simple config file to demonstrate: defaults.xml
<?xml version="1.0" encoding="utf-8" ?>
<Project DefaultTargets="DefaultsTest" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MyProperty Condition=" '$(MyProperty)' == '' ">MyProperty with Conditional</MyProperty>
<MyOtherProperty>MyOtherProperty without Conditional</MyOtherProperty>
</PropertyGroup>
<Target Name="DefaultsTest">
<Message Text="$(MyProperty)"></Message>
<Message Text="$(MyOtherProperty)"></Message>
</Target>
</Project>
This can be run simply as msbuild defaults.xml
or
msbuild defaults.xml /p:MyProperty="Changed Value" /p:MyOtherProperty="Changed as well"
You correctly noticed, that you can achieve behavior you want with unconditional assignment of property. The following project when built without /p: override on command line will produce Default Value. And when built using command msbuild myproj.proj /t:DefaultsTest /p:MyProperty=NewValue will produce NewValue.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MyProperty>Default Value</MyProperty>
</PropertyGroup>
<Target Name="DefaultsTest">
<Message Text="MyProperty=$(MyProperty)"></Message>
</Target>
</Project>
This is because any properties specified on MSBuild command line or provided as a parameter of task will be treated as Global Properties. For global properties, any assignment or modifications, either conditional or unconditional, are simply ignored -- global properties will remain constant throughout lifetime of the project execution.
The only difference in behavior between conditional assignment and unconditional will be if you use TreatAsLocalProperty attribute.
For example, consider the following project:
<Project TreatAsLocalProperty="Prop1;Prop2" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Prop1>Prop1 Default Value</Prop1>
<Prop2 Condition="$(Prop2) == ''">Prop2 Default Value</Prop2>
</PropertyGroup>
<Target Name="DefaultsTest">
<Message Text="Prop1=$(Prop1)"></Message>
<Message Text="Prop2=$(Prop2)"></Message>
</Target>
</Project>
Two properties -- Prop1 and Prop2 are both declared as local in the Project element. Prop1 is assigned unconditionally, while Prop2 is assigned using non-empty condition. Executing build command:
msbuild b.proj /t:DefaultsTest /p:Prop1=NewValue1 /p:Prop2=NewValue2
will produce output:
Prop1=Prop1 Default Value
Prop2=NewValue2
This means that in general case (if you are not absoluterly sure if property will be global or local), it is safer to use conditional assignment of the default value, because it works always.

Can I be sure that i receive correct item if I defined item in one msbuild target and get it in other one?

I have two different targets which are invoked with one collection of properties via msbuild task. In one target I define ItemGroup and in other one I recieve it. I invoke targets in the next way:
<MsBuild Projects="deploypkg.project" Properties="CurrentSite=%(SitesName.Identity)" Targets="TargetA"/>
<MsBuild Projects="deploypkg.project" Properties="CurrentSite=%(SitesName.Identity)" Targets="TargetB"/>
When in TargetB I refer defined in TargetA ItemGroup i get items defined only for current site(input property). It's exactly what i need but I'm not sure I can rely on it, because I found nothing about this possibility.
You are not sure, you can create test.project to test it. Something like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Target Name="TargetA">
<Message Text="TargetA: CurrentSite = $(CurrentSite)"/>
</Target>
<Target Name="TargetB">
<Message Text="TargetB: CurrentSite = $(CurrentSite)"/>
</Target>
</Project>
Use test.project in your script:
<MsBuild Projects="test.project" Properties="CurrentSite=%(SitesName.Identity)" Targets="TargetA"/>
<MsBuild Projects="test.project" Properties="CurrentSite=%(SitesName.Identity)" Targets="TargetB"/>

Trouble with outputting MSBuild variables

I'm trying to output the variable from one target, into the parent target which started it. For example,
Target 1 simply calls the task in file 2 and is supposed to be able to use the variable set within that. However, I just can't seem to get it to work (wrong syntax perhaps?). Target 1 looks like this:
<Target Name="RetrieveParameter">
<MSBuild Projects="$(MSBuildProjectFile)" Targets="ObtainOutput" />
<Message Text="Output = $(OutputVar)" />
</Target>
Target 2 is where it reads in the value of the text file and sets it to the property and sets the variable 'OutputVar' to match. This is supposed to be returned to the parent.
<Target Name="ObtainOutput" Outputs="$(OutputVar)">
<ReadLinesFromFile File="output.txt">
<Output TaskParameter="Lines"
PropertyName="OutputVar" />
</ReadLinesFromFile>
</Target>
I'm quite new to MSBuild tasks, so it could well be something obvious. All I want to do is set a variable in one task, and then have that available in the parent task which called it.
Julien has given you the right answer, but not explained why it is correct.
As you're new to MSBuild tasks, I'll explain why Julien's answer is correct.
All tasks in MSBuild have parameters - you will know them as the attributes that you put on the task. Any of these parameters can be read back out by placing an Output element within it. The Output element has three attributes that can be used:
TaskParameter - this is the name of the attribute/parameter on the task that you want to get
ItemName - this is the itemgroup to put that parameter value into
PropertyName - this is the name of the property to put that parameter value into
In your original scripts, you were invoking one from the other. The second script will execute in a different context, so any property or itemgroup it sets only exists in that context. Therefore when the second script completes, unless you have specified some Output elements to capture values they will be discarded.
Note that you can put more than one Output element under a task to capture multiple parameters or just set the same value to multiple properties/itemgroups.
You have to use TargetOutputs of the MSBuild task:
<Target Name="RetrieveParameter">
<MSBuild Projects="$(MSBuildProjectFile)" Targets="ObtainOutput">
<Output TaskParameter="TargetOutputs" ItemName="OutputVar"/>
</MSBuild>
<Message Text="Output = #(OutputVar)" />
</Target>
(More information on MSBuild task.)

What is the difference between 'DependsOnTargets' and 'AfterTargets'?

What is the difference between DependsOnTargets and AfterTargets?
I can not distinguish these two.
DependsOnTargets
Defines the targets that must be executed before the target can be executed.
<Target Name="DependsOn" DependsOnTargets="DependencyTarget1;DependencyTarget2">
<Message Text="Target : DependsOn"/>
</Target>
<Target Name="DependencyTarget2">
<Message Text="Target : DependencyTarget2"/>
</Target>
<Target Name="DependencyTarget1">
<Message Text="Target : DependencyTarget1"/>
</Target>
Output
> Target : DependencyTarget1
> Target : DependencyTarget2
> Target : DependsOn
BeforeTargets and AfterTargets (Only available in MSBuild 4)
Indicates that the target should run before or after the specified target or targets.
<Target Name="BeforeAndAfter">
<Message Text="Target : BeforeAndAfter"/>
</Target>
<!-- BeforeTarget1 will run BEFORE target "BeforeAndAfter" -->
<Target Name="BeforeTarget" BeforeTargets="BeforeAndAfter">
<Message Text="BeforeTarget run before : BeforeAndAfter"/>
</Target>
<!-- BeforeTarget1 will run AFTER target "BeforeAndAfter" -->
<Target Name="AfterTarget" AfterTargets="BeforeAndAfter">
<Message Text="AfterTarget run after : BeforeAndAfter"/>
</Target>
Output
> BeforeTarget run before : BeforeAndAfter
> Target : BeforeAndAfter
> AfterTarget run after : BeforeAndAfter
If you have multiples targets that should run before or after the same specified target, they will be executed in declaration order :
<Target Name="BeforeAndAfter">
<Message Text="Target : BeforeAndAfter"/>
</Target>
<!--
BOTH BeforeTarget1 and BeforeTarget2 should run before target "BeforeAndAfter"
-->
<Target Name="BeforeTarget1" BeforeTargets="BeforeAndAfter">
<Message Text="BeforeTarget1 run before : BeforeAndAfter"/>
</Target>
<Target Name="BeforeTarget2" BeforeTargets="BeforeAndAfter">
<Message Text="BeforeTarget2 run before : BeforeAndAfter"/>
</Target>
BeforeTargets and AfterTargets could be use to extend existing build process.
For example, with this attributes you can easily execute a target before CoreCompile (defines in Microsoft.CSharp.targets). Without that you'll have to override the property CoreCompileDependsOn.
Without AfterTargets you have no way to easily execute a target after another one if no extension point is defined (CallTarget at the end of the target with a property that you can override)
DependsOnTargets, BeforeTargets and AfterTargets execution order?
When DependsOnTargets, BeforeTargets and AfterTargets are used on the same target, the order of execution is :
DependsOnTargets
BeforeTargets
The target
AfterTargets
<Target Name="MainTarget" DependsOnTargets="DefaultDependsOn">
<Message Text="Target : MainTarget"/>
</Target>
<Target Name="DefaultDependsOn">
<Message Text="Target : DefaultDependsOn"/>
</Target>
<Target Name="DefaultBeforeTarget" BeforeTargets="MainTarget">
<Message Text="Target : DefaultBeforeTarget"/>
</Target>
<Target Name="DefaultAfterTarget" AfterTargets="MainTarget">
<Message Text="Target : DefaultAfterTarget"/>
</Target>
Output
> Target : DefaultDependsOn
> Target : DefaultBeforeTarget
> Target : MainTarget
> Target : DefaultAfterTarget
More succinctly from this GitHub issue on Microsoft Docs:
<Target Name="x" DependsOnTargets="y" /> means:
If something wants to run x, y must run first.
<Target Name="a" AfterTargets="b" /> means:
If something runs b, then run a after it.
While the other answers previously provided are correct, I think they failed to mention what I think is the primary benefit of AfterTargets over DependsOnTargets.
DependsOnTargets has been around from the beginning of MSBuild. The problem with DependsOnTargets, is that it requires a target author to explicitly allow for extensibility. This is done by defining a property that is used as the DependsOnTargets value, as follows:
<PropertyGroup>
<SomeTargetDependsOnTargets>
Dependency1;Dependency2
</SomeTargetDependsOnTargets>
</PropertyGroup>
<Target Name="SomeTarget" DependsOnTargets="$(SomeTargetDependsOnTargets)">
...
</Target>
You could then add a dependency by modifying the SomeTargetDependsOnTargets property as follows:
<SomeTargetDependsOnTargets>
$(SomeTargetDependsOnTargets);Dependency3
</SomeTargetDependsOnTargets>
The problem with this design, is that if the author had simply inlined Dependency1;Dependency2 rather than extracting it into a property, there would be no way to externally modify it to allow for customization.
AfterTargets, on the other hand, doesn't require the original target author to have explicitly extracted the DependsOnTargets value into a property to allow for extensibility.
I think the answer is much simpler. The effect of DependsOnTargets and AfterTargets is essentially the same. The reason for BeforeTargets & AfterTargets (from the Microsoft Documentation):
This lets the project author extend an existing set of targets without
modifying them directly.
So if you have an existing target B and you want to add a new target A that must execute first, you have two choices:
Modify target B to read: DependsOnTargets="A".
Modify target A to read: BeforeTargets="B".
If you can't modify B (e.g. it's an existing Microsoft target), that's when you need BeforeTargets.
One more difference, that is mentioned in another answer is that
BeforeTargets and AfterTargets are run regardless of the Condition whereas DependsOnTargets (and target itself) are skipped when Condition evaluates to false"
In below code second target is executed even though first target is not executed:
<Target Name="FirstTarget" AfterTargets="PostBuildEvent" Condition="'False'">
<Message Text="This target is never executed" />
</Target>
<Target Name="SecondTarget" AfterTargets="FirstTarget">
<Message Text="SecondTarget is executed" />
</Target>
In below code second target is not executed:
<Target Name="FirstTarget" AfterTargets="PostBuildEvent" Condition="'False'">
<Message Text="This target is never executed" />
</Target>
<Target Name="SecondTarget" DependsOnTargets="FirstTarget">
<Message Text="SecondTarget is executed" />
</Target>
DependsOnTarget
Let's assume that you have two tasks:
Build Project
Copy all content.
You can start your build by executing task 2 and then in the task declaration define its dependencies. So if you define that task 2 depends on task 1, the build process will start and execute task 1 and then 2.
AfterTargets
Much simpler: it means only tasks which are execute after other targets. So taking the example from above - after Task 1 - build project execute task 2.
I hope this helps
One more difference between AfterTargets and DependsOnTargets.
When you have a target dependency chain/graph, be aware that putting 1+ targets into BeforeTargets or AfterTargets works like "any", and not like "all".
It works like saying "if any of them has run...", and not "if all of them has run..."
Let's say you want to achive this target dependency chain: A1 -> A2 -> A3.
The following will not work:
<Target Name="A1" BeforeTargets="Compile">
<Message Text="A1" />
</Target>
<Target Name="A3" AfterTargets="A1;A2">
<Message Text="A3" />
</Target>
<Target Name="A2" AfterTargets="A1">
<Message Text="A2" />
</Target>
Output will be:
A1
A3
A2
Because, as already mentioned here, the above only means:
 
<Target Name="A1" BeforeTargets="Compile">
If Compile wants to run, run A1 before it.
 
<Target Name="A3" AfterTargets="A1;A2">
If A1 run, run A3 after it.
If A2 run, run A3 after it.
 
<Target Name="A2" AfterTargets="A1">
If A1 run, run A2 after it.
 
Thus, running A1 will trigger running A3.
Note, that the declaration order matters!
We wanted to say A3 depends on A1 and A2, but the above did not mean that.
Viewing from another aspect, declaring this:
<Target Name="A3" AfterTargets="A1;A2">
Works like "if any of the AfterTargets has run, run me."
If A1 or A2 run, run A3
 
To safely achive target dependency chain A1 -> A2 -> A3, I would use DependsOnTargets this way:
<Target Name="B1" BeforeTargets="Compile">
<Message Text="B1" />
</Target>
<Target Name="B3" DependsOnTargets="B1;B2" BeforeTargets="Compile">
<Message Text="B3" />
</Target>
<Target Name="B2" DependsOnTargets="B1" BeforeTargets="Compile">
<Message Text="B2" />
</Target>
Declaring all the dependencies of a Target in DependsOnTargets creates the graph/chain we wanted.
Note, that we need to hook in all the targets into the build here in a plus step, because DependsOnTargets does not do that (unlike AfterTargets). (The above code uses BeforeTargets="Compile" in all Targets to achive this)
However, since we declared the dependencies between our Targets, the declaration order (and thus the hook-in order) does not matter.
There are two mainly differences:
First: Targets specified in the dependsOnTargets attribute are
executed before those in AfterTargets.
Second: If one of the targets specified in AfterTargets attribute got executed
this target get also executed after(I mean the target
where you specify the AfterTargets attribute), which is not true for
those in DependsOnTargets.