开发者

MSBuild Validating Properties

I'm working on a reusable MSBuild Target that will be consumed by several other tasks. This target requires that several properties be defined. What's the best way to validate that properties are defined, throwing an Error if the are not?

Two attempts that I almost like:

<?xml version="1.0" encoding="utf-8" ?>
  <Project ToolsVersion="3.5" DefaultTarget="Release" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <Target Name="Release">
    <Error
      Text="Property PropA required"
      Condition="'$(PropA)' == ''"/>
    <Error
      Text="Property PropB required"
      Condition="'$(PropB)' == ''"/>

    <!-- The body of the task -->

  </Target>
</Project>

Here's an attempt at batching. It's ugly because of the extra "Name" parameter. Is it possible to use the Include attribute instead?

<?xml version="1.0" encoding="utf-8" ?>
<Project ToolsVersion="3.5" DefaultTarget="Release" xmlns="http://schemas.microsoft.com/develop开发者_如何学编程er/msbuild/2003">
  <Target Name="Release">
    <!-- MSBuild BuildInParallel="true" Projects="@(ProjectsToBuild)"/ -->
    <ItemGroup>
      <RequiredProperty Include="PropA"><Name>PropA</Name></RequiredProperty>
      <RequiredProperty Include="PropB"><Name>PropB</Name></RequiredProperty>
      <RequiredProperty Include="PropC"><Name>PropC</Name></RequiredProperty>
    </ItemGroup>

    <Error
      Text="Property %(RequiredProperty.Name) required"
      Condition="'$(%(RequiredProperty.Name))' == ''" />

  </Target>

</Project>


Great question! I have written about this in depth in my book and in a blog post, Elements of Reusable MSBuild Scripts: Validation. My approach will cover properties and items.

Here is the run down. In the shared .targets file create a validation target, and this should be one of the first targets declared in the file so that users can easily locate it.

Properties

Inside the validation target define your properties like this:

<_RequiredProperties Include="Root">
  <Value>$(Root)</Value>
</_RequiredProperties>

I place the name of the property in the include and its value inside of the Value metadata.The reason why I do this is so that I can detect when Value is blank and then I use the include value to report the name of the missing property back to the user.

Items

Inside the target place the required items inside of an item like:

<_RequiredItems Include="AllConfigurations">
  <RequiredValue>@(AllConfigurations)</RequiredValue>
</_RequiredItems>

Similar to the properties, inside the include you place the name of the item and then the value to check inside of RequiredValue metadata. In this example it just checks to ensure the the AllConfiguraitons item is not empty. If you want to make sure that a given metadata value is specified on all items then do something like:

<_RequiredItems Include = "AllConfigurations.Configuration">
  <RequiredValue>%(AllConfigurations.Configuration </RequiredValue>
</_RequiredItems>

If you want to make sure that a file exists then add the additional metadata, RequiredFilePath.

<_RequiredItems Include ="ProjectsToBuild">
  <RequiredValue>%(ProjectsToBuild.Identity)</RequiredValue>
  <RequiredFilePath>%(ProjectsToBuild.Identity)</RequiredFilePath>
</_RequiredItems>

Validation

Here is what you need to perform the validation

Complete example

Here is the full example

<Target Name="ValidateBuildSettings">
  <ItemGroup>
    <_RequiredProperties Include="Root">
      <Value>$(Root)</Value>
    </_RequiredProperties>

    <_RequiredProperties Include="BuildInstallRoot">
      <Value>$(BuildInstallRoot)</Value>
    </_RequiredProperties>

    <_RequiredProperties Include="SourceRoot">
      <Value>$(SourceRoot)</Value>
    </_RequiredProperties>
    <!-- 
    _RequiredItems is the item where required items should be placed. 
    The following metadata is significant: 
      REQUIRED METADATA: 
      Identity          = This will basically be used to identify the specific required item 
      RequiredValue     = This is the specific value that will be validated to exist 

      OPTIONAL METADATA 
      RequiredFilePath  = Populate this with a path that should exists, if it is not empty 
                            then it will be checked to exist on disk. 
    -->

    <_RequiredItems Include="AllConfigurations">
      <RequiredValue>@(AllConfigurations)</RequiredValue>
    </_RequiredItems>
    <_RequiredItems Include = "AllConfigurations.Configuration">
      <RequiredValue>%(AllConfigurations.Configuration </RequiredValue>
    </_RequiredItems>
    <_RequiredItems Include ="ProjectsToBuild">
      <RequiredValue>%(ProjectsToBuild.Identity)</RequiredValue>
      <RequiredFilePath>%(ProjectsToBuild.Identity)</RequiredFilePath>
    </_RequiredItems>
  </ItemGroup>
  <!-- Raise an error if any value in _RequiredProperties is missing -->

  <Error Condition =" '%(_RequiredProperties.Value)'=='' "
          Text=" Missing required property [%(_RequiredProperties.Identity)]" />

  <!-- Raise an error if any value in _RequiredItems is empty -->
  <Error Condition = " '%(_RequiredItems.RequiredValue)'=='' "
          Text = " Missing required item value [%(_RequiredItems.Identity)] " />

  <!-- Validate any file/directory that should exist -->
  <Error Condition = " '%(_RequiredItems.RequiredFilePath)' != '' and !Exists('%(_RequiredItems.RequiredFilePath)') "
          Text = " Unable to find expeceted path [%(_RequiredItems.RequiredFilePath)] on item [%(_RequiredItems.Identity)] " />
</Target>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