# Script compilation with MSBuild (Experimental)

> Compile scripts with MSBuild as an experimental alternative to the default Unity script compilation pipeline.

> **Important:**
>
> Script compilation with MSBuild is an experimental feature in this version of Unity. It's provided as a technical preview for functional testing and validation and is not suitable for production use.

By default Unity compiles scripts with its own script compilation pipeline, which determines what gets compiled automatically based on [folder names](/engine/6000.7/manual/get-started/project-configuration/special-folders.md), [file locations](/engine/6000.7/manual/scripting/compilation-and-code-reload/script-compilation/assembly-definition-files/script-compile-order-folders.md), and [assembly definition](/engine/6000.7/manual/scripting/compilation-and-code-reload/script-compilation/assembly-definition-files.md) (.asmdef) files. The default pipeline generates [C# project (.csproj) files](https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-project-file-schema-reference?view=visualstudio) but they aren't authoritative. It also doesn't support [NuGet](https://learn.microsoft.com/en-us/nuget/what-is-nuget) natively, and references are resolved through `.asmdef` references, [plug-ins](/engine/6000.7/manual/scripting/compilation-and-code-reload/plug-ins.md) in the `Assets` folder, and [Unity packages](/engine/6000.7/manual/packages-list.md).

[MSBuild (Microsoft Build Engine)](https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild?view=visualstudio) 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 `<PackageReference>` and `<ProjectReference>` entries.

There are some Unity-specific limitations to the support for MSBuild features. For more informaton, refer to [MSBuild feature support in Unity](#msbuild-feature-support)

## 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:

  1. Go to **Edit** > Editor.
  2. Click on the **Editor** tab to open the [**Editor**](/engine/6000.7/manual/unity-editor/editor-settings-reference/comp-manager-group/class-editor-manager.md) settings.
  3. Navigate to the **Script Compilation** section heading.
  4. Select the **Enable MSBuild Compilation (Experimental)** checkbox.

* By supplying the [Editor command-line argument](/engine/6000.7/manual/unity-editor/command-line-arguments/editor.md) `-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**                                                                                                                                                                                                                                     |
| --------------------------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Main.EntryPoint.csproj`                | 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.                                                                          |
| `global.json`                           | Partially.                     | User keys and extra `msbuild-sdks` entries are preserved. Unity forces `sdk.version="8.0.0"`, `sdk.rollForward="latestMinor"`, and the four Unity SDK entries (`Unity.Sdk`, `Unity.EntryPoint.Sdk`, `Unity.Test.Sdk`, `Microsoft.Build.Traversal`). |
| `NuGet.config`                          | Partially.                     | Unity forces a single entry: `<add key="Unity Source" value="..."/>` under `<packageSources>`. Everything else (credentials, disabled sources, custom sources, config) is preserved.                                                                |
| `{project-name}.slnx`                   | Partially (if Unity-generated) | Filename controlled by `<SolutionGenerationName>` in `Main.EntryPoint.csproj`.                                                                                                                                                                      |
| `Assets/**/*.csproj` (`.gen.csproj`)    | No.                            | Regenerated from assembly definition on every signature change. Don't attempt to manually edit.                                                                                                                                                     |
| `Assets/**/*.csproj` (no `.gen` suffix) | 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 ##msbuild-feature-support

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](https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild?view=visualstudio).

### Unsupported features

The following MSBuild features are not supported for Unity projects:

| **Unsupported feature**                  | **Description**                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Web / WPF / WinForms SDKs                | `Microsoft.NET.Sdk.Web`, `Microsoft.NET.Sdk.Razor`, `Microsoft.NET.Sdk.Worker`, `Microsoft.NET.Sdk.WindowsDesktop`, `Microsoft.NET.Sdk.BlazorWebAssembly` and their child FrameworkReferences (`Microsoft.AspNetCore.App`, `Microsoft.WindowsDesktop.App*`) are out of scope for Unity project output.                                                                                                                                                                               |
| 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 (`<COMReference>`)           | If you need [COM](https://learn.microsoft.com/en-us/windows/win32/com/component-object-model--com--portal), build a separate C# helper assembly with standard `dotnet build` and reference the output DLL via `Assets/Plugins/`.                                                                                                                                                                                                                                                     |
| Target frameworks above Net Standard 2.1 | Manually authored .csproj files can technically set any `TargetFramework` value and MSBuild accepts it, builds the assembly, and copies it to `Library/ScriptAssemblies/HandLib.dll`. However, Unity scripting backends only support `netstandard2.1` (or opt-in `net10.0` via `enablelastdotnet.config`). Don't rely on other target frameworks even though the build accepts them.                                                                                                 |
| Multiple target frameworks               | `<TargetFrameworks>net10.0;netstandard2.1</TargetFrameworks>` on a manually authored .csproj is accepted at the restore layer (both target frameworks appear in `project.assets.json`), but the actual build produces a single DLL. Unity's assembly-loading pipeline is one DLL per assembly name and multiple target framework output is not supported.You can't ship a single project targeting multiple frameworks. If that's requird, produce one .csproj per target framework. |

### 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. As such, Unity applies the following restrictions to the use of NuGet:

* Any NuGet packages that use Unity APIs are blocked by the compilation pipeline.
* Any UPM or Asset Store packages that import a NuGet package are blocked.
* Any package that causes functional conflicts with Engine behavior or poses a security risk to users can be blocked at Unity's discretion.

**Base class libraries (BCLs)**

Unity bundles specific versions of several .NET libraries with an Editor installation. These versions are dependencies for certain Unity features and are chosen to align with Unity's internal .NET runtime version. The currently bundled packages are:

* `System.Text.Json`
* `System.Collections.Immutable`
* `System.Reflection.Metadata`
* `System.Runtime.CompilerServices.Unsafe`

> **Note:**
>
> This list is expected to evolve as Unity's runtime and feature set change.

In the .NET assembly model it's not possible to have two versions of the same package present at once. If you add your own copy of one of the listed packages, Unity discards it and use its own bundled version instead.

A package you depend on that depends on a different version of one of these libraries will be resolved against Unity's version. In most cases this is transparent, but if your dependency requires an API surface that is not present in Unity's version, you might encounter incompatibilities.

#### MSBuild Restore

MSBuild in Unity supports both [MSBuild restore](https://learn.microsoft.com/en-us/nuget/consume-packages/package-restore#restore-by-using-msbuild) and a faster [NuGet restore](https://learn.microsoft.com/en-us/nuget/consume-packages/package-restore#restore-by-using-the-nuget-cli) engine. NuGet restore is the default and it bypasses MSBuild `Restore` targets entirely. Any `<Target Name="…" BeforeTargets="Restore">` or `AfterTargets="Restore"` hooks don't run under the default configuration. To switch to the MSBuild restore engine and make `Restore` target hooks run normally, launch the Editor with the `-disable-nuget-restore` command-line argument.

#### Custom property settings and targets

If you create [`Directory.Build.props` and `Directory.Build.targets`](https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-by-directory?view=visualstudio) files, they're disabled by default and setting `<ImportDirectoryBuildProps>true</ImportDirectoryBuildProps>` alone doesn't enable them. To enable them, you must also create the file `ProjectSettings/enableDirectoryBuild.config`. Even when enabled, `Directory.Build.*` is never applied to package projects or to `Main.EntryPoint.csproj`.

Prefer `UnityProject.Build.props` for project-wide extensions. It has the same reach as an enabled `Directory.Build.props` for user projects, without requiring the config file.

#### Custom build configurations

`<Configurations>` in `UnityProject.Build.props` accepts additional entries (for example, `Custom+Beta`) without error, but the Editor UI never selects them for its own compilation invocations. The Editor only recognizes standard build configurations configurable through the UI. For more information, refer to [Introduction to building](/engine/6000.7/manual/building-and-publishing/building-introduction.md).

You can define custom configurations for direct `dotnet build -p:Configuration=Custom+Beta` 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.

#### Output paths for Unity assemblies

Unity uses the properties `OutputPath`, `IntermediateOutputPath`, `PublishDir`, and `UnityOutputDir` to define intermediate and output paths to `Library/MSBuild/artifacts/{obj,bin,Publish}` and copies final DLLs to `Library/ScriptAssemblies/`. These are critical for the Editor's assembly discovery. Overriding them on a `Unity.Sdk` .csproj breaks the Editor's ability to find the compiled assembly.

#### Framework reference not supported with .NET Standard 2.1

Any [`FrameworkReference`](https://learn.microsoft.com/en-us/dotnet/core/project-sdk/msbuild-props#frameworkreference) item, such as `<FrameworkReference Include="Microsoft.NETCore.App"/>`, fails with `NETSDK1073: The FrameworkReference '…' was not recognized` with Unity's default `TargetFramework=netstandard2.1`.

`FrameworkReference` by design requires a `net5+`/`netcore` target framework. To use it, you must opt into `net10.0` via `enablelastdotnet.config`.

#### Build events

The property form of [build events](https://learn.microsoft.com/en-us/cpp/build/how-to-use-build-events-in-msbuild-projects?view=msvc-170) works. For example, `<PreBuildEvent>echo hello</PreBuildEvent>` fires an `echo` command correctly. Some Visual Studio specific macros inside the string (such as `$(SolutionDir)` might work depending on evaluation context, but others such as `$(ProjectDir)` that assume the legacy .csproj format might not always expand. Prefer the target-based form (`<Target Name="X" BeforeTargets="PreBuildEvent">`).

#### Disabling Unity's package source

Never disable the package source `Unity Source` unless you have an alternative way to find Unity SDK packages. Adding `<disabledPackageSources><add key="Unity Source" value="true"/></disabledPackageSources>` to `NuGet.config` disables Unity's own SDK feed and Unity's `MaintainNugetConfig` 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 `Unity.Sdk` (and the other Unity MSBuild SDKs) fails.

#### Third-party SDKs via Nuget SDK resolution

Third-party SDKs, such as [Microsoft.Build.NoTargets](https://www.nuget.org/packages/Microsoft.Build.NoTargets), referenced via `<Project Sdk="Name/version">` (NuGet-based SDK resolution) don't inherit Unity's output-path overrides, so `obj/` and `bin/` land inside `Assets/…/` and clutter the Asset Database.

Place third-party SDK .csproj files inside a folder suffixed with `~`, for example `Assets/OrchLib~/OrchLib.csproj`. The folder is then excluded from the auto-include, so you must explicitly reference it with `<ProjectReference Include="OrchLib~/OrchLib.csproj"/>` from `Main.EntryPoint.csproj`.

#### Assemblies referenced via package reference

Due to a known issue, analyzers and other assemblies referenced via `<PackageReference>` in manually-authored .csproj files might be restored but never run. Working alternatives are as follows:

* **Asset-labeled analyzers**: Place the analyzer DLL under `Assets/`, set the asset's label to `RoslynAnalyzer`. For more information, refer to [Create and use a Roslyn analyzer](/engine/6000.7/manual/scripting/debugging-and-diagnostics/roslyn-analyzers/create-roslyn-analyzer.md).
* **Scoped analyzers via `{AssemblyName}.Build.props`**: Manually declare `<Analyzer Include="path/to/analyzer.dll"/>` in a `{name}.Build.props` next to an .asmdef-generated .csproj. Applies to direct referencers of that assembly.

#### 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 `<ProjectReference>` entries. Unity doesn't overwrite custom solution folders and non-project sections.

#### Embedding resources

Default [`EmbeddedResource`](https://learn.microsoft.com/en-us/visualstudio/msbuild/common-msbuild-project-items?view=visualstudio#embeddedresource) items are disabled. You must must explicitly `<EmbeddedResource Include="…"/>`, but the pipeline itself works.

[`.resx` files](https://learn.microsoft.com/en-us/visualstudio/msbuild/how-to-build-a-project-that-has-resources?view=visualstudio) are compiled into `.resources` and embedded in the DLL. [Satellite assemblies](https://learn.microsoft.com/en-us/dotnet/core/extensions/create-satellite-assemblies) for culture variants (such as `Strings.fr.resx` producing `fr/{Name}.resources.dll`) are subject to whether the satellite DLL reaches `Library/ScriptAssemblies/`, which is subject to the same DLL pattern matching filter as any other output.

## Additional resources

* [Organizing scripts into assemblies](/engine/6000.7/manual/scripting/compilation-and-code-reload/script-compilation/assembly-definition-files.md)
* [Building and publishing](/engine/6000.7/manual/building-and-publishing.md)
