VS 2015中MSBuild自定义任务的动态输出

时间:2016-11-12 06:08:05

标签: msbuild

我一直在尝试在VS2015中为消息编译器创建一个自定义构建任务 - mc.exe。您可能会意识到支持这种自定义的文档很少。

我决定基于VS附带的masm和lc扩展的XamlTask​​Factory方法。我已经创建了道具,目标和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 &quot;[value]&quot;"
                          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>

0 个答案:

没有答案