Customize your build

MSBuild projects that use the standard build process (importing Microsoft.Common.props and Microsoft.Common.targets) have several extensibility hooks that you can use to customize your build process.

Many customizable build operations are controlled by properties. It is important to know how and where to set a property value in order to have the desired effect. You can set properties on the command line (and in response files), in special files like Directory.Build.props, in imported files, or in the project file. It is important to know where a property is used, set, or changed and the order of imported files, including implicit imports from SDKs like the .NET SDK.

For a list of properties, see MSBuild common properties.

Add arguments to command-line MSBuild invocations for your project

You can set global properties on the command line. Global properties affect all project builds, including dependencies. Recall that building a project automatically triggers a possible build for all project dependencies. The normal behavior of MSBuild is to build any dependent projects that are out-of-date. Those dependent project builds are launched with the same global property settings from the command line as the original project.

A Directory.Build.rsp file in or above your source directory will be applied to command-line builds of your project. For details, see MSBuild response files.

Directory.Build.props and Directory.Build.targets

You can add a new property to every project by defining it in a single file called Directory.Build.props in the root folder that contains your source.

When MSBuild runs, Microsoft.Common.props searches your directory structure for the Directory.Build.props file. If it finds one, it imports the file and reads the properties defined within it. Directory.Build.props is a user-defined file that provides customizations to projects under a directory.

Similarly, Microsoft.Common.targets looks for Directory.Build.targets.

Directory.Build.props is imported early in the sequence of imported files, which can be important if you need a set a property that is used by imports, especially those that are implicitly imported by using the Sdk attribute, such as when using the .NET SDK in most .NET project files.


Linux-based file systems are case-sensitive. Make sure the casing of the Directory.Build.props filename matches exactly, or it won't be detected during the build process.

See this GitHub issue for more information.

Directory.Build.props example

For example, if you wanted to enable all of your projects to access the new Roslyn /deterministic feature (which is exposed in the Roslyn CoreCompile target by the property $(Deterministic)), you could do the following.

  1. Create a new file in the root of your repo called Directory.Build.props.

  2. Add the following XML to the file.

  3. Run MSBuild. Your project’s existing imports of Microsoft.Common.props and Microsoft.Common.targets find the file and import it.

Search scope

When searching for a Directory.Build.props file, MSBuild walks the directory structure upwards from your project location ($(MSBuildProjectFullPath)), stopping after it locates a Directory.Build.props file. For example, if your $(MSBuildProjectFullPath) was c:\users\username\code\test\case1, MSBuild would start searching there and then search the directory structure upward until it located a Directory.Build.props file, as in the following directory structure.


The location of the solution file is irrelevant to Directory.Build.props.

Import order

Directory.Build.props is imported very early in Microsoft.Common.props, and properties defined later are unavailable to it. So, avoid referring to properties that are not yet defined (and will evaluate to empty).

Properties that are set in Directory.Build.props can be overridden elsewhere in the project file or in imported files, so you should think of the settings in Directory.Build.props as specifying the defaults for your projects.

Directory.Build.targets is imported from Microsoft.Common.targets after importing .targets files from NuGet packages. So, it can override properties and targets defined in most of the build logic, or set properties for all your projects regardless of what the individual projects set.

When you need to set a property or define a target for an individual project that overrides any prior settings, put that logic in the project file after the final import. In order to do this in an SDK-style project, you first have to replace the SDK-style attribute with the equivalent imports. See How to use MSBuild project SDKs.


The MSBuild engine reads in all imported files during evaluation, before starting build execution for a project (including any PreBuildEvent), so these files are not expected to be modified by the PreBuildEvent or any other part of the build process. Any modifications do not take effect until the next invocation of MSBuild.exe or the next Visual Studio build. Also, if your build process contains many project builds (as with multitargeting or building dependent projects), then imported files, including are read when evaluation occurs for each individual project build.

Use case: multi-level merging

Suppose you have this standard solution structure:

  Directory.Build.props     (1)
    Directory.Build.props   (2-src)
    Directory.Build.props   (2-test)

It might be desirable to have common properties for all projects (1), common properties for src projects (2-src), and common properties for test projects (2-test).

To make MSBuild correctly merge the "inner" files (2-src and 2-test) with the "outer" file (1), you must take into account that once MSBuild finds a Directory.Build.props file, it stops further scanning. To continue scanning and merge into the outer file, place this code into both inner files:

<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

A summary of MSBuild's general approach is as follows:

  • For any given project, MSBuild finds the first Directory.Build.props upward in the solution structure, merges it with defaults, and stops scanning for more.
  • If you want multiple levels to be found and merged, then <Import...> (shown above) the "outer" file from the "inner" file.
  • If the "outer" file does not itself also import something above it, then scanning stops there.

Or more simply: the first Directory.Build.props that doesn't import anything is where MSBuild stops.

To control the import process more explicitly, use the properties $(DirectoryBuildPropsPath), $(ImportDirectoryBuildProps), $(DirectoryBuildTargetsPath), and $(ImportDirectoryBuildTargets). The property $(DirectoryBuildPropsPath) specifies the path to the Directory.Build.props file to use; similarly, $(DirectoryBuildTargetsPath) specifies the path to the Directory.Build.targets file.

The Boolean properties $(ImportDirectoryBuildProps) and $(ImportDirectoryBuildTargets) are set to true by default, so MSBuild normally searches for these files, but you can set them to false to prevent MSBuild from importing them.

Choose between adding properties to a .props or .targets file

MSBuild is import-order dependent, and the last definition of a property (or a UsingTask or target) is the definition used.

When using explicit imports, you can import from a .props or .targets file at any point. Here is the widely used convention:

  • .props files are imported early in the import order.

  • .targets files are imported late in the build order.

This convention is enforced by <Project Sdk="SdkName"> imports (that is, the import of Sdk.props comes first, before all of the contents of the file, then Sdk.targets comes last, after all of the contents of the file).

When deciding where to put the properties, use the following general guidelines:

  • For many properties, it doesn't matter where they're defined, because they're not overwritten and will be read only at execution time.

  • For behavior that might be customized in an individual project, set defaults in .props files.

  • Avoid setting dependent properties in .props files by reading the value of a possibly customized property, because the customization won't happen until MSBuild reads the user's project.

  • Set dependent properties in .targets files, because they'll pick up customizations from individual projects.

  • If you need to override properties, do it in a .targets file, after all user-project customizations have had a chance to take effect. Be cautious when using derived properties; derived properties may need to be overridden as well.

  • Include items in .props files (conditioned on a property). All properties are considered before any item, so user-project property customizations get picked up, and this gives the user's project the opportunity to Remove or Update any item brought in by the import.

  • Define targets in .targets files. However, if the .targets file is imported by an SDK, remember that this scenario makes overriding the target more difficult because the user's project doesn't have a place to override it by default.

  • If possible, prefer customizing properties at evaluation time over changing properties inside a target. This guideline makes it easier to load a project and understand what it's doing.


By default, Microsoft.Common.props imports $(MSBuildProjectExtensionsPath)$(MSBuildProjectFile).*.props and Microsoft.Common.targets imports $(MSBuildProjectExtensionsPath)$(MSBuildProjectFile).*.targets. The default value of MSBuildProjectExtensionsPath is $(BaseIntermediateOutputPath), obj/. NuGet uses this mechanism to refer to build logic delivered with packages; that is, at restore time, it creates {project}.nuget.g.props files that refer to the package contents.

You can disable this extensibility mechanism by setting the property ImportProjectExtensionProps to false in a Directory.Build.props or before importing Microsoft.Common.props.


Disabling MSBuildProjectExtensionsPath imports will prevent build logic delivered in NuGet packages from applying to your project. Some NuGet packages require build logic to perform their function and will be rendered useless when this is disabled.

.user file

Microsoft.Common.CurrentVersion.targets imports $(MSBuildProjectFullPath).user if it exists, so you can create a file next to your project with that additional extension. For long-term changes you plan to check into source control, prefer changing the project itself, so that future maintainers do not have to know about this extension mechanism.

MSBuildExtensionsPath and MSBuildUserExtensionsPath


Using these extension mechanisms makes it harder to get repeatable builds across machines. Try to use a configuration that can be checked into your source control system and shared among all developers of your codebase.

By convention, many core build logic files import


before their contents, and


afterward. This convention allows installed SDKs to augment the build logic of common project types.

The same directory structure is searched in $(MSBuildUserExtensionsPath), which is the per-user folder %LOCALAPPDATA%\Microsoft\MSBuild. Files placed in that folder will be imported for all builds of the corresponding project type run under that user's credentials. You can disable the user extensions by setting properties named after the importing file in the pattern ImportUserLocationsByWildcardBefore{ImportingFileNameWithNoDots}. For example, setting ImportUserLocationsByWildcardBeforeMicrosoftCommonProps to false would prevent importing $(MSBuildUserExtensionsPath)\$(MSBuildToolsVersion)\Imports\Microsoft.Common.props\ImportBefore\*.

