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
$(Root)
$(BuildInstallRoot)
$(SourceRoot)
<_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>