使用外部任务确定OutputPath

时间:2012-11-02 11:28:34

标签: msbuild msbuild-task msbuild-4.0

我试图通过使用外部任务来获取有关我希望存储最终输出的位置的信息,从而在csproj中全局覆盖OutputPath参数。

我创建了一个任务:

<Target Name="SetSolutionTarget">
    <SetSolutionConfiguration SolutionPath="$(SolutionPath)">
      <Output PropertyName="SolutionConfig" TaskParameter="SolutionConfiguration"/>
    </SetSolutionConfiguration>
    <Message Text="SolutionConfiguration is: $(SolutionConfig)" Importance="high" />
</Target>

任务运行良好,消息输出正确的值,我想输出文件。

问题是当我尝试将它与OutputPath集成时。 试着做:

<OutputPath>$(SolutionConfig)</OutputPath>

不起作用 - 它会抛出错误:“OutputPath属性未设置....” - 这意味着变量不在任务之间传递,或者必须在执行构建之前设置OutputPath的变量。

我还尝试过其他一些事情,例如在我的任务中设置环境变量而不是输出结果(但仍然没有运气)。

1 个答案:

答案 0 :(得分:0)

我意识到这是一个古老的问题,但找出解决方案令人厌倦乏味。因此,在此发布我的发现(希望)可以节省其他人花费很多时间。

解决方案1(推荐)

既然MSBuild已经开源了on GitHub,我问MSFT团队成员(Jeff Kluge)他的MSBuild样本回复了一个类似的问题(例外情况是,我正在尝试使用内联任务),并收到了一个解决方案: https://github.com/jeffkl/MSBuild-NetCore/issues/2

  

@jeffkl说:

     

以下是我的示例:https://github.com/jeffkl/MSBuild-NetCore/blob/master/src/OutputPathViaInlineTask/build.targets

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <UsingTask TaskName="GetOutputPath" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
    <ParameterGroup>
      <OutputPath ParameterType="System.String" Output="true" />
    </ParameterGroup>
    <Task>
      <Code Type="Fragment" Language="cs">
        <![CDATA[
          OutputPath = Path.Combine("bin", Guid.NewGuid().ToString("N")) + System.IO.Path.DirectorySeparatorChar;
        ]]>
      </Code>
    </Task>
  </UsingTask>

  <Target Name="SetOutputPath"
          BeforeTargets="_CheckForInvalidConfigurationAndPlatform">

    <Message Text="Before:" />
    <Message Text=" OutputPath: '$(OutputPath)'" />
    <Message Text=" OutDir: '$(OutDir)'" />
    <Message Text=" PublishDir: '$(PublishDir)'" />
    <Message Text=" _InvalidConfigurationError: '$(_InvalidConfigurationError)'" />
    <Message Text=" _InvalidConfigurationWarning: '$(_InvalidConfigurationWarning)'" />

    <GetOutputPath>
      <Output TaskParameter="OutputPath" PropertyName="OutputPath"/>
      <!--
        OutDir is set in Microsoft.Common.CurrentVersion.targets as a copy of $(OutputPath). Since
        we've reset $(OutputPath), we need to reset $(OutDir) as well for backwards compatibility.
      -->
      <Output TaskParameter="OutputPath" PropertyName="OutDir" />
    </GetOutputPath>


    <PropertyGroup>
      <!--
        PublishDir is set in Microsoft.Common.CurrentVersion.targets and starts with $(OutputPath).
        Since we've reset $(OutputPath), we need to reset $(PublishDir).  We have to do this in a
        <PropertyGroup /> because the <Output /> from <GetOutputPath /> can't append text.
      -->
      <PublishDir>$(OutputPath)app.publish\</PublishDir>
      <!--
        If <OutputPath /> is not set in a top-level <PropertyGroup /> then $(_InvalidConfigurationError)
        is set to 'true' which will cause a build error.  However, we've set $(OutputPath) in a task 
        just before _CheckForInvalidConfigurationAndPlatform ran so we can turn off the error now that
        we know everything is set properly. 
      -->
      <_InvalidConfigurationError>false</_InvalidConfigurationError>
      <_InvalidConfigurationWarning>false</_InvalidConfigurationWarning>
    </PropertyGroup>

    <Message Text="After:" />
    <Message Text=" OutputPath: '$(OutputPath)'" />
    <Message Text=" OutDir: '$(OutDir)'" />
    <Message Text=" PublishDir: '$(PublishDir)'" />
    <Message Text=" _InvalidConfigurationError: '$(_InvalidConfigurationError)'" />
    <Message Text=" _InvalidConfigurationWarning: '$(_InvalidConfigurationWarning)'" />

  </Target>
</Project>
  

@jeffkl说:

     

在此导入的内容:https://github.com/jeffkl/MSBuild-NetCore/blob/master/src/OutputPathViaInlineTask/OutputPathViaInlineTask.csproj#L24

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{C76AA0AA-3B50-48D6-BE46-0F2D8DC56C02}</ProjectGuid>
    <OutputType>Exe</OutputType>
    <RootNamespace>OutputPathViaInlineTask</RootNamespace>
    <AssemblyName>OutputPathViaInlineTask</AssemblyName>
    <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "/>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "/>

  <ItemGroup>
    <Reference Include="System" />
    <Compile Include="Program.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <None Include="App.config" />
  </ItemGroup>

  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <Import Project="$(MSBuildThisFileDirectory)build.targets" />

</Project>

请注意,此解决方案不仅限于.NET Core(MSBuild 15.0+)。我已经使用MSBuild 14.0(VS2015)测试过它。 :)

解决方案2

我们也可以使用MSBuild Property Functions。它提供有限/白名单的BCL API方法集,并且需要外部环境变量来加载自定义程序集。请注意,我们无法在target / proj文件中使用System.Enviornment.SetEnvironmentVariable,因为它不在白名单中(仅System.Enviornment.GetEnvironmentVariable是)。因此,在构建解决方案之前需要对环境进行外部更改(甚至在VS中打开解决方案)。从命令行开始会更容易:

# cmd
set MSBUILDENABLEALLPROPERTYFUNCTIONS=1 && msbuild my.sln

# PowerShell
$env:MSBUILDENABLEALLPROPERTYFUNCTIONS=1 ; msbuild my.sln


# UnixShell
MSBUILDENABLEALLPROPERTYFUNCTIONS=1 dotnet build my.sln

有关使用,限制及其解决方法的详细信息,请参阅https://blogs.msdn.microsoft.com/visualstudio/2010/04/02/msbuild-property-functions/