MSBuild compilation in Unity
Enable script compilation with MSBuild and understand the Unity-specific limitations to MSBuild features.
Read time 10 minutesLast updated 7 days ago
By default Unity compiles scripts with its own script compilation pipeline, which determines what gets compiled automatically based on folder names, file locations, and assembly definition (.asmdef) files. The default pipeline generates C# project (.csproj) files but they aren't authoritative. It also doesn't support NuGet natively, and references are resolved through references, plug-ins in the folder, and Unity packages.
.asmdefAssetsMSBuild (Microsoft Build Engine) is the build platform for .NET and Visual Studio and is available experimentally as an alternative script compilation pipeline. MSBuild differs from Unity's default script compilation pipeline in the following ways:
- MSBuild is an explicit system where what's compiled is determined by authoritative project (.csproj) and solution (.slnx) files that list files, references, and build targets.
- MSBuild supports NuGet natively and handles dependency resolution via explicit and
<PackageReference>entries.<ProjectReference>
There are some Unity-specific limitations to the support for MSBuild features. For more informaton, refer to MSBuild feature support in Unity
Enable script compilation with MSBuild
You can enable script compilation with MSBuild in one of the following ways:
-
Through the Editor Settings menu in the Unity Editor. Perform the following steps to activate MSBuild compilation through the Editor Settings menu:
- Go to Edit > .Project Settings
?
- Click on the Editor tab to open the Editor settings.
- Navigate to the Script Compilation section heading.
- Select the Enable MSBuild Compilation (Experimental) checkbox.
- Go to Edit >
-
By supplying the Editor command-line argument.
-enable-msbuild
In either case the Editor must be restarted for the change to take effect.
MSBuild files reference
When MSBuild compilation is enabled, the following files appear at the project root. In some cases you can edit these files and your changes are preserved. In other cases Unity controls the file contents and changes you make are overwritten. For further details, refer to the table:
File | Editable | Description |
|---|---|---|
| Yes (write-once) | Unity creates this file if missing and never overwrites it. You can add elements such as PropertyGroups, targets, and imports to this file and your changes are preserved. |
| Partially. | User keys and extra |
| Partially. | Unity forces a single entry: |
| Partially (if Unity-generated) | Filename controlled by |
| No. | Regenerated from assembly definition on every signature change. Don't attempt to manually edit. |
| Yes. | Manually-authored .csproj files are supported. Unity's pattern-matching for file types detects and includes them in compilation automatically. |
MSBuild feature support in Unity
All MSBuild features are supported in Unity projects unless documented otherwise in this section. For the authoritative reference on MSBuild features, refer to the Microsoft documentation.
Unsupported features
The following MSBuild features are not supported for Unity projects:
Unsupported feature | Description |
|---|---|
| Web / WPF / WinForms SDKs | |
| Non-C# compilers (Vbc / Fsc) | Unity's assembly definition model and C# compilation pipeline assume C#. VB.NET and F# projects aren't supported. |
COM Interop ( | If you need COM, build a separate C# helper assembly with standard |
| Target frameworks above Net Standard 2.1 | Manually authored .csproj files can technically set any |
| Multiple target frameworks | |
Limitations and best practices
The following section details MSBuild features that Unity supports to a limited extent and where particular workarounds or best practices apply.
Restrictions on use of NuGet
Unity doesn't support the use of NuGet as an alternative to the Unity Package Manager (UPM) for developing and distributing Unity Editor or Engine functionality. Unity restricts which NuGet packages a project can use, and checks every restored package against a policy before it compiles any code. A package that violates the policy fails the build with the error or . You can't opt out of these checks.
MSBU0006MSBU0007Unity also bundles specific versions of several .NET base class libraries (BCLs) with an Editor installation, and resolves your project against those versions instead of any copy you add yourself.
For more information about the restrictions, the checks, and the bundled libraries, refer to NuGet package policy.
MSBuild Restore
MSBuild in Unity supports both MSBuild restore and a faster NuGet restore engine. NuGet restore is the default and it bypasses MSBuild targets entirely. Any or hooks don't run under the default configuration. To switch to the MSBuild restore engine and make target hooks run normally, launch the Editor with the command-line argument.
Restore<Target Name="…" BeforeTargets="Restore">AfterTargets="Restore"Restore-disable-nuget-restoreCustom property settings and targets
If you create and files, they're disabled by default and setting alone doesn't enable them. To enable them, you must also create the file . Even when enabled, is never applied to package projects or to .
Directory.Build.propsDirectory.Build.targets<ImportDirectoryBuildProps>true</ImportDirectoryBuildProps>ProjectSettings/enableDirectoryBuild.configDirectory.Build.*Main.EntryPoint.csprojPrefer for project-wide extensions. It has the same reach as an enabled for user projects, without requiring the config file.
UnityProject.Build.propsDirectory.Build.propsCustom build configurations
<Configurations>UnityProject.Build.propsCustom+BetaYou can define custom configurations for direct invocations from the command line, but the Editor never builds with them. The C# preprocessor defines added under a custom configuration are not active in Editor-driven builds.
dotnet build -p:Configuration=Custom+BetaOutput paths for Unity assemblies
Unity uses the properties , , , and to define intermediate and output paths to and copies final DLLs to . These are critical for the Editor's assembly discovery. Overriding them on a .csproj breaks the Editor's ability to find the compiled assembly.
OutputPathIntermediateOutputPathPublishDirUnityOutputDirLibrary/MSBuild/artifacts/{obj,bin,Publish}Library/ScriptAssemblies/Unity.SdkFramework reference not supported with .NET Standard 2.1
Any item, such as , fails with with Unity's default .
FrameworkReference<FrameworkReference Include="Microsoft.NETCore.App"/>NETSDK1073: The FrameworkReference '…' was not recognizedTargetFramework=netstandard2.1FrameworkReferencenet5+netcorenet10.0enablelastdotnet.configBuild events
The property form of build events works. For example, fires an command correctly. Some Visual Studio specific macros inside the string (such as might work depending on evaluation context, but others such as that assume the legacy .csproj format might not always expand. Prefer the target-based form ().
<PreBuildEvent>echo hello</PreBuildEvent>echo$(SolutionDir)$(ProjectDir)<Target Name="X" BeforeTargets="PreBuildEvent">Disabling Unity's package source
Never disable the package source unless you have an alternative way to find Unity SDK packages. Adding to disables Unity's own SDK feed and Unity's doesn't re-enable it. Restore continues to succeed as long as the Unity SDK packages are already in the local NuGet cache, but on a fresh cache, restore of (and the other Unity MSBuild SDKs) fails.
Unity Source<disabledPackageSources><add key="Unity Source" value="true"/></disabledPackageSources>NuGet.configMaintainNugetConfigUnity.SdkThird-party SDKs via Nuget SDK resolution
Third-party SDKs, such as Microsoft.Build.NoTargets, referenced via (NuGet-based SDK resolution) don't inherit Unity's output-path overrides, so and land inside and clutter the Asset Database.
<Project Sdk="Name/version">obj/bin/Assets/…/Place third-party SDK .csproj files inside a folder suffixed with , for example . The folder is then excluded from the auto-include, so you must explicitly reference it with from .
~Assets/OrchLib~/OrchLib.csproj<ProjectReference Include="OrchLib~/OrchLib.csproj"/>Main.EntryPoint.csprojImporting analyzers
You can import code analyzers in several ways.
Referencing an analyzer in
{AssemblyName}.Build.propsIf your project is using MSBuild compilation exclusively, you can add a reference to the analyzer in a file, using . This file is entirely user-authored and Unity never overwrites or modifies it.
{AssemblyName}.Build.props<Analyzer Include="path/to/analyzer.dll"/>{AssemblyName}.csproj.genAssemblyName.gen.csprojAssemblyName.Build.propsAssemblyName.gen.Build.propsImporting an analyzer via an asset label (conversion workflow)
If your project is converting from assembly definition files to MSBuild compilation, place the analyzer DLL under and set the asset's label to . For more information, refer to Create and use a Roslyn analyzer. The analyzer scope is determined by the location of the DLL:
Assets/RoslynAnalyzer- Global analyzers: A labeled DLL outside any assembly definition folder applies to every project, through .
Library/MSBuild/UnityProps/Common/UnityRoslynAnalyzers.props - Scoped analyzers: A DLL inside an assembly definition folder applies only to that assembly and the assemblies that reference it, through . The
{AssemblyName}.Analyzers.gen.propsfile is automatically generated during conversion from .asmdef to .csproj.{AssemblyName}.Analyzers.gen.props
Referencing an analyzer in a manually authored .csproj
In a manually authored .csproj file, you can also add the reference as a in the .csproj file itself.
<PackageReference>Solution file maintenance
When you add and remove projects, for example by adding a new assembly definition or installing a package, their task correctly updates the solution (.slnx) file. Project membership within the solution correctly reflects the reference graph defined by entries. Unity doesn't overwrite custom solution folders and non-project sections.
<ProjectReference>Embedding resources
Default items are disabled. You must must explicitly , but the pipeline itself works.
EmbeddedResource<EmbeddedResource Include="…"/>.resx.resourcesStrings.fr.resxfr/{Name}.resources.dllLibrary/ScriptAssemblies/