Skip to content

Instantly share code, notes, and snippets.

@rainersigwald
Last active August 29, 2023 18:48
Show Gist options
  • Save rainersigwald/1fbd21a24a41b9b7c6204cb9cfcbb1cf to your computer and use it in GitHub Desktop.
Save rainersigwald/1fbd21a24a41b9b7c6204cb9cfcbb1cf to your computer and use it in GitHub Desktop.
Customizing ProjectReferences

Customizing ProjectReferences

Project references have a dual role in MSBuild. They ensure the correct build order, and allow a project to locate the outputs of other projects to reference them.

Solution Build Dependencies

Visual Studio allows creating project dependencies in solution (.sln) files. These are stored in the solution file and are only respected when building a solution or inside Visual Studio. If you build a single project, this type of dependency is ignored.

TODO: transformed into ProjectReferences

ProjectReference items

The common MSBuild targets used in most project types express dependencies between projects using ProjectReference items. The Include value of the item is a path to the project to reference, and metadata on the project controls aspects of the referencing process.

TODO: this doesn't work in VS. Except maybe Properties do, somehow? See https://twitter.com/onovotny/status/1052219278173396992

ReferenceOutputAssembly

By default, the output of projects from ProjectReferences is treated as a Reference in the current project--for example, if you reference A.csproj which produces A.dll, A.dll will be passed to the compiler as an assembly reference.

If the metadatum ReferenceOutputAssembly is set to false, the dependency is considered "build ordering only", and the output of the referenced project will not be automatically added to the current projects References.

Targets

If no Targets metadata is specified, the default target of the referenced project (usually Build) will be built and its output referenced in the current project.

If you would like to build a different target (or targets), specify them as a semicolon-delimited list in the Targets metadatum.

NOTE: Specifying a nonstandard target usually also needs ReferenceOutputAssembly=false, unless the target returns the primary output assembly of the referenced project.

OutputItemType

When set, the output of the project is put into the specified item, in addition to the default Reference (which can be disabled with ReferenceOutputAssembly). It is sometimes useful to set <OutputItemType>Content</OutputItemType> to copy the output of the other project as a "plain file" rather than a .NET assembly reference.

Specifying properties

WARNING: global properties are INFECTIOUS, so be careful with this

In RPR in common

  • SetConfiguration
  • SetPlatform
  • GlobalPropertiesToRemove

Built into the MSBuild task

  • Properties
  • AdditionalProperties
  • UndefineProperties

Referencing multitargeted projects

When using SDK-style projects, a single project may be built for different TargetFrameworks, resulting in multiple, incompatible outputs for the project. When such a project is a ProjectReference, the common targets automatically select the most appropriate TargetFramework to reference.

TargetFramework negotiation

NuGet's asset-compatibility matrix (TODO LINK) is used to select the best available TargetFramework of the referenced project. If no compatible TargetFramework is available, an error like

TODO

will be emitted from the referencing project.

The AssetTargetFallback property of the referencing project is considered when selecting a compatible framework.

Overriding TargetFramework

SetTargetFramework="TargetFramework=.."

Disabling TargetFramework negotiation

SkipGetTargetFrameworkProperties=true implies there's a single TF or you want the outer build

@benrobot
Copy link

benrobot commented Dec 1, 2021

Hopefully, the following information will help fill in some TODOs or someone else who ends up here trying to figure out how to control build order with non-matching frameworks.

Example of TargetFramework negotiation error:

C:\Program Files\dotnet\sdk\5.0.403\Microsoft.Common.CurrentVersion.targets(1718,5): error : 
Project '..\ProjectOne\ProjectOne.csproj' targets 'net5.0'. 
It cannot be referenced by a project that targets '.NETStandard,Version=v2.1'. 
[C:\AbsolutePath\ProjectTwo\ProjectTwo.csproj]

The following worked for me to create a build dependency from a multi-target project to a single target project:

  <ItemGroup>
    <ProjectReference Include="..\SingleTargetProject\SingleTargetProject.csproj" >
      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
      <SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties>
      <Private>false</Private>
      <UndefineProperties>TargetFramework</UndefineProperties>
    </ProjectReference>
  </ItemGroup>

Without <UndefineProperties>TargetFramework</UndefineProperties> the error was

C:\Program Files\dotnet\sdk\5.0.403\Sdks\Microsoft.NET.Sdk\targets\Microsoft.PackageDependencyResolution.targets(241,5): error NETSDK1005: 
Assets file 'C:\AbsolutePath\SingleTargetProject\obj\project.assets.json' doesn't have a target for 'netstandard2.1'. 
Ensure that restore has run and that you have included 'netstandard2.1' in the TargetFrameworks for your project. 
[C:\AbsolutePath\SingleTargetProject\SingleTargetProject.csproj]

Without <SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties> the error was

C:\Program Files\dotnet\sdk\5.0.403\Microsoft.Common.CurrentVersion.targets(1718,5): error : 
Project '..\SingleTargetProject\SingleTargetProject.csproj' targets 'net5.0'. 
It cannot be referenced by a project that targets '.NETStandard,Version=v2.1'. 
[C:\AbsolutePath\MultiTargetProject\MultiTargetProject.csproj]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment