18

I want to declare three properties in my MSBuild file and overwrite one property with the value of another (depending on the target being called), but can't figure out how to do this. My build file looks something like this:

<PropertyGroup>
   <DeployPath_TEST>\\test-server-path\websites\mysite</DeployPath_TEST>
   <DeployPath_LIVE>\\live-server-path\websites\mysite</DeployPath_LIVE>
   <DeployPath></DeployPath>
</PropertyGroup>

<Target Name="Deploy-TEST">
   <PropertyGroup>
      <DeployPath>$(DeployPath_TEST)</DeployPath>
   </PropertyGroup>
   <CallTarget Targets="Deploy-Sub"/>
</Target>

<Target Name="Deploy-LIVE">
   <PropertyGroup>
      <DeployPath>$(DeployPath_TEST)</DeployPath>
   </PropertyGroup>
   <CallTarget Targets="Deploy-Sub"/>
</Target>

<Target Name="Deploy-Sub">
   <Message Text="Deploying to $(DeployPath)"/>
   <MSBuild Projects="MySolution.csproj" Targets="Rebuild" />

   <ItemGroup>
     <MyFiles Include="**\*"/>
   </ItemGroup>

   <Copy SourceFiles="@(MyFiles)" 
         DestinationFiles="@(MyFiles->'$(DeploymentPath)\%(RecursiveDir)%(FileName)%(Extension)')"/>

</Target>

At the moment I'm trying to re-declare the property setting it's value accordingly, but this isn't working.

Matthew Dresser
  • 11,273
  • 11
  • 76
  • 120

4 Answers4

17

Mehmet is right about how to set a property value from another property, but there is a bug/feature in MSBuild which means that if you call CreateProperty and CallTarget in the same Target, your new property will not be globally available to other targets (described here).

So here is the final solution to the problem:

<PropertyGroup>
   <DeployPath_TEST>\\test-server-path\websites\mysite</DeployPath_TEST>
   <DeployPath_LIVE>\\live-server-path\websites\mysite</DeployPath_LIVE>
   <DeployPath></DeployPath>
</PropertyGroup>

<Target Name="SetDeployPath-TEST">
  <CreateProperty Value="$(DeployPath_TEST)">
    <Output TaskParameter="Value" PropertyName="DeployPath"/>
  </CreateProperty>
</Target>

<Target Name="Deploy-TEST">
   <CallTarget Targets="SetDeployPath-TEST"/>
   <CallTarget Targets="Deploy-Sub"/>
</Target>

<Target Name="Deploy-Sub">
  <Message Text="Deploying to $(DeployPath)"/>
  <MSBuild Projects="MySolution.csproj" Targets="Rebuild" />

  <ItemGroup>
    <MyFiles Include="**\*"/>
  </ItemGroup>

  <Copy SourceFiles="@(MyFiles)" 
     DestinationFiles="@(MyFiles->'$(DeploymentPath)\%(RecursiveDir)%(FileName)%(Extension)')"/>

</Target>
Matthew Dresser
  • 11,273
  • 11
  • 76
  • 120
  • Thank you, that was usefull. I am still somewhat new to batching and MS doesn't do a good job explaining related concepts in my opinion. One more note. I actually got away without creating a global property; that narrows the context a bit so it is easier to read the code: Target A calls targets B and C. B sets a list property and C does batching on the value of that property. as mdresser noted setting the property in the same target did not work. Hope it helps someone; I've spent 3-4 hours trying to understand this before I found this post. – AlexeiOst Mar 01 '13 at 14:45
  • One more point, you say you get notification that only Cat has changed, have you tried to save the changes? Does it work? (I doubt it) – Paul Zahra Feb 04 '15 at 16:06
9

You can use CreateProperty task to overwrite the value of an existing property.

<Target Name="Deploy-LIVE">   
  <CreateProperty Value="$(DeployPath_LIVE)">
    <Output PropertyName="DeployPath" TaskParameter="Value"/>
  </CreateProperty>
  <CallTarget Targets="Deploy-Sub"/>
</Target>
Mehmet Aras
  • 5,284
  • 1
  • 25
  • 32
  • 1
    This doesn't seem to work for me. I can use the value of the DeployPath property immediately after the CreateProperty block, but it looses it's value within the Deploy-Sub target. – Matthew Dresser Sep 02 '09 at 11:51
  • 2
    Haa, just found this article http://weblogs.asp.net/bhouse/archive/2006/03/20/440648.aspx describing a bug/feature with the CreateProperty task. – Matthew Dresser Sep 02 '09 at 11:57
  • Interesting I did not know about this bug. I used CreateProperty before but apparently nothing needed to access the overwritten property outside of the target that overwrote it. Thanks. – Mehmet Aras Sep 02 '09 at 12:18
4

I typically avoid the CallTarget task. Much better is to use target dependencies.

Sayed Ibrahim Hashimi
  • 43,864
  • 17
  • 144
  • 178
  • Could you elaborate a little? Why is it better for you? – Haymo Kutschbach Jan 08 '15 at 08:39
  • 3
    @HaymoKutschbach because of this bug http://sedodream.com/PermaLink,guid,dd6cb1db-c0e4-47f7-ad84-6e59ff6b03d0.aspx and I think CallTarget goes against the mental model of how msbuild should be used. MSBuild is declarative, CallTarget is very imperative. With that said there are some cases when its needed, but u should prefer DependsOn before CallTarget. – Sayed Ibrahim Hashimi Jan 09 '15 at 01:31
  • Thanks! I agree and it seems the msbuild team encourages DependsOn as well for enabling properties to be set globally in imported projects. – Haymo Kutschbach Jan 09 '15 at 10:11
0

Besides, you can use following way:

<PropertyGroup>
   <DeployPath_TEST>\\test-server-path\websites\mysite</DeployPath_TEST>
   <DeployPath_LIVE>\\live-server-path\websites\mysite</DeployPath_LIVE>
   <DeployPath></DeployPath>
</PropertyGroup>

<Target Name="SetDeployPath-TEST">
  <CreateProperty Value="$(DeployPath_TEST)">
    <Output TaskParameter="Value" PropertyName="DeployPath"/>
  </CreateProperty>
</Target>

<Target Name="Deploy-Sub" DependsOnTargets="SetDeployPath-TEST">
  <Message Text="Deploying to $(DeployPath)"/>
  <MSBuild Projects="MySolution.csproj" Targets="Rebuild" />

  <ItemGroup>
    <MyFiles Include="**\*"/>
  </ItemGroup>

  <Copy SourceFiles="@(MyFiles)" 
     DestinationFiles="@(MyFiles->'$(DeploymentPath)\%(RecursiveDir)%(FileName)%(Extension)')"/>

</Target>