RasmusW
RasmusW

Reputation: 3481

MSBuild output in subfolder for each targetframework when using OutDir

When using the new SDK style csproj format, it is possible to use a <targetframeworks> element for a semicolon-separated list of target frameworks to build the project for.

This results in one subfolder per target framework in the build output folder:

build output folder structure

However, when passing the OutDir property on the msbuild command line, it does NOT create the subfolders, and the built assemblies are all placed in the same folder.

Command line that works, but doesn't allow output location:

msbuild projectfile.csproj

Command line that selects output directory, but places built assemblies in same folder (effectively overwriting the target framework assemblies that are built first):

msbuild projectfile.csproj /p:OutDir=buildtemp

Is there a way to place the build output in a non-default folder while still retaining the targetframwork subfolders?

Upvotes: 4

Views: 6035

Answers (2)

Rob Davis
Rob Davis

Reputation: 1319

If you need this to work with OutDir, there's an alternative method...

You can use TreatAsLocalProperty to redefine OutDir in a way that appends the multi-target folders to the output path; for example:

   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' And '$(OutDir)' != '' ">
    <!-- OutDir is passed to msbuild via command line, but it 
         prevents AppendTargetFrameworkToOutputPath from functioning.
         Combined with TreatAsLocalProperty="OutDir", we can redefine 
         outdir so it appends the $(TargetFramework) to support multi-targeting -->
    <OutDir>$(OutDir)\$(TargetFramework)</OutDir>
  </PropertyGroup>

Full SDK style project example:

<Project Sdk="Microsoft.NET.Sdk" TreatAsLocalProperty="OutDir">
  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;net462</TargetFrameworks>
    <AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
    <OutputType>Library</OutputType>
    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
    <AssemblyTitle>Some.Assembly</AssemblyTitle>
    <Description>Some Assembly</Description>
    <AssemblyVersion>1.0.0</AssemblyVersion>
    <FileVersion>1.0.0</FileVersion>
  </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' And '$(OutDir)' != '' ">
    <!-- OutDir is passed to msbuild via command line, but it 
         prevents AppendTargetFrameworkToOutputPath from functioning.
         Combined with TreatAsLocalProperty="OutDir", we can redefine 
         outdir so it appends the $(TargetFramework) to support multi-targeting -->
    <OutDir>$(OutDir)\$(TargetFramework)</OutDir>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
    <PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
  </ItemGroup>
</Project>

Upvotes: 1

Martin Ullrich
Martin Ullrich

Reputation: 100751

The property that is now used is OutputPath, however setting it from the CLI makes it a global property and overrules the automatic appending of the output path. The workaround is to make an intermediate property that is global and consume it from the project.

Add this to your project file inside a PropertyGroup:

<OutputPath>$(BaseOutputPath)</OutputPath>

Then you can set this property globally when calling the build command:

martin.ullrich@martins-imac:~/tmp$ dotnet build /p:BaseOutputPath=bin/foo

  tmp -> /Users/martin.ullrich/tmp/bin/foo/netstandard1.4/tmp.dll
  tmp -> /Users/martin.ullrich/tmp/bin/foo/netstandard1.6/tmp.dll

The problem here is that the SDK tries to change the OutputPath property based on TargetFramework and AppendTargetFrameworkToOutputPath. But if the value is specified via CLI, the project logic cannot overwrite it.

Also note that BaseOutputPath is actually used in the SDK defaults (defaulted to bin\), but it will try to append the configuration name by default..

Upvotes: 7

Related Questions