14

How do I keep values defined in one build target alive in other targert? If PropertyGroup is not the write MsBuild entity I should use here, what is? ReleaseDir is printed ok in "Package" target, but is empty in "DoPackage"

<Target Name="Package">
  <PropertyGroup>
    <ReleasesDir>c:\tmp</ReleasesDirBase>
  </PropertyGroup>
  <Message Text="$(ReleaseDir)"/>
  <CallTarget Targets="DoPackage" Condition="!Exists('$(ReleaseDir)')"/>
</Target>

<!-- Do the acutal packaging -->
<Target Name="DoPackage">
  <Message Text="Creating package in '$(ReleaseDir)'"/>
  <Error Condition="'$(ReleaseDir)' == ''" Text="No ReleaseDir defined"/>
  <MakeDir Directories="$(ReleaseDir)"/>
  ...
</Target>
ripper234
  • 222,824
  • 274
  • 634
  • 905

3 Answers3

23

There is a well known issue with properties and the CallTarget task. You should use DependsOnTargets instead.

<Target Name="Package">
  <PropertyGroup>
    <ReleasesDir>c:\tmp</ReleasesDir>
  </PropertyGroup>
  <Message Text="$(ReleasesDir)"/>
</Target>

<Target Name="PrePackage" DependsOnTargets="Package">
  <CallTarget Targets="DoPackage" Condition="!Exists('$(ReleasesDir)')"/>
</Target>

<!-- Do the actual packaging -->
<Target Name="DoPackage" DependsOnTargets="Package">
  <Message Text="Creating package in '$(ReleasesDir)'"/>
  <Error Condition="'$(ReleasesDir)' == ''" Text="No ReleaseDir defined"/>
  <MakeDir Directories="$(ReleasesDir)"/>
</Target>
Julien Hoarau
  • 48,964
  • 20
  • 128
  • 117
  • I would like DoPackage to run only if ReleaseDir exists (I used the Condition in CallTarget to achieve this). Can I achieve this using DependsOnTarget? – ripper234 Sep 03 '09 at 15:02
  • Now that I actually read the link you posted, the solution is simple - I wrote a separate task called 'DefineProperties', and after it's finished the properties are ... defined. Thanks. – ripper234 Sep 03 '09 at 15:15
  • I have a feeling the reason properties are not defined until a task has finished is because of parallel execution. MsBuild doesn't guarantee anything mid-target because this way it can execute the subtargets in parallel. – ripper234 Sep 03 '09 at 16:36
  • 1
    In this example the DoPackage Target does not need the DependsOnTargets="Package" because the PrePackage Target already has DependsOnTargets="Package". It might be useful to remove this extra DependsOnTargets="Package" if your build file needs to run DoPackage with different values of ReleasesDir. Like so: c:\tmp2 – Joel Jun 05 '12 at 17:01
1

If one wants to pass a property to a target, the MSBuild task can be useful. This is the only way to call a target multiple times with different property values, but it does not allow passing in items or item groups. See this comment in the thread that Julien links to.

...[C]all the MSBuild target on it again, this time passing in the required properties. This bypasses incremental building ..., but has many limitations, namely you can't pass in items and you must specify which properties that need to get passed.

Here is what your code snippet would look like using the MSBuild task:

<Target Name="Package">
  <PropertyGroup>
    <ReleasesDir>c:\tmp</ReleasesDir>
  </PropertyGroup>
  <Message Text="$(ReleaseDir)"/>
  <MSBuild Projects="$(MSBuildProjectFile)" Targets="DoPackage" Properties="ReleaseDir=$(ReleaseDir)" /> 
</Target>

<!-- Do the acutal packaging -->
<Target Name="DoPackage">
  <Message Text="Creating package in '$(ReleaseDir)'"/>
  <Error Condition="'$(ReleaseDir)' == ''" Text="No ReleaseDir defined"/>
  <MakeDir Directories="$(ReleaseDir)"/>
  ...
</Target>

This technique is useful if you want to use the target as a subroutine, which you can call multiple times with different parameter values. For example, to call a build process for several product configurations.

JJS
  • 6,431
  • 1
  • 54
  • 70
Dan Solovay
  • 3,134
  • 3
  • 26
  • 55
1

It might not be the cleanest way to solve this problem, but if some one still wants to use CallTarget on the build file, he/she must define the PropertyGroup in another Target, the following is the solution to this weird problem.

<Target Name="DebugBuild" DependsOnTargets="DebugBuildProp">
  <CallTarget Targets="CompileSolution"/>
</Target>
<Target Name="DebugBuildProp">
  <PropertyGroup>
    <Configuration>Debug</Configuration>
  </PropertyGroup>
</Target>
<Target Name="CompileSolution">
   <Message Text="$(Configuration)" />
</Target>
Hossein Shahdoost
  • 1,692
  • 18
  • 32
  • If `DeugBuild` updates the value of `Configuration`, `CompileSolution` still won't use the new value. – makhdumi Oct 29 '15 at 23:53
  • @Al-Muhandis It actually does, I don't know why but if you define the Property on the dependency target all the other targets which you call using CallTarget would have the value. Try it yourself – Hossein Shahdoost Oct 30 '15 at 06:00