You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.
dotnet / msbuild Public
The MSBuild engine doesn't have a notion of a “project reference”—it only provides the MSBuild task to allow cross-project communication.
That's a powerful tool, but no one would want to have to specify how to build every single reference in every single project. The common targets introduce an item, ProjectReference , and a default process for building references declared via that item.
Default protocol implementation:
In its simplest form, a project need only specify the path to another project in a ProjectReference item. For example,
ItemGroup> ProjectReference Include="..\..\some\other\project.csproj" /> ItemGroup>
Importing Microsoft.Common.targets includes logic that consumes these items and transforms them into compile-time references before the compiler runs.
This document describes that process, including what is required of a project to be referenceable through a ProjectReference . It is intended for MSBuild SDK maintainers, and those who have created a completely custom project type that needs to interoperate with other projects. It may also be of interest if you'd like to see the implementation details of references. Understanding the details should not be necessary to use ProjectReferences in your project.
The bulk of the work of transforming ProjectReference items into something suitable to feed the compiler is done by tasks listed in the ResolveReferencesDependsOn property defined in Microsoft.Common.CurrentVersion.targets .
There are empty hooks in the default targets for
AssignProjectConfiguration runs when building in a solution context, and ensures that the right Configuration and Platform are assigned to each reference. For example, if a solution specifies (using the Solution Build Manager) that for a given solution configuration, a project should always be built Release , that is applied inside MSBuild in this target.
PrepareProjectReferences then runs, ensuring that each referenced project exists (creating the item @(_MSBuildProjectReferenceExistent) ).
_ComputeProjectReferenceTargetFrameworkMatches calls GetTargetFrameworks in existent ProjectReferences and determines the parameters needed to produce a compatible build by calling the AssignReferenceProperties task for each reference that multitargets.
ResolveProjectReferences does the bulk of the work, building the referenced projects and collecting their outputs.
After the compiler is invoked, GetCopyToOutputDirectoryItems pulls child-project outputs into the current project to be copied to its output directory.
When Clean ing the output of a project, CleanReferencedProjects ensures that referenced projects also clean.
These targets should exist in a project to be compatible with the common targets' ProjectReference (unless marked with the SkipNonexistentTargets='true' metadatum). Some are called only conditionally.
These targets are all defined in Microsoft.Common.targets and are defined in Microsoft SDKs. You should only have to implement them yourself if you require custom behavior or are authoring a project that doesn't import the common targets.
If implementing a project with an “outer” (determine what properties to pass to the real build) and “inner” (fully specified) build, only GetTargetFrameworks is required in the “outer” build. The other targets listed can be “inner” build only.
GetTargetFrameworks and GetTargetFrameworksWithPlatformForSingleTargetFramework are skippable if nonexistent since some project types (for example, wixproj projects) may not define them. See this comment for more details.
As with all MSBuild logic, targets can be added to do other work with ProjectReference s.
In particular, NuGet depends on being able to identify referenced projects' package dependencies, and calls some targets that are imported through Microsoft.Common.targets to do so. At the time of writing this this is in NuGet.targets .
Microsoft.AppxPackage.targets adds a dependency on the target GetPackagingOutputs .
As of MSBuild 16.10, it is possible to gather additional properties from referenced projects. To do this, the referenced project should declare an AdditionalTargetFrameworkInfoProperty item for each property that should be gathered for referencing projects. For example:
ItemGroup> AdditionalTargetFrameworkInfoProperty Include="SelfContained"/> AdditionalTargetFrameworkInfoProperty Include="_IsExecutable"/> ItemGroup>
These properties will then be gathered via the GetTargetFrameworks call. They will be available to the referencing project via the AdditionalPropertiesFromProject metadata on the _MSBuildProjectReferenceExistent item. The AdditionalPropertiesFromProject value will be an XML string which contains the values of the properties for each TargetFramework in the referenced project. For example:
⚠️ This format is being changed. Soon, the schema will replace with . You can opt into that behavior early by setting the _UseAttributeForTargetFrameworkInfoPropertyNames property to true. This property will have no effect after the transition is complete.
AdditionalProjectProperties> net5.0> SelfContained>trueSelfContained> _IsExecutable>true_IsExecutable> net5.0> net5.0-windows> SelfContained>falseSelfContained> _IsExecutable>true_IsExecutable> net5.0-windows> AdditionalProjectProperties>
The NearestTargetFramework metadata will be the target framework which was selected as the best one to use for the reference (via GetReferenceNearestTargetFrameworkTask ). This can be used to select which set of properties were used in the target framework that was active for the reference.
As of version 17.0, MSBuild can now dynamically figure out what platform a ProjectReference should build as. This includes a new target and task to determine what the SetPlatform metadata should be, or whether to undefine the platform so the referenced project builds with its default platform.
Note: If a ProjectReference has SetPlatform metadata defined already, the negotiation logic is skipped over.
In addition to the above task and target, .vcxproj and .nativeproj projects will receive an extra MSBuild call to the GetTargetFrameworks target. Previously, TargetFramework negotiation skipped over these projects because they could not multi-target in the first place. Because SetPlatform negotiation needs information given from the GetTargetFrameworks target, it is required that the _GetProjectReferenceTargetFrameworkProperties target calls the MSBuild task on the ProjectReference.
This means most projects will see an evaluation with no global properties defined, unless set by the user.
First, set the property EnableDynamicPlatformResolution to true for every project in your solution. The easiest way to do this is by creating a Directory.Build.props file and placing it at the root of your project directory:
Project> PropertyGroup> EnableDynamicPlatformResolution>trueEnableDynamicPlatformResolution> PropertyGroup> Project>
If only set in one project, the SetPlatform metadata will carry forward to every consecutive project reference.
Next, every referenced project is required to define a Platforms property, where Platforms is a semicolon-delimited list of platforms that project could build as. For .vcxproj or .nativeproj projects, Platforms is constructed from the ProjectConfiguration items that already exist in the project. For managed SDK projects, the default is AnyCPU . Managed non-SDK projects need to define this manually.
Lastly, a PlatformLookupTable may need to be defined for more complex scenarios. A PlatformLookupTable is a semicolon-delimited list of mappings between platforms.
Some cases of ProjectReference s require a $(PlatformLookupTable) to correctly determine what a referenced project should build as. References between managed and unmanaged projects also get a default lookup table that can be opted out of by setting the property UseDefaultPlatformLookupTables to false. See the table below for details.
Note: Defining a PlatformLookupTable overrides the default mapping.
Project Reference Type | PlatformLookupTable Required? | Notes |
---|---|---|
Unmanaged -> Unmanaged | No | |
Managed -> Managed | No | |
Unmanaged -> Managed | Optional | Uses default mapping: Win32=x86 |
Managed -> Unmanaged | Yes when the project is AnyCPU | Uses default mapping: x86=Win32 |
Example: Project A: Managed, building as AnyCPU , has a ProjectReference on Project B. Project B: Unmanaged, has $(Platforms) constructed from its Platform metadata from its ProjectConfiguration items, defined as x64;Win32 .
Because AnyCPU does not map to anything architecture-specific, a custom mapping must be defined. Project A can either:
Example of project A defining a lookup table directly on the ProjectReference :
ItemGroup> ProjectReference Include="B.csproj" PlatformLookupTable="AnyCPU=Win32"> ItemGroup>