我一直在尝试在VS2015中为消息编译器创建一个自定义构建任务 - mc.exe。您可能会意识到支持这种自定义的文档很少。
我决定基于VS附带的masm和lc扩展的XamlTaskFactory方法。我已经创建了道具,目标和UI xml文件。
我的第一个问题是Xaml任务本身。我一直收到错误MSB(不记得),一个漂亮的红鲱鱼说找不到任务等等......经过数周的试验和错误,(最终下载MSBuild源并检查当我从UI xml文件中删除属性默认值时,我让MSBuild / VS合作 - 但这是另一个故事,现在已经解决,并且时间允许我将整个体验写出来。< / p>
我仍然面临的问题是任务的输出。 mc.exe生成(为了我的目的)三个输出:头文件;资源文件;和一个bin文件。问题是这些文件的名称和路径依赖于用户在属性表中选择的选项。
props文件中的ItemDefinitionGroup设置UI的默认值,定义XamlTask命令模板和Outputs项。
我最初设置了要在此处生成的输出,使用UI属性表中设置的参数构建它们。我很快就意识到这不会起作用,因为MSBuild中的评估顺序意味着无论我做了什么,这里完成的任何事情都已经完成,并且不会受到UI属性表中设置的更改的影响。
我把头撞了一个星期,但找不到修改输出元数据的方法,以便允许我合并通过UI设置的用户更改。
所以我提出了使用属性来管理输出的想法。我创建了四个属性来表示输出:一个用于标题,资源和bin文件,一个用于表示整个Outputs属性本身(用分号连接每个文件),然后使用最后一个$(MessageCompileOutputs)作为输出任务的参数。
表示单个文件的三个属性包括转换(@()和%())运算符,以便将评估推迟到评估/构建阶段。一个丑陋的解决方案,但它允许增量构建并且工作正常。
除了TLog ....由于输出由使用转换的字符串组成,当我尝试将WriteLinesToFile写入适当的TLog文件时,IT DOESNT扩展字符串,因此VS正在寻找永远不会存在的文件,所以该项目永远不会出现最新。
目前我刚刚完全省略了TLog--缺点是VS Clean目标不起作用。但是渐进式构建确实如此。
虽然让TLog工作会很好,但我已经把这场战斗放弃了足够好(我很少这么做)。但在我最后的绝望中,我想我会尝试在这里发帖。
任何帮助,想法,建设性批评等都会非常感激。我非常乐意上传源代码(三个文件),但不知道怎么做。
[编辑]
猜猜我的第一个也是最重要的问题是“我如何使用UI属性页面中的选定选项设置输出元数据,以便让TLog工作”?
第二个问题是 - 有更好/规范的方法吗?含义 - 如何根据UI属性页中选择的参数动态设置任务的输出。我不可能是唯一试图这样做的人......
瑞克
这是最小的代码 - 足以(希望)将想法推到一边,切出批次以使其尽可能短。
切断XML文件(MessageCompile.xml)以保持代码
<ProjectSchemaDefinitions ...
<!-- Content Type -->
<ItemType, <ContentType, <FileExtension...
<Rule Name="MessageCompile", ....
<Rule.DataSource>
<DataSource Persistence="ProjectFile" ItemType="MessageCompile" HasConfigurationCondition="false" /> <!-- On Purpose - global is easier, per config is just painful. -->
</Rule.DataSource>
<Rule.Categories>
<Category Name="General" DisplayName="General" />
<Category Name="Command Line" DisplayName="Command Line" Subtype="CommandLine" />
</Rule.Categories>
<more stuf...................=""/>
<StringProperty Name="HeaderFolder"
DisplayName="Header Folder"
Description="Specifes the folder into which you want the compiler to place the generated header file. The default is the current directory."
Category="General"
Switch="-h "[value]""
Subtype="folder"
/>
<StringProperty Name="HeaderFile"
DisplayName="HeaderFile"
IncludeInCommandLine="False"
Visible="False"
/>
</Rule>
</ProjectSchemaDefinitions>
道具档案(MessageCompile.props)
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MessageCompileHeaderFileName >@(MessageCompile->'%(HeaderFolder)'\MessageCompile->'%(Filename)').h</MessageCompileHeaderFileName>
<MessageCompileResourceFileName >@(MessageCompile->'%(ResourceFolder)'\MessageCompile->'%(Filename)').rc</MessageCompileResourceFileName>
<MessageCompileBinFileName >@(MessageCompile->'%(ResourceFolder)'\MessageCompile->'%(Filename)').bin</MessageCompileBinFileName>
<MessageCompileOutputs >$(MessageCompileHeaderFileName);$(MessageCompileResourceFileName);$(MessageCompileBinFileName)</MessageCompileOutputs>
</PropertyGroup>
<ItemDefinitionGroup>
<MessageCompile>
<CustomerBit >true</CustomerBit>
<Verbose >false</Verbose>
<HeaderFolder >Generated</HeaderFolder>
<ResourceFolder >Generated</ResourceFolder>
<UseInputFileNameForBin >true</UseInputFileNameForBin>
<BinFileNameLanguage >MSG00409</BinFileNameLanguage>
<CommandLineTemplate >mc.exe [AllOptions] [AdditionalOptions] [Inputs]</CommandLineTemplate>
<ExecutionDescription >Compiling message resource %(Identity)</ExecutionDescription>
</MessageCompile>
</ItemDefinitionGroup>
目标文件(MessageCompile.targets)
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml" />
<AvailableItemName Include="MessageCompile">
<Targets>_MessageCompile</Targets>
</AvailableItemName>
</ItemGroup>
<UsingTask TaskName="MessageCompile"
TaskFactory="XamlTaskFactory"
AssemblyName="Microsoft.Build.Tasks.Core"
>
<Task>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml</Task>
</UsingTask>
<Target Name="_MessageCompile"
BeforeTargets="CustomBuild;ResourceCompile"
Condition="'@(MessageCompile)' != ''"
Inputs="%(MessageCompile.Identity);$(MSBuildProjectFile)"
Outputs="$(MessageCompileOutputs)"
DependsOnTargets="_SelectedFiles"
>
<ItemGroup Condition="'@(SelectedFiles)' != ''">
<MessageCompile Remove="@(MessageCompile)" Condition="'%(Identity)' != '@(SelectedFiles)'" />
</ItemGroup>
<ItemGroup>
<MessageCompile_tlog Include="@($(MessageCompileOutputs))" Condition="'$(MessageCompileOutputs)' != '' and '%(MessageCompile.ExcludedFromBuild)' != 'true'">
<Source>@(MessageCompile, '|')</Source>
</MessageCompile_tlog>
</ItemGroup>
<WriteLinesToFile Condition="'@(MessageCompile_tlog)' != '' and '%(MessageCompile_tlog.ExcludedFromBuild)' != 'true' and false"
File="$(TLogLocation)MessageCompile.write.1u.tlog"
Lines="^%(MessageCompile_tlog.Source);$(MessageCompileHeaderFileName);$(MessageCompileResourceFileName);$(MessageCompileBinFileName)"
Encoding="Unicode"
/>
<Message Importance="High" Text="%(MessageCompile.ExecutionDescription)" />
<MessageCompile Condition="'@(MessageCompile)' != '' and '%(MessageCompile.ExcludedFromBuild)' != 'true'"
CommandLineTemplate="%(MessageCompile.CommandLineTemplate)"
Inputs="%(MessageCompile.Identity)"
CustomerBit="%(MessageCompile.CustomerBit)"
Verbose="%(MessageCompile.Verbose)"
HeaderFolder="%(MessageCompile.HeaderFolder)"
ResourceFolder="%(MessageCompile.ResourceFolder)"
UseInputFileNameForBin="%(MessageCompile.UseInputFileNameForBin)"
BinFileNameLanguage="%(MessageCompile.BinFileNameLanguage)"
SpecifiedBinFileName="%(MessageCompile.SpecifiedBinFileName)"
/>
</Target>
</Project>