I realize that this is an old question, but figuring out the solution was tiring tedious. Therefore posting my findings here to (hopefully) save others spending to much time.
Solution 1 (recommended)
Now that MSBuild is opened sourced on GitHub, I asked MSFT team member (Jeff Kluge) on his MSBuild samples repo a similar question (exception being, I was trying with an inline task), and received a solution: https://github.com/jeffkl/MSBuild-NetCore/issues/2
@jeffkl said:
Here is my example: 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 said:
Which is imported here: 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>
Note that this solution is not limited to .NET Core (MSBuild 15.0+). I have tested with MSBuild 14.0 (VS2015) and it worked. :)
Solution 2
We can also use MSBuild Property Functions. It provides the limited / white-listed set of BCL APIs methods and requires an external environment variable to load custom assembly. Note that we cannot use System.Enviornment.SetEnvironmentVariable
within our target/proj file, as it is not in the white-list (only System.Enviornment.GetEnvironmentVariable
is). Hence it requires an external change in the environment prior to building the solution (or even opening the solution in VS). From command line it would be bit easier:
# cmd
set MSBUILDENABLEALLPROPERTYFUNCTIONS=1 && msbuild my.sln
# PowerShell
$env:MSBUILDENABLEALLPROPERTYFUNCTIONS=1 ; msbuild my.sln
# UnixShell
MSBUILDENABLEALLPROPERTYFUNCTIONS=1 dotnet build my.sln
See https://blogs.msdn.microsoft.com/visualstudio/2010/04/02/msbuild-property-functions/ for further details on usage, limitations and their workaround.