Collections and $, @, % Variable References in MSBuild

A very useful and illuminating article:

MSBuild: PropertyGroup, ItemGroup, Item Metadata, and crazy $@% syntax

It also expertly articulates a common feeling:

It brings me great joy that the title of this blog post is both technically accurate and evokes the type of comic-book language I use when trying to figure out MSBuild variable syntax.

It expounds on how to express discrete items, lists, and dictionaries as well as how to refer to them; even implicitly looping over collections.

At the high-level:

  • $: Refers to scalar values. Renders an empty-string for collections.
  • @: Can be used to pass collections or flatten a collection into semicolon-delimited values if used in a scalar context (like embedding in a message task). Note that the individual values will first be normalized by expanding by semicolon and then be flattened by semicolon. So, extraneous semicolons will in fact be removed.
  • %: Expand the given task into N-tasks for each item in a collection.

It’s worth mentioning that %(VariableName) will not expand in such a way that it would result in doing the same thing twice.

For example, I have a list of two assemblies and two XML files of the same two names and would like to print them:

<Target Name="BeforeBuild">
  <ItemGroup>
    <File Include="$(MSBuildProjectDirectory)\AncillaryAssemblies\*.*" />
  </ItemGroup>
  <Message Text="Inner file: %(File.Filename)" />
</Target>

Output:

1>  Inner file: EntityFramework
1>  Inner file: EntityFramework.SqlServer

However, if I were to print the extensions, as well:

<Target Name="BeforeBuild">
  <ItemGroup>
    <File Include="$(MSBuildProjectDirectory)\AncillaryAssemblies\*.*" />
  </ItemGroup>
  <Message Text="Inner file: %(File.Filename) %(File.Extension)" />
</Target>

Output:

1>  Inner file: EntityFramework .dll
1>  Inner file: EntityFramework.SqlServer .dll
1>  Inner file: EntityFramework.SqlServer .xml
1>  Inner file: EntityFramework .xml

Something to keep in mind.

Advertisements

Publishing with ClickOnce via Visual Studio and TFS 2015 (vNext)

ClickOnce equips your published application to be able to check for updates and to, optionally, do the update automatically. See this post for more information.

To invoke this functionality from either the command-line or your build definition/process, you need to pass some additional project parameters to MSBuild.

ApplicationVersion (e.g. 1.0.1.6)
InstallUrl (e.g. http://clickonce.localhost/consoletestfrombuild/)
UpdateUrl (e.g. http://clickonce.localhost/consoletestfrombuild/)
PublishUrl (e.g. http://clickonce.localhost/consoletestfrombuild/)
UpdateEnabled (e.g. true)
UpdateMode (e.g. Foreground)
ProductName (e.g. TestConsoleApplication)

There is some disagreement as to whether the “ProductName” parameter is necessary.

The corresponding set of MSBuild arguments using the example values:

/target:publish /p:ApplicationVersion=1.0.1.6 /p:InstallUrl=http://clickonce.localhost/consoletestfrombuild/ /p:UpdateUrl=http://clickonce.localhost/consoletestfrombuild/ /p:PublishUrl=http://clickonce.localhost/consoletestfrombuild/ /p:UpdateEnabled=true /p:UpdateMode=Foreground /p:ProductName=TestConsoleApplication

If your polling locations are going to be UNCs (“\\path\\file”) rather than URLs then you’ll have to pass an additional argument:

/p:IsWebBootstrapper=false

To configure this under a TFS 2015 build-definition, simply add the parameters to the “MSBuild Arguments” field:

ClickOnce Settings in TFS 2015

References

Building ClickOnce Applications from the Command Line (Microsoft)

Deploying a click-once application with build vNext in Azure Blob Storage