Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using MSBuild PropertyGroup outside of Target block

Tags:

msbuild

I have a project files collection:

<ItemGroup>
  <ApplicationToDeploy
    Include="Frontend.WebSite.csproj;11.WebServices.csproj;22.WebServices.csproj"/>
  <ApplicationToDeploy
    Include="33.WebServices.csproj;44.WebServices.csproj;Workflow55Svc.csproj"/>
</ItemGroup>

I'm trying to get collection of .config-files of these projects:

<Target Name="111">
  <PropertyGroup>
    <Cfgs>@(ApplicationToDeploy->'%(RootDir)%(Directory)*.config')</Cfgs>
  </PropertyGroup>

  <ItemGroup>
    <InputConfigs Include="$(Cfgs)" />
  </ItemGroup>

  <Message Text="Cfgs: @(InputConfigs)"/>
</Target>

Inside the Target block all works fine (I see collection of Web.Configs, App.Configs, Log4net.Configs etc.):

Cfgs: C:\Sources\WebServices\11\WebServices\11.WebServices\Web.config;C:\Sources\WebServices\22\WebServices\22.WebServices\web.log4net.config;C:\Sources\WebServices\33\WebServices\33.WebServices\web.environment.config

But I want to initialize this ItemGroup outside of the Target block. Like this:

<PropertyGroup>
  <Cfgs>@(ApplicationToDeploy->'%(RootDir)%(Directory)*.config')</Cfgs>
</PropertyGroup>

<ItemGroup>
  <InputConfigs Include="$(Cfgs)" />
</ItemGroup>

<Target Name="111">
  <Message Text="Cfgs: @(InputConfigs)"/>
</Target>

And when I do this outside of the Target block I get this:

Cfgs: C:\Sources\WebServices\11\WebServices\11.WebServices\*.config;C:\Sources\WebServices\22\WebServices\22.WebServices\*.config;C:\Sources\WebServices\33\WebServices\33.WebServices\*.config

I don't understand what's happens. Is it possible to get the same result outside Target block?

like image 937
Artem Bokov Avatar asked Dec 24 '12 11:12

Artem Bokov


1 Answers

I don't understand what's happens.

This behavior is an effect of the MSBuild evaluation order:

During the evaluation phase of a build:

  • Properties are defined and modified in the order in which they appear. Property functions are executed. Property values in the form $(PropertyName) are expanded within expressions. The property value is set to the expanded expression.
  • Item definitions are defined and modified in the order in which they appear. Property functions have already been expanded within expressions. Metadata values are set to the expanded expressions.
  • Item types are defined and modified in the order in which they appear. Item values in the form @(ItemType) are expanded. Item transformations are also expanded. Property functions and values have already been expanded within expressions. The item list and metadata values are set to the expanded expressions.

During the execution phase of a build:

  • Properties and items that are defined within targets are evaluated together in the order in which they appear. Property functions are executed and property values are expanded within expressions. Item values and item transformations are also expanded. The property values, item type values, and metadata values are set to the expanded expressions."

There's another key point on that link "(...) The string expansion is dependent on the build phase.".

You're using the property 'Cfgs' to recursively map your projects' folders AND to define a wildcard to config files (*.config). When you define 'Cfgs' INSIDE the target, the InputConfigs receives the expanded value of Cfgs (semicolon-separated string list of folders), and just resolve the wildcards. On the other hand, when you define 'Cfgs' OUTSIDE the target, the InputConfigs receives the unexpanded value of Cfgs (@(ApplicationToDeploy->'%(RootDir)%(Directory)*.cs'). When the InputConfigs expands it, it results on the semicolon-separated string list of folders, but it doesn't resolve the wildcards (*.config).

Is it possible to get the same result outside Target block?

I think that InputConfigs should always receive the expanded list of directories. The expansion is made during the execution phase of the build. During this phase, only properties and items defined within targets are evaluated. So, I would keep all the initialization inside an 'Initialization' Target block. I don't mean it is impossible to do it outside a Target block, but for the reasons mentioned it does not seems logical. =]

Hope this helps,

like image 102
Marcos Brigante Avatar answered Oct 14 '22 04:10

Marcos Brigante