如何在 MSBuild 目标中重新 运行 属性 求值?
How to re-run property evaluation in MSBuild target?
我有一个自定义的 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 in that I'm trying to isolate this template to an external file. The template contains MSBuild property references such as $(ApplicationName)
. When doing this exactly as described in the linked solution 的返工尝试,它工作正常,但是当将模板作为字符串加载时,这些 属性 表达式中的 none获取文件。
<SPAN CLASS="BannerTextApplication">$(ApplicationName)</SPAN>
是否有 MSBuild expression/function 我可以使用它来获取要在调用 Target 的上下文中重新评估的字符串?
顺便说一句,我宁愿不使用find/replace或正则表达式替换来解决这个问题,并坚持使用MSBuild表达式引擎。
使用 Visual Studio 2012 和 .NET Framework 4.5。
很抱歉暂时没有回到这个问题。
最初我认为要解决这个问题,我们需要以非常不寻常的方式改变 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 提议的 "this solution" 中的大量 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>
您可以使用评估任务
<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]) >= 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;
}
}
}
我有一个自定义的 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 in that I'm trying to isolate this template to an external file. The template contains MSBuild property references such as $(ApplicationName)
. When doing this exactly as described in the linked solution 的返工尝试,它工作正常,但是当将模板作为字符串加载时,这些 属性 表达式中的 none获取文件。
<SPAN CLASS="BannerTextApplication">$(ApplicationName)</SPAN>
是否有 MSBuild expression/function 我可以使用它来获取要在调用 Target 的上下文中重新评估的字符串?
顺便说一句,我宁愿不使用find/replace或正则表达式替换来解决这个问题,并坚持使用MSBuild表达式引擎。
使用 Visual Studio 2012 和 .NET Framework 4.5。
很抱歉暂时没有回到这个问题。 最初我认为要解决这个问题,我们需要以非常不寻常的方式改变 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 提议的 "this solution" 中的大量 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>
您可以使用评估任务
<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]) >= 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;
}
}
}