如何在MSBuild目标中重新运行属性评估?

时间:2015-04-15 00:06:21

标签: msbuild msbuild-task msbuild-4.0

我有一个自定义的MSBuild目标,部分代码段如下..

<Target Name="PublishHtm">
  <PropertyGroup>
    <PublishHtmTemplateContents>$([System.IO.File]::ReadAllText($(PublishHtmTemplatePath)))</PublishHtmTemplateContents>
    <PublishHtm>$(PublishHtmTemplateContents)</PublishHtm>
  </PropertyGroup>
  <WriteLinesToFile Lines="$(PublishHtm)" File="$(PublishDir)\publish.htm" Overwrite="true"/>
</Target>

这是this solution的返工尝试,因为我试图将此模板与外部文件隔离开来。该模板包含MSBuild属性引用,例如$(ApplicationName)。完全按照linked solution中的描述执行此操作时,它可以正常工作,但是当以字符串形式加载模板时,这些属性表达式在到达文件时都不会被评估。

<SPAN CLASS="BannerTextApplication">$(ApplicationName)</SPAN>

是否有一个MSBuild表达式/函数,我可以使用它来获取要调用Target的上下文重新评估的字符串?

BTW我宁愿使用find / replace或regex replace解决问题,并坚持使用MSBuild表达式引擎。

使用Visual Studio 2012&amp; .NET Framework 4.5。

2 个答案:

答案 0 :(得分:2)

很抱歉没有回到这个问题一段时间。 最初我认为要解决这个问题,我们需要以非常不寻常的方式弯曲MSBuild(今天的计划是编写复杂的内联任务,它将使用Msbuild属性作为标记在外部文件中进行正则表达式替换)。但我认为使用CDATA部分可以更容易地解决这个问题,该部分在属性定义中是有效的:

这是主要的脚本:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
    <PropertyGroup>
        <MyOtherProperty>$([System.DateTime]::Now)</MyOtherProperty>
        <Version>1.0.1b</Version>
        <ProjectName>MSBuild Rox</ProjectName>
        <Author>Alexey Shcherbak</Author>
    </PropertyGroup>

    <Target Name="Build">
        <ItemGroup>
            <PropsToPass Include="MyOtherProperty=$(MyOtherProperty)" />
            <PropsToPass Include="Version=$(Version)" />
            <PropsToPass Include="ProjectName=$(ProjectName)" />
            <PropsToPass Include="Author=$(Author)" />
        </ItemGroup>

        <MSBuild Projects="TransformHTML.Template.proj" Properties="@(PropsToPass)" />
    </Target>
</Project>  

这是你的模板。它不是纯粹的html,它仍然是msbuild文件,但至少对xml中的html标签没有丑陋的编码。它只是CDATA的一个区块

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Transform">
    <PropertyGroup>
            <HtmlProperty><![CDATA[
                <body>
                    <div>$(MyOtherProperty)</div>
                    <div>$(Version)</div>
                    <div>$(ProjectName)</div>
                    <div>$(Author)</div>
                </body>
            ]]></HtmlProperty>
        </PropertyGroup>

    <Target Name="Transform">
        <Message Text="HtmlProperty: $(HtmlProperty)" Importance="High" />
    </Target>
</Project>  

也许它不是很优雅(我个人不喜欢@PropsToPass的部分),但它会完成这项工作。您可以将所有内联内容放入单个文件中,然后您不需要将属性传递给MSBuild任务。我不喜欢提出的“这个解决方案”中的大量html编码,但我更喜欢将HTML模板保存在它将被转换的同一个脚本中,只是以漂亮的html格式,没有编码。

单个文件示例:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
    <PropertyGroup>
        <MyOtherProperty>$([System.DateTime]::Now)</MyOtherProperty>
        <Version>1.0.1b</Version>
        <ProjectName>MSBuild Rox</ProjectName>
        <Author>Alexey Shcherbak</Author>
    </PropertyGroup>

    <Target Name="Build">
        <PropertyGroup>
            <HtmlProperty><![CDATA[
                <body>
                    <div>$(MyOtherProperty)</div>
                    <div>$(Version)</div>
                    <div>$(ProjectName)</div>
                    <div>$(Author)</div>
                </body>
            ]]></HtmlProperty>
        </PropertyGroup>

        <Message Text="HtmlProperty: $(HtmlProperty)" Importance="High" />
    </Target>
</Project>  

您还可以download these files here

答案 1 :(得分:1)

您可以使用 Eval 任务来完成

<Target Name="PublishHtm">
  <PropertyGroup>
    <PublishHtmTemplateContents>$([System.IO.File]::ReadAllText($(PublishHtmTemplatePath)))</PublishHtmTemplateContents>
    <Eval Values="$(PublishHtmTemplateContents)">
      <Output TaskParameter="Result" ItemName="EvalItemTemp"/>
    </Eval>
    <PublishHtm>%(EvalItemTemp.Identity)</PublishHtm>
  </PropertyGroup>
  <WriteLinesToFile Lines="$(PublishHtm)" File="$(PublishDir)\publish.htm" Overwrite="true"/>
</Target>

实际上该任务除了返回与接收到的完全相同的值之外什么都不做,但是当您将返回值 %(EvalItemTemp.Identity) 传递到任何地方时,msbuild 会进行评估!

评估任务来源:

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Choose>
    <When Condition="'$(MSBuildToolsVersion)' == 'Current' OR $(MSBuildToolsVersion.Split('.')[0]) &gt;= 14">
      <PropertyGroup>
        <TasksAssemblyName>Microsoft.Build.Tasks.Core.dll</TasksAssemblyName>
      </PropertyGroup>
    </When>
    <Otherwise>
      <PropertyGroup>
        <TasksAssemblyName>Microsoft.Build.Tasks.v$(MSBuildToolsVersion).dll</TasksAssemblyName>
      </PropertyGroup>
    </Otherwise>
  </Choose>
  <UsingTask TaskName="Eval" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\$(TasksAssemblyName)">
    <ParameterGroup>
      <Values ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="True" Output="False" />
      <Result ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="False" Output="True" />
    </ParameterGroup>
    <Task>
      <Code Type="Class" Language="cs" Source="$(MSBuildThisFileDirectory)TaskSource\EvalTask.cs"/>
    </Task>
  </UsingTask>
</Project>

TaskSource\EvalTask​​.cs 在哪里

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.Diagnostics;
using System.Threading;

namespace Varonis.MSBuild.Tasks
{
    public class Eval : Task
    {

        [Required]
        public ITaskItem[] Values { get; set; }
        [Output]
        public ITaskItem[] Result { get; set; }

        public override bool Execute()
        {
            Result = new TaskItem[Values.Length];
            for (int i = 0; i < Values.Length; i++)
            {
                Result[i] = new TaskItem(Values[i].ItemSpec);
            }
            return true;
        }
    }
}