1

我试图通过使用外部任务来获取有关我想在哪里存储最终输出的信息,从而在 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 的变量。

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

4

1 回答 1

0

我意识到这是一个老问题,但找出解决方案很乏味。因此,在这里发布我的发现是为了(希望)为其他人节省大量时间。


解决方案 1(推荐)

现在 MSBuild在 GitHub 上开源,我在他的 MSBuild 示例 repo 上向 MSFT 团队成员 (Jeff Kluge) 询问了一个类似的问题(例外是,我正在尝试使用内联任务),并收到了一个解决方案: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 属性函数。它提供了一组有限/列入白名单的 BCL API 方法,并且需要一个外部环境变量来加载自定义程序集。请注意,我们不能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/ 。

于 2017-01-31T21:25:14.710 回答