使用MSBuild将多个值传递给Wix DefineConstants属性

时间:2009-02-03 10:50:49

标签: msbuild wix msbuild-task

我目前正在将我的Wix项目集成到MSBuild中。我有必要将多个值传递给Wix项目。一个值将起作用(以下示例中的ProductVersion)。

<Target Name="BuildWixSetups">
    <MSBuild Condition="'%(WixSetups.Identity)'!=''"
                Projects="%(WixSetups.Identity)"
                Targets="Rebuild" Properties="Configuration=Release;OutputPath=$(OutDir);DefineConstants=ProductVersion=%(WixSetups.ISVersion)" ContinueOnError="true"/>
</Target>

但是,如何将多个值传递给DefineConstants键?我已经尝试了所有'逻辑'分隔符(空格,逗号,分号,管道符号),但这不起作用。

有其他人遇到过这个问题吗?

不起作用的解决方案:

  1. 尝试添加DefineConstants元素不起作用,因为需要在Properties属性中表达DefineConstants。

12 个答案:

答案 0 :(得分:13)

问题:

MSBuild任务(不是MSBuild.exe,名为MSBuild的MSBuild任务)无法处理WIX项目使用的多个常量。通常,您可以在构建脚本中指定属性,如:

<MSBuild Projects="YourSolution.sln" Properties="Configuration=MyConfig;Platform=x86;DefineConstants=&quot;SOMETHING=1;SOMETHINGELSE=2&quot;" />

然而,在查看构建日志时,您看到的是MSBuild将常量分开,并且不会像您期望的那样将值组合在一起 - 类似于:

Task "MSBuild" Global Properties:
Configuration=MyConfig
Platform=x86
DefineConstants="SOMETHING=1
SOMETHINGELSE=2"

因此当candle尝试使用这些常量时,它通常会响应“错误CNDL0150:未定义的预处理器变量'$(var.SOMETHINGELSE)'。这意味着MSBuild任务无法正确处理包含多个'='的属性即使在引号中分组的值也是如此。如果没有将属性值分组为引号,它们显然应被视为单独的属性,而不是单个值。

解决方法:

为了解决此问题,您需要直接调用MSBuild.exe并手动将这些值传递给它。

msbuild.exe /p:Configuration=MyConfig /p:Platform=x86 /p:DefineConstants="SOMETHING=1;SOMETHINGELSE=2" YourSolution.sln

这将使您的常量按照您希望的方式工作,而无需重新设计您的WiX安装项目。

注意:如果您只使用一个常量,您仍然可以像这样使用MSBuild任务:

<MSBuild Projects="YourSolution.sln" Properties="Configuration=MyConfig;Platform=x86;DefineConstants=&quot;SOMETHING=1&quot;" />

答案 1 :(得分:13)

问题在于将名称 - 值对传递给MSBuild任务,然后让MSBuild正确解析它们,以便将它们传递给Candle任务。似乎MSBuild可以处理简单名称列表或单个名称 - 值对,但不能处理对列表。

我的解决方案是在传递到MSBuild任务时转义列表,并在转到Candle任务时再次取消它。

在MSBuild文件中,定义属性中的对,如下所示:

<PropertyGroup>
    <WixValues>
        One=1;
        Two=2;
        Three=3;
    </WixValues>
</PropertyGroup>

当您调用MSBuild任务时,请转义属性(需要MSBuild 4):

<MSBuild 
    Projects="setup.wixproj"
    Properties="WixValues=$([MSBuild]::Escape($(WixValues)))" />

unescaping必须在wixproj文件中完成,但不需要手动编辑文件。只需打开项目的属性,转到Build选项卡,并在其中显示“Define preprocessor variables”,放置:

$([MSBuild]::Unescape($(WixValues)))

即使该框中已有其他变量,这也有效;只需将其与分号一起添加到列表中。

您将在MSBuild日志中看到candle.exe工具正确接收参数:

candle.exe -dOne=1 -dTwo=2 -dThree=3 -dConfiguration=Release...

答案 2 :(得分:3)

我仍然很难让这个工作,但上面每个答案的peices促成了我的解决方案,所以我想分享。

环境:TFS Build Server 2010 - Powershell调用MSBuild.exe Wix 3.6安装在构建服务器上

目标是将构建版本号和5个目录传递给MSBuild,以便Candle.exe正确接收它们

Build在构建结束时调用命令文件,该命令文件包含调用powershell脚本以构建Wix安装程序的步骤。它传递构建的版本号以及源和输出目录(输出是UNC文件共享引用\ tfsbuildserver ..)

要在Powershell ps1文件中使其正常工作,

  • 使用单个/ p:DefineConstants =以双“
  • 开头
  • 编码所有分离;如%3b
  • =没有问题
  • 任何带空格的文件名都不应附加引号

    $msbuild = "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe"
    
    $options = " /p:Configuration=Release /p:Platform=x64 "
    $options = $options + " /p:DefineConstants="""
    $options = $options + "SolutionDir=" + $SourceDir
    $options = $options + "%3bTFSBuildSourceLanding=" + $OutputLocation + "SharepointWebRoot\Landing"
    $options = $options + "%3bTFSBuildSourceLandingAdmin=" + $OutputLocation + "SharepointWebRoot\LandingAdmin"
    $options = $options + "%3bTFSBuildSourceRegistration=" + $OutputLocation + "Extranet_Registration"
    $options = $options + "%3bTFSBuildSourceGAC=" + $OutputLocation + "GAC"
    $options = $options + "%3bTFSBuildSourceSQL=" + $OutputLocation + "SQL"
    $options = $options + "%3bProductVersion=" + $BuildVersion + """" 
    
    
    $build = $msbuild + " ""EUM WIX\EUM Wix.wixproj"" " + $options + " /t:Build"
    $clean = $msbuild + " ""EUM WIX\EUM Wix.wixproj"" " + $options + " /t:Clean"
    
    
    Write-Host "Building Wix Installer..."
    Invoke-Expression $clean
    Invoke-Expression $build
    
  • 在wixproj文件中,我们必须unescape(在底部,在其中说取消注释以进行修改)

      <Target Name="BeforeBuild">
          <CreateProperty Value="$([MSBuild]::Unescape($(DefineConstants)))">
             <Output TaskParameter="Value" PropertyName="DefineConstants" />
          </CreateProperty>
      </Target>
    
  • 另请注意,在Visual Studio中,我将使用调试设置,并且我可以为这些值设置默认值,在“构建”选项卡中,“定义预处理器变量”

  • 在发布设置中
  • 此项目应为空白,因为它将在上面的命令行中传递。

答案 3 :(得分:2)

使用MSBuild任务构建Visual Studio解决方案时,以下内容适用于我:

<MSBuild Projects="Solution.sln"
         Targets="Rebuild"
         Properties="Configuration=Debug;DefineConstants=DEBUG%3bTRACE" />

诀窍是使用%3b来转义;值内的DefineConstants分隔符。我不确定这是否适用于=。它们可能需要以%3d进行转义,否则可能根本不起作用......

MSBuild元素上还有一个TargetAndPropertyListSeparators属性。我找不到任何文档,但可以使用它来设置;以外的分隔符。

答案 4 :(得分:2)

您可以将其作为参数传递,而不是常量。代码如下所示:

<MSBuild ...
    Properties="ProductVersion=%(WixSetups.ISVersion)" />

现在在WiX项目中添加一个常量:

<DefineConstants>BuildVersion=$(ProductVersion)</DefineConstants>

并在需要的* .wxs文件中使用它:

$(var.BuildVersion)

例如:

<Product Id="*" Name="My Company" Language="1033" Version="$(var.BuildVersion)"... />

这将适用于多个参数。

答案 5 :(得分:1)

我知道MSDN文档中充满了错误,有时会产生误导:这就是它所说的 DefineConstants

  

定义条件编译器   常量。符号/值对是   用分号隔开并且是   使用以下指定   语法:

     

symbol1 = value1; symbol2 = value2

     

该属性相当于   / define compiler switch。

http://msdn.microsoft.com/en-us/library/bb629394.aspx

根据MSDN,您可以1.定义多个常量和2.(@Sayed)赋值

HOWEVER 我无法使MSBuild任务的此属性的预期行为按预期工作,并推荐Jeff Winn的解决方法,并且应将其帖子标记为答案。

答案 6 :(得分:1)

当我将它们包含在.wixproj文件中时(使用Visual Studio 2010),以下行有效。

<PropertyGroup>
  <DefineConstants>Const1=Value1;Const2=Value2;Const3=Value3</DefineConstants>
</PropertyGroup>

答案 7 :(得分:0)

我认为这样的事情应该有用。

 <DefineConstants>DEBUG;TRACE</DefineConstants> 

查看此博客文章,了解它是否可以帮助您。 http://www.sedodream.com/PermaLink,guid,9b1d23aa-6cb2-48cb-a47a-9cef29622676.aspx

另请查看此论坛帖子。它解决了与您类似的问题。 http://social.msdn.microsoft.com/Forums/en-US/msbuild/thread/3f485bf4-1b00-48bf-b5d0-4b83341ce4a6/

答案 8 :(得分:0)

解决方法黑客。

假设:

  • SomeEnumValue的可能值是 EnumValue1和EnumValue2

在MSBuild中:

<DefineConstants>Debug;use_$(SomeEnumValue)</DefineConstants>

在WiX中:

<?ifdef $(var.use_EnumValue1) ?>
  ...
<?elseif $(var.use_EnumValue2) ?>
  ...
<?endif?>

答案 9 :(得分:0)

我的解决方案是使用&#59;以这种方式转义分号:

<MSBuild
    Projects="MyApplication.sln"
    Properties="DefineConstants=Sources=$(OutputPath)\MyApplication\&#59;Configuration=$(Configuration)&#59;OutDir=$(OutputPath)\WiX\"
    Targets="Clean;Rebuild"
/>

答案 10 :(得分:-1)

为什么要指定DefineContstants = ProductVersion = XXXXXX?

对于DefineConstants,您没有分配值,或者定义了常量(如DEBUG或TRACE),或者不是。此属性与/define C#编译器开关有关。你真的想做什么?

另外,当你说我的博客帖子是“黑客”时,我不确定你的意思是它建立两次的事实是重点。

Sayed Ibrahim Hashimi

我的书:Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

答案 11 :(得分:-1)

这对我有用,并允许我传递尚未定义的键值对的项目。不是最优雅的,但我不是编码员。

msbuild test.proj / p:PassedInProp =“ProductVersion = 45; rt = 669; wewanttoknow = test5”

<?xml version="1.0" encoding="utf-8" ?>
<Project ToolsVersion="4.0" DefaultTargets="Test" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
    <test Include="$([MSBuild]::Unescape($(PassedInProp)))" />
  </ItemGroup>
 <Target Name ="Test">
    <CreateItem Include ="$([System.String]::New('%(test.identity)').Split('=')[0])" AdditionalMetadata="value=$([System.String]::New('%(test.identity)').Split('=')[1])">
      <Output TaskParameter="Include" ItemName ="Test2"/>
    </CreateItem>
    <Message Text ="Key: %(test2.identity)  Value: %(test2.value)"/>
  </Target>
</Project>