Hi,
I manage to build a custom build rule with VS2008 and upgrade it in VS2010.
But I have a problem with the Outputs Property of the Rule in VS2010.
Basically, I add a custom property name OutputSubDir (String) and I want to use it in the Outputs property like this : $(Target)%(OutputSubDir)%(Filename).txt
When I put a default value (in the props file) for OutputSubDir, my Custom Build Rule used this value even if I set a new one for the selected item in the project.
The only way, I made MSBuild use the new one is to override the Outputs property as well (using the same value as the default one !)
It looks like :
- Project Item has Outputs property, then use the OutputSubDir Property of the Project Item
- Project Item does not have Outputs property, use the Outputs property of the Project then use the OutputSubDir property at the same location of the Outputs property (i.e.: at the Project level = default value)
It looks like MSBuild does not allow Polymorphism on its Items.
Do you have any idea how can I configure the properties, the rule, and/or the target to make the Outputs using the good value ?
Do you know if the problem still exists with VS2013 ?
I can provide sample files (.props, .xml & .targets) but there are quite long to put them directly in the question. (and I don't know if it is possible to upload them here)
Here are the files :
TestRule.xml
="1.0"="utf-8"
<ProjectSchemaDefinitions xmlns="clr-namespace:Microsoft.Build.Framework.XamlTypes;assembly=Microsoft.Build.Framework" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:transformCallback="Microsoft.Cpp.Dev10.ConvertPropertyCallback">
<Rule
Name="TestRule"
PageTemplate="tool"
DisplayName="Test Rule"
Order="200">
<Rule.DataSource>
<DataSource
Persistence="ProjectFile"
ItemType="TestRule" />
</Rule.DataSource>
<Rule.Categories>
<Category
Name="General">
<Category.DisplayName>
<sys:String>General</sys:String>
</Category.DisplayName>
</Category>
<Category
Name="Command Line"
Subtype="CommandLine">
<Category.DisplayName>
<sys:String>Command Line</sys:String>
</Category.DisplayName>
</Category>
</Rule.Categories>
<StringListProperty
Name="Inputs"
Category="Command Line"
IsRequired="true"
Switch=" ">
<StringListProperty.DataSource>
<DataSource
Persistence="ProjectFile"
ItemType="TestRule"
SourceType="Item" />
</StringListProperty.DataSource>
</StringListProperty>
<StringProperty
Name="OutputSubDir"
Category="General"
HelpContext="0"
DisplayName="Output SubDir"
Description="Sub Folder of $(Target) where output file should be"
Switch="[value]" />
<StringProperty
Name="CommandLineTemplate"
DisplayName="Command Line"
Visible="False"
IncludeInCommandLine="False" />
<DynamicEnumProperty
Name="TestRuleBeforeTargets"
Category="General"
EnumProvider="Targets"
IncludeInCommandLine="False">
<DynamicEnumProperty.DisplayName>
<sys:String>Execute Before</sys:String>
</DynamicEnumProperty.DisplayName>
<DynamicEnumProperty.Description>
<sys:String>Specifies the targets for the build customization to run before.</sys:String>
</DynamicEnumProperty.Description>
<DynamicEnumProperty.ProviderSettings>
<NameValuePair
Name="Exclude"
Value="^TestRuleBeforeTargets|^Compute" />
</DynamicEnumProperty.ProviderSettings>
<DynamicEnumProperty.DataSource>
<DataSource
Persistence="ProjectFile"
HasConfigurationCondition="true" />
</DynamicEnumProperty.DataSource>
</DynamicEnumProperty>
<DynamicEnumProperty
Name="TestRuleAfterTargets"
Category="General"
EnumProvider="Targets"
IncludeInCommandLine="False">
<DynamicEnumProperty.DisplayName>
<sys:String>Execute After</sys:String>
</DynamicEnumProperty.DisplayName>
<DynamicEnumProperty.Description>
<sys:String>Specifies the targets for the build customization to run after.</sys:String>
</DynamicEnumProperty.Description>
<DynamicEnumProperty.ProviderSettings>
<NameValuePair
Name="Exclude"
Value="^TestRuleAfterTargets|^Compute" />
</DynamicEnumProperty.ProviderSettings>
<DynamicEnumProperty.DataSource>
<DataSource
Persistence="ProjectFile"
ItemType=""
HasConfigurationCondition="true" />
</DynamicEnumProperty.DataSource>
</DynamicEnumProperty>
<StringListProperty
Name="Outputs"
DisplayName="Outputs"
Visible="True"
IncludeInCommandLine="False"/>
<StringProperty
Name="ExecutionDescription"
DisplayName="Execution Description"
IncludeInCommandLine="False" />
<StringListProperty
Name="AdditionalDependencies"
DisplayName="Additional Dependencies"
Visible="False"
IncludeInCommandLine="False" />
<StringProperty
Subtype="AdditionalOptions"
Name="AdditionalOptions"
Category="Command Line"
Visible="False">
<StringProperty.DisplayName>
<sys:String>Additional Options</sys:String>
</StringProperty.DisplayName>
<StringProperty.Description>
<sys:String>Additional Options</sys:String>
</StringProperty.Description>
</StringProperty>
</Rule>
<ItemType
Name="TestRule"
DisplayName="Test Rule" />
<FileExtension
Name="*.ext"
ContentType="TestRule" />
<ContentType
Name="TestRule"
DisplayName="Test Rule"
ItemType="TestRule" />
</ProjectSchemaDefinitions>
TestRule.props
="1.0"="utf-8"
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup
Condition="'$(TestRuleBeforeTargets)' == '' and '$(TestRuleAfterTargets)' == '' and '$(ConfigurationType)' != 'Makefile'">
<TestRuleBeforeTargets>Midl</TestRuleBeforeTargets>
<TestRuleAfterTargets>CustomBuild</TestRuleAfterTargets>
</PropertyGroup>
<PropertyGroup>
<TestRuleDependsOn
Condition="'$(ConfigurationType)' != 'Makefile'">_SelectedFiles;$(TestRuleDependsOn)</TestRuleDependsOn>
</PropertyGroup>
<ItemDefinitionGroup>
<TestRule>
<CommandLineTemplate>
ECHO OutputDir should be $(TargetDir)[OutputSubDir]%(Filename).txt
</CommandLineTemplate>
<Outputs>$(TargetDir)%(OutputSubDir)%(Filename).txt</Outputs>
<ExecutionDescription>Test Rule</ExecutionDescription>
</TestRule>
</ItemDefinitionGroup>
</Project>
TestRule.targets
="1.0"="utf-8"
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PropertyPageSchema
Include="$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml" />
<AvailableItemName
Include="TestRule">
<Targets>_TestRule</Targets>
</AvailableItemName>
</ItemGroup>
<UsingTask
TaskName="TestRule"
TaskFactory="XamlTaskFactory"
AssemblyName="Microsoft.Build.Tasks.v4.0">
<Task>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml</Task>
</UsingTask>
<Target
Name="_TestRule"
BeforeTargets="$(TestRuleBeforeTargets)"
AfterTargets="$(TestRuleAfterTargets)"
Condition="'@(TestRule)' != ''"
DependsOnTargets="$(TestRuleDependsOn);ComputeTestRuleOutput"
Outputs="%(TestRule.Outputs)"
Inputs="%(TestRule.Identity);%(TestRule.AdditionalDependencies);$(MSBuildProjectFile)">
<ItemGroup
Condition="'@(SelectedFiles)' != ''">
<TestRule
Remove="@(TestRule)"
Condition="'%(Identity)' != '@(SelectedFiles)'" />
</ItemGroup>
<ItemGroup>
<TestRule_tlog
Include="%(TestRule.Outputs)"
Condition="'%(TestRule.Outputs)' != '' and '%(TestRule.ExcludedFromBuild)' != 'true'">
<Source>@(TestRule, '|')</Source>
</TestRule_tlog>
</ItemGroup>
<Message
Importance="High"
Text="%(TestRule.ExecutionDescription)" />
<Message
Importance="High"
Text="OutputSubDir = %(TestRule.OutputSubDir)" />
<Message
Importance="High"
Text="Outputs = %(TestRule.Outputs)" />
<WriteLinesToFile
Condition="'@(TestRule_tlog)' != '' and '%(TestRule_tlog.ExcludedFromBuild)' != 'true'"
File="$(IntDir)$(ProjectName).write.1.tlog"
Lines="^%(TestRule_tlog.Source);@(TestRule_tlog->'%(Fullpath)')" />
<TestRule
Condition="'@(TestRule)' != '' and '%(TestRule.ExcludedFromBuild)' != 'true'"
CommandLineTemplate="%(TestRule.CommandLineTemplate)"
OutputSubDir="%(TestRule.OutputSubDir)"
AdditionalOptions="%(TestRule.AdditionalOptions)"
Inputs="%(TestRule.Identity)" />
</Target>
<PropertyGroup>
<ComputeLinkInputsTargets>
$(ComputeLinkInputsTargets);
ComputeTestRuleOutput;
</ComputeLinkInputsTargets>
<ComputeLibInputsTargets>
$(ComputeLibInputsTargets);
ComputeTestRuleOutput;
</ComputeLibInputsTargets>
</PropertyGroup>
<Target
Name="ComputeTestRuleOutput"
Condition="'@(TestRule)' != ''">
<ItemGroup>
<TestRuleDirsToMake
Condition="'@(TestRule)' != '' and '%(TestRule.ExcludedFromBuild)' != 'true'"
Include="%(TestRule.Outputs)" />
<Link
Include="%(TestRuleDirsToMake.Identity)"
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" />
<Lib
Include="%(TestRuleDirsToMake.Identity)"
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" />
<ImpLib
Include="%(TestRuleDirsToMake.Identity)"
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" />
</ItemGroup>
<MakeDir
Directories="@(TestRuleDirsToMake->'%(RootDir)%(Directory)')" />
</Target>
</Project>
and the Extract of .VCXPROJ :
<ItemGroup>
<TestRule Include="test.ext">
<OutputSubDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">info\</OutputSubDir>
<FileType>Document</FileType>
</TestRule>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="TestRule.targets" />
</ImportGroup>
I discovered something else:
<ItemGroup>
<TestRule Include="test.ext">
<OutputSubDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">info\</OutputSubDir>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(TargetDir)%(OutputSubDir).dll\</Outputs>
<FileType>Document</FileType>
</TestRule>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="TestRule.targets" />
</ImportGroup>
is not the same as:
<ItemGroup>
<TestRule Include="test.ext">
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(TargetDir)%(OutputSubDir).dll\</Outputs>
<OutputSubDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">info\</OutputSubDir>
<FileType>Document</FileType>
</TestRule>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="TestRule.targets" />
</ImportGroup>
In the first case, the Outputs will use the value of OutputSubDir but not in the second case where it will use the value defined in the props file.
MSBuild looks to be
sequential