Custom configuration based on project language

If you need different behaviors depending on the .NET language (C#, Visual Basic, or F#), you can add property groups with conditions that depend on the project file extension in $(MSBuildProjectExtension) to define language-specific properties and their values.

<PropertyGroup Condition="'$(MSBuildProjectExtension)' == '.vbproj'">
   <!-- Put VB-only property definitions here -->
<PropertyGroup Condition="'$(MSBuildProjectExtension)' == '.fsproj'">
   <!-- Put F#-only property definitions here -->
<PropertyGroup Condition="'$(MSBuildProjectExtension)' == '.csproj'">
   <!-- Put C#-only property definitions here -->

Handle generated files

In any given build, files that get generated during the build behave differently from static files (such as source files). For this reason, it's important to understand How MSBuild Builds Projects. The two phases are the evaluation phase and the execution phase. During the evaluation phase, MSBuild reads your project, imports everything, creates properties, expands globs for items, and sets up the build process. During the execution phase, MSBuild performs the build by running targets and tasks with the data it parsed during the evaluation phase.

Files generated during execution don't exist during the evaluation phase, therefore they aren't included in the build process. To solve this problem, you must manually add the generated files into the build process. The recommended way to do this is by adding the new file to the Content or None items before the BeforeBuild target, as in the following example:

<Target Name="MyTarget" BeforeTargets="BeforeBuild">
  <!-- Some logic that generates your file goes here -->
  <!-- Generated files should be placed in $(IntermediateOutputPath) -->

    <!-- If your generated file was placed in `obj\` -->
    <None Include="$(IntermediateOutputPath)" CopyToOutputDirectory="PreserveNewest"/>
    <!-- If you know exactly where that file is going to be, you can hard code the path. -->
    <None Include="some\specific\path\" CopyToOutputDirectory="PreserveNewest"/>
    <!-- If you want to capture "all files of a certain type", you can glob like so. -->
    <None Include="some\specific\path\*.xyz" CopyToOutputDirectory="PreserveNewest"/>
    <None Include="some\specific\path\*.*" CopyToOutputDirectory="PreserveNewest"/>

Adding your generated file to None or Content is sufficient for the build process to see it. You also want to ensure it gets added at the right time. Ideally, your target runs before BeforeBuild. AssignTargetPaths is another possible target, as it is the final opportunity to modify None and Content items (among others) before they are transformed into new items. See Common Item Types.

Customize the solution build


Customizing the solution build in this way applies only to command-line builds with MSBuild.exe. It does not apply to builds inside Visual Studio. For this reason, it is not recommended to put customization at the solution level. A better alternative for customizing all projects in a solution is to use Directory.Build.props and files in the solution folder, as discussed elsewhere in this article.

When MSBuild builds a solution file, it first translates it internally into a project file and then builds that. The generated project file imports before.{solutionname}.sln.targets before defining any targets and after.{solutionname}.sln.targets after importing targets, including targets installed to the $(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\SolutionFile\ImportBefore and $(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\SolutionFile\ImportAfter directories.

For example, you could define a new target to write a custom log message after building MyCustomizedSolution.sln by creating a file in the same directory named after.MyCustomizedSolution.sln.targets that contains

 <Target Name="EmitCustomMessage" AfterTargets="Build">
   <Message Importance="High" Text="The solution has completed the Build target" />

The solution build is separate from the project builds, so settings here do not affect project builds.

When you have many solution files that you want to extend in the same way, but you don't want to write to the $(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\SolutionFile\ folder (which usually requires elevated permissions), you can create the files Directory.Solution.props and Directory.Solution.targets and place them in the root path above the solution files you want to extend. Directory.Solution.props is imported at the beginning of the solution build, and Directory.Solution.targets is imported at the end of the solution build. When you build a solution file, Directory.Build.props and Directory.Build.targets are not imported, so you must use Directory.Solution.props and Directory.Solution.targets instead. They do not implicitly import each other.

When you have Directory.Solution.props or Directory.Solution.targets in a root folder, but you have a solution under that folder that you don't want to import them, you can use the solution-specific files mentioned previously before.{solutionname}.sln.targets and after.{solutionname}.sln.targets to set the properties $(ImportDirectorySolutionProps) and $(ImportDirectorySolutionTargets) to false. Or, you can use the properties $(DirectorySolutionPropsPath) and $(DirectorySolutionTargetsPath) to specify a different location for those files. This could be helpful if you have various subsets of your solutions that require certain property values or targets common to the subsets.

Customize all .NET builds

When maintaining a build server, you might need to configure MSBuild settings globally for all builds on the server. In principle, you could modify the global Microsoft.Common.Targets or Microsoft.Common.Props files, but there is a better way. You can affect all builds of a certain project type (such as all C# projects) by using certain MSBuild properties and adding certain custom .targets and .props files.

To affect all C# or Visual Basic builds governed by an installation of MSBuild or Visual Studio, create a file Custom.Before.Microsoft.Common.Targets or Custom.After.Microsoft.Common.Targets with targets that will run before or after Microsoft.Common.targets, or a file Custom.Before.Microsoft.Common.Props or Custom.After.Microsoft.Common.Props with properties that will be processed before or after Microsoft.Common.props.

You can specify the locations of these files by using the following MSBuild properties:

  • CustomBeforeMicrosoftCommonProps
  • CustomBeforeMicrosoftCommonTargets
  • CustomAfterMicrosoftCommonProps
  • CustomAfterMicrosoftCommonTargets
  • CustomBeforeMicrosoftCSharpTargets
  • CustomBeforeMicrosoftVisualBasicTargets
  • CustomAfterMicrosoftCSharpTargets
  • CustomAfterMicrosoftVisualBasicTargets

The Common versions of these properties affect both C# and Visual Basic projects. You can set these properties in the MSBuild command line.

msbuild /p:CustomBeforeMicrosoftCommonTargets="C:\build\config\Custom.Before.Microsoft.Common.Targets" MyProject.csproj

You can also use the property AlternateCommonProps to specify a file to use instead of Microsoft.Common.props. If you ever felt the need to customize Microsoft.Common.props by modifying it directly, you could instead copy the standard version of that file into another file, and then make your changes only in the alternate version.

The best approach depends on your scenario. Using Visual Studio Extensibility, you can customize the build system and provide a mechanism for installing and managing the customizations.

If you have a dedicated build server and want to ensure that certain targets always execute on all builds of the appropriate project type that execute on that server, then using a global custom .targets or .props file makes sense. If you want the custom targets to only execute when certain conditions apply, then use another file location and set the path to that file by setting the appropriate MSBuild property in the MSBuild command line only when needed.


Visual Studio uses the custom .targets or .props files if it finds them in the MSBuild folder whenever it builds any project of the matching type. This can have unintended consequences, and if done incorrectly, can disable the ability of Visual Studio to build on your computer.

Customize C++ builds

For C++ projects, the previously mentioned custom .targets and .props files cannot be used in the same way to override default settings. Directory.Build.props is imported by Microsoft.Common.props, which is imported in Microsoft.Cpp.Default.props while most of the defaults are defined in Microsoft.Cpp.props and for a number of properties a "if not yet defined" condition cannot be used, as the property is already defined, but the default needs to be different for particular project properties defined in PropertyGroup with Label="Configuration" (see .vcxproj and .props file structure).

But, you can use the following properties to specify .props file(s) to be automatically imported before/after Microsoft.Cpp.* files:

  • ForceImportAfterCppDefaultProps
  • ForceImportBeforeCppProps
  • ForceImportAfterCppProps
  • ForceImportBeforeCppTargets
  • ForceImportAfterCppTargets

To customize the default values of properties for all C++ builds, create another .props file (say, MyProps.props), and define the ForceImportAfterCppProps property in Directory.Build.props pointing to it:


MyProps.props will be automatically imported at the very end of Microsoft.Cpp.props.

Customize all C++ builds

Customizing the Visual Studio installation isn't recommended, since it's not easy to keep track of such customizations, but if you're extending Visual Studio to customize C++ builds for a particular platform, you can create .targets files for each platform and place them in the appropriate import folders for those platforms as part of a Visual Studio extension.

The .targets file for the Win32 platform, Microsoft.Cpp.Win32.targets, contains the following Import element:

<Import Project="$(VCTargetsPath)\Platforms\Win32\ImportBefore\*.targets"

There's a similar element near the end of the same file:

<Import Project="$(VCTargetsPath)\Platforms\Win32\ImportAfter\*.targets"

Similar import elements exist for other target platforms in *%ProgramFiles32%\MSBuild\Microsoft.Cpp\v{version}\Platforms*.

Once you place the .targets file in the appropriate ImportAfter folder according to the platform, MSBuild imports your file into every C++ build for that platform. You can put multiple .targets files there, if needed.

Using Visual Studio Extensibility, further customizations are possible, such as defining a new platform. For more information, see C++ project extensibility.

Specify a custom import on the command line

For custom .targets that you want to include for a specific build of a C++ project, set one or both of the properties ForceImportBeforeCppTargets and ForceImportAfterCppTargets on the command line.

msbuild /p:ForceImportBeforeCppTargets="C:\build\config\Custom.Before.Microsoft.Cpp.Targets" MyCppProject.vcxproj

For a global setting (to affect, say, all C++ builds for a platform on a build server), there are two methods. First, you can set these properties using a system environment variable that is always set. This works because MSBuild always reads the environment and creates (or overrides) properties for all the environment variables.


This example shows the use of preprocessed MSBuild project file output to determine where to set a property.

To help you analyze the usage of a particular property you want to set, you can run MSBuild with the /preprocess or /pp argument. The output text is the result of all the imports, including the system imports like Microsoft.Common.props that are implicitly imported, and any of your own imports. With this output, you can see where your property needs to be set relative to where its value is used.

As an example, suppose you have a simple .NET Core or .NET 5 or later Console App project, and you want to customize the intermediate output folder, normally obj. The property that specifies this path is BaseIntermediateOutput. If you try putting this in a PropertyGroup element in your project file along with the various other properties that are already set there, such as TargetFramework, you would discover when you build the project that the property does not take effect. If you run MSBuild with the /pp option and search the output for BaseIntermediateOutputPath, you can see why. In this case, BaseIntermediateOutput is read and used in Microsoft.Common.props.

There's a comment in Microsoft.Common.props that says the property BaseIntermediateOutput has to be set here, before it's used by another property, MSBuildProjectExtensionsPath. You can also see that when BaseIntermediateOutputPath is initially set, there's a check for a pre-existing value, and if it's undefined, it gets set to obj.

<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">obj\</BaseIntermediateOutputPath>

So, this tells you that to set this property, it must be specified somewhere earlier than this. Just before this code in the preprocessed output, you can see that Directory.Build.props is imported, so this tells you that you can set BaseIntermediateOutputPath there and it will be set early enough to have the desired effect.

The following abbreviated preprocessed output shows the result of putting the BaseIntermediateOutput setting in Directory.Build.props. The comments at the top of standard imports include the filename and usually some helpful information about why that file is imported.

<?xml version="1.0" encoding="IBM437"?>
<Project DefaultTargets="Build">
  <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk">
  This import was added implicitly because the Project element's Sdk attribute specified "Microsoft.NET.Sdk".

C:\Program Files\dotnet\sdk\7.0.200-preview.22628.1\Sdks\Microsoft.NET.Sdk\Sdk\Sdk.props

WARNING:  DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
          created a backup copy.  Incorrect changes to this file will make it
          impossible to load or build your projects from the command-line or the IDE.

Copyright (c) .NET Foundation. All rights reserved.
  <PropertyGroup xmlns="">
      Indicate to other targets that Microsoft.NET.Sdk is being used.

      This must be set here (as early as possible, before Microsoft.Common.props)
      so that everything that follows can depend on it.

      In particular, Directory.Build.props and nuget package props need to be able
      to use this flag and they are imported by Microsoft.Common.props.
      Indicate whether the set of SDK defaults that makes SDK style project concise are being used.
      For example: globbing, importing msbuild common targets.

      Similar to the property above, it must be set here.
  <PropertyGroup Condition="'$(MSBuildProjectFullPath)' == '$(ProjectToOverrideProjectExtensionsPath)'" xmlns="">
  <!--<Import Project="$(AlternateCommonProps)" Condition="'$(AlternateCommonProps)' != ''" />-->
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="'$(AlternateCommonProps)' == ''">

C:\Program Files\Microsoft Visual Studio\2022\Preview\MSBuild\Current\Microsoft.Common.props

WARNING:  DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
          created a backup copy.  Incorrect changes to this file will make it
          impossible to load or build your projects from the command-line or the IDE.

Copyright (C) Microsoft Corporation. All rights reserved.
    <ImportByWildcardBeforeMicrosoftCommonProps Condition="'$(ImportByWildcardBeforeMicrosoftCommonProps)' == ''">true</ImportByWildcardBeforeMicrosoftCommonProps>
    <ImportByWildcardAfterMicrosoftCommonProps Condition="'$(ImportByWildcardAfterMicrosoftCommonProps)' == ''">true</ImportByWildcardAfterMicrosoftCommonProps>
    <ImportUserLocationsByWildcardBeforeMicrosoftCommonProps Condition="'$(ImportUserLocationsByWildcardBeforeMicrosoftCommonProps)' == ''">true</ImportUserLocationsByWildcardBeforeMicrosoftCommonProps>
    <ImportUserLocationsByWildcardAfterMicrosoftCommonProps Condition="'$(ImportUserLocationsByWildcardAfterMicrosoftCommonProps)' == ''">true</ImportUserLocationsByWildcardAfterMicrosoftCommonProps>
    <ImportDirectoryBuildProps Condition="'$(ImportDirectoryBuildProps)' == ''">true</ImportDirectoryBuildProps>
      Determine the path to the directory build props file if the user did not disable $(ImportDirectoryBuildProps) and
      they did not already specify an absolute path to use via $(DirectoryBuildPropsPath)
  <PropertyGroup Condition="'$(ImportDirectoryBuildProps)' == 'true' and '$(DirectoryBuildPropsPath)' == ''">
    <_DirectoryBuildPropsFile Condition="'$(_DirectoryBuildPropsFile)' == ''">Directory.Build.props</_DirectoryBuildPropsFile>
    <_DirectoryBuildPropsBasePath Condition="'$(_DirectoryBuildPropsBasePath)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), '$(_DirectoryBuildPropsFile)'))</_DirectoryBuildPropsBasePath>
    <DirectoryBuildPropsPath Condition="'$(_DirectoryBuildPropsBasePath)' != '' and '$(_DirectoryBuildPropsFile)' != ''">$([System.IO.Path]::Combine('$(_DirectoryBuildPropsBasePath)', '$(_DirectoryBuildPropsFile)'))</DirectoryBuildPropsPath>
  <Import Project="$(DirectoryBuildPropsPath)" Condition="'$(ImportDirectoryBuildProps)' == 'true' and exists('$(DirectoryBuildPropsPath)')">


C:\Program Files\Microsoft Visual Studio\2022\Preview\MSBuild\Current\Microsoft.Common.props
      Prepare to import project extensions which usually come from packages.  Package management systems will create a file at:

      Each package management system should use a unique moniker to avoid collisions.  It is a wild-card import so the package
      management system can write out multiple files but the order of the import is alphabetic because MSBuild sorts the list.
        The declaration of $(BaseIntermediateOutputPath) had to be moved up from Microsoft.Common.CurrentVersion.targets
        in order for the $(MSBuildProjectExtensionsPath) to use it as a default.
    <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">obj\</BaseIntermediateOutputPath>
    <BaseIntermediateOutputPath Condition="!HasTrailingSlash('$(BaseIntermediateOutputPath)')">$(BaseIntermediateOutputPath)\</BaseIntermediateOutputPath>
    <MSBuildProjectExtensionsPath Condition="'$(MSBuildProjectExtensionsPath)' == '' ">$(BaseIntermediateOutputPath)</MSBuildProjectExtensionsPath>
        Import paths that are relative default to be relative to the importing file.  However, since MSBuildExtensionsPath
        defaults to BaseIntermediateOutputPath we expect it to be relative to the project directory.  So if the path is relative
        it needs to be made absolute based on the project directory.
    <MSBuildProjectExtensionsPath Condition="'$([System.IO.Path]::IsPathRooted($(MSBuildProjectExtensionsPath)))' == 'false'">$([System.IO.Path]::Combine('$(MSBuildProjectDirectory)', '$(MSBuildProjectExtensionsPath)'))</MSBuildProjectExtensionsPath>
    <MSBuildProjectExtensionsPath Condition="!HasTrailingSlash('$(MSBuildProjectExtensionsPath)')">$(MSBuildProjectExtensionsPath)\</MSBuildProjectExtensionsPath>
    <ImportProjectExtensionProps Condition="'$(ImportProjectExtensionProps)' == ''">true</ImportProjectExtensionProps>
    <_InitialMSBuildProjectExtensionsPath Condition=" '$(ImportProjectExtensionProps)' == 'true' ">$(MSBuildProjectExtensionsPath)</_InitialMSBuildProjectExtensionsPath>

See also