diff --git a/src/Tasks.UnitTests/OutputPathTests.cs b/src/Tasks.UnitTests/OutputPathTests.cs index 6b4da05750a..f2bc410bbbd 100644 --- a/src/Tasks.UnitTests/OutputPathTests.cs +++ b/src/Tasks.UnitTests/OutputPathTests.cs @@ -61,7 +61,7 @@ public void BothBaseOutputPathAndOutputPathWereNotSpecified() project.Build(new MockLogger(_output)).ShouldBeFalse(); // Assert - project.GetPropertyValue("BaseOutputPath").ShouldBe(baseOutputPath + '\\'); + project.GetPropertyValue("BaseOutputPath").ShouldBe(baseOutputPath.WithTrailingSlash()); project.GetPropertyValue("BaseOutputPathWasSpecified").ShouldBe(string.Empty); project.GetPropertyValue("_OutputPathWasMissing").ShouldBe("true"); } diff --git a/src/Tasks.UnitTests/ProjectExtensionsImportTestBase.cs b/src/Tasks.UnitTests/ProjectExtensionsImportTestBase.cs index e72c228d93d..b2aa3e9fdeb 100644 --- a/src/Tasks.UnitTests/ProjectExtensionsImportTestBase.cs +++ b/src/Tasks.UnitTests/ProjectExtensionsImportTestBase.cs @@ -171,25 +171,25 @@ public void ErrorIfChangedInBodyOfProject() MockLogger logger = new MockLogger(); - project.Build("_CheckForInvalidConfigurationAndPlatform", new[] {logger}).ShouldBeFalse(); + project.Build("_CheckForInvalidOutputPaths", new[] {logger}).ShouldBeFalse(); logger.Errors.Select(i => i.Code).FirstOrDefault().ShouldBe("MSB3540"); } /// - /// Ensures that an error is logged if BaseIntermediateOutputPath is modified after it was set by Microsoft.Common.props and - /// EnableBaseIntermediateOutputPathMismatchWarning is 'true'. + /// Ensures that an error is logged if BuildDir is modified after it was set by Microsoft.Common.props and + /// EnableBuildDirMismatchWarning is 'true'. /// [Fact] - public void WarningIfBaseIntermediateOutputPathIsChangedInBodyOfProject() + public void WarningIfBuildDirIsChangedInBodyOfProject() { Project project = ObjectModelHelpers.LoadProjectFileInTempProjectDirectory(ObjectModelHelpers.CreateFileInTempProjectDirectory(_projectRelativePath, @" - true - foo + true + foo @@ -198,7 +198,7 @@ public void WarningIfBaseIntermediateOutputPathIsChangedInBodyOfProject() MockLogger logger = new MockLogger(); - project.Build("_CheckForInvalidConfigurationAndPlatform", new[] { logger }).ShouldBeTrue(); + project.Build("_CheckForInvalidOutputPaths", new[] { logger }).ShouldBeTrue(); logger.Warnings.Select(i => i.Code).FirstOrDefault().ShouldBe("MSB3539"); } diff --git a/src/Tasks.UnitTests/ProjectExtensionsPropsImportTest.cs b/src/Tasks.UnitTests/ProjectExtensionsPropsImportTest.cs index df22c20882a..679e082c13c 100644 --- a/src/Tasks.UnitTests/ProjectExtensionsPropsImportTest.cs +++ b/src/Tasks.UnitTests/ProjectExtensionsPropsImportTest.cs @@ -9,12 +9,12 @@ namespace Microsoft.Build.UnitTests /// sealed public class ProjectExtensionsPropsImportTest : ProjectExtensionsImportTestBase { - protected override string CustomImportProjectPath => Path.Combine(ObjectModelHelpers.TempProjectDir, "obj", $"{Path.GetFileName(_projectRelativePath)}.custom.props"); + protected override string CustomImportProjectPath => Path.Combine(ObjectModelHelpers.TempProjectDir, "ext", $"{Path.GetFileName(_projectRelativePath)}.custom.props"); - protected override string ImportProjectPath => Path.Combine(Path.GetDirectoryName(_projectRelativePath), "obj", $"{Path.GetFileName(_projectRelativePath)}.custom.props"); + protected override string ImportProjectPath => Path.Combine(Path.GetDirectoryName(_projectRelativePath), "ext", $"{Path.GetFileName(_projectRelativePath)}.custom.props"); protected override string PropertyNameToEnableImport => "ImportProjectExtensionProps"; protected override string PropertyNameToSignalImportSucceeded => "WasProjectExtensionPropsImported"; } -} \ No newline at end of file +} diff --git a/src/Tasks.UnitTests/ProjectExtensionsTargetsImportTest.cs b/src/Tasks.UnitTests/ProjectExtensionsTargetsImportTest.cs index 0bd9c8774a0..53c1681a80b 100644 --- a/src/Tasks.UnitTests/ProjectExtensionsTargetsImportTest.cs +++ b/src/Tasks.UnitTests/ProjectExtensionsTargetsImportTest.cs @@ -9,12 +9,12 @@ namespace Microsoft.Build.UnitTests /// sealed public class ProjectExtensionsTargetsImportTest : ProjectExtensionsImportTestBase { - protected override string CustomImportProjectPath => Path.Combine(ObjectModelHelpers.TempProjectDir, "obj", $"{Path.GetFileName(_projectRelativePath)}.custom.targets"); + protected override string CustomImportProjectPath => Path.Combine(ObjectModelHelpers.TempProjectDir, "ext", $"{Path.GetFileName(_projectRelativePath)}.custom.targets"); - protected override string ImportProjectPath => Path.Combine(Path.GetDirectoryName(_projectRelativePath), "obj", $"{Path.GetFileName(_projectRelativePath)}.custom.targets"); + protected override string ImportProjectPath => Path.Combine(Path.GetDirectoryName(_projectRelativePath), "ext", $"{Path.GetFileName(_projectRelativePath)}.custom.targets"); protected override string PropertyNameToEnableImport => "ImportProjectExtensionTargets"; protected override string PropertyNameToSignalImportSucceeded => "WasProjectExtensionTargetsImported"; } -} \ No newline at end of file +} diff --git a/src/Tasks/Microsoft.Common.CrossTargeting.targets b/src/Tasks/Microsoft.Common.CrossTargeting.targets index c7d553aecd3..d485b0e5718 100644 --- a/src/Tasks/Microsoft.Common.CrossTargeting.targets +++ b/src/Tasks/Microsoft.Common.CrossTargeting.targets @@ -215,7 +215,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. <_DirectoryBuildTargetsFile Condition="'$(_DirectoryBuildTargetsFile)' == ''">Directory.Build.targets <_DirectoryBuildTargetsBasePath Condition="'$(_DirectoryBuildTargetsBasePath)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), '$(_DirectoryBuildTargetsFile)')) - $([System.IO.Path]::Combine('$(_DirectoryBuildTargetsBasePath)', '$(_DirectoryBuildTargetsFile)')) + $([MSBuild]::NormalizePath('$(_DirectoryBuildTargetsBasePath)', '$(_DirectoryBuildTargetsFile)')) diff --git a/src/Tasks/Microsoft.Common.CurrentVersion.targets b/src/Tasks/Microsoft.Common.CurrentVersion.targets index 2847dcd30d7..7115948dff8 100644 --- a/src/Tasks/Microsoft.Common.CurrentVersion.targets +++ b/src/Tasks/Microsoft.Common.CurrentVersion.targets @@ -105,6 +105,10 @@ Copyright (C) Microsoft Corporation. All rights reserved. Several properties must be set in the main project file, before using this .TARGETS file. However, if the properties are not set, we pick some defaults. + BuildDir: + Indicates the final output location for the project or solution. + All the *OutpuPath properties should derive from this. + OutDir: Indicates the final output location for the project or solution. When building a solution, OutDir can be used to gather multiple project outputs in one location. In addition, @@ -130,8 +134,8 @@ Copyright (C) Microsoft Corporation. All rights reserved. This is the full intermediate Output Path, and is derived from BaseIntermediateOutputPath, if none specified (eg. obj\Debug). If this property is overridden, then setting BaseIntermediateOutputPath has no effect. - Ensure any and all path property has a trailing slash, so it can be concatenated. - --> + Ensure any and all path properties have a trailing slash which enables concatenation without specifying path separator. + --> @@ -142,6 +146,8 @@ Copyright (C) Microsoft Corporation. All rights reserved. <_OutputPathWasMissing Condition="'$(_OriginalPlatform)' != '' and '$(_OriginalConfiguration)' != '' and '$(OutputPath)' == ''">true true + + $([MSBuild]::EnsureTrailingSlash($([MSBuild]::ValueOrDefault('$(BuildDir)', 'build')))) @@ -150,17 +156,15 @@ Copyright (C) Microsoft Corporation. All rights reserved. Debug $(Configuration) - bin\ - $(BaseOutputPath)\ - $(BaseOutputPath)$(Configuration)\ - $(BaseOutputPath)$(PlatformName)\$(Configuration)\ - $(OutputPath)\ - - obj\ - $(BaseIntermediateOutputPath)\ - $(BaseIntermediateOutputPath)$(Configuration)\ - $(BaseIntermediateOutputPath)$(PlatformName)\$(Configuration)\ - $(IntermediateOutputPath)\ + $([MSBuild]::EnsureTrailingSlash($([MSBuild]::ValueOrDefault('$(BaseOutputPath)', '$(BuildDir)bin')))) + $([System.IO.Path]::Combine('$(BaseOutputPath)', '$(Configuration)')) + $([System.IO.Path]::Combine('$(BaseOutputPath)', '$(PlatformName)', '$(Configuration)')) + $([MSBuild]::EnsureTrailingSlash('$(OutputPath)')) + + $([MSBuild]::EnsureTrailingSlash($([MSBuild]::ValueOrDefault('$(BaseIntermediateOutputPath)', '$(BuildDir)obj')))) + $([System.IO.Path]::Combine('$(BaseIntermediateOutputPath)', '$(Configuration)')) + $([System.IO.Path]::Combine('$(BaseIntermediateOutputPath)', '$(PlatformName)', '$(Configuration)')) + $([MSBuild]::EnsureTrailingSlash('$(IntermediateOutputPath)')) @@ -225,15 +229,14 @@ Copyright (C) Microsoft Corporation. All rights reserved. true - $(OutputPath) - $(OutDir)\ + $([MSBuild]::EnsureTrailingSlash($([MSBuild]::ValueOrDefault('$(OutDir)', '$(OutputPath)')))) $(MSBuildProjectName) - $(OutDir)$(ProjectName)\ + $([MSBuild]::EnsureTrailingSlash('$(OutDir)$(ProjectName)')) $(MSBuildProjectName) $(RootNamespace) @@ -263,11 +266,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. <_DeploymentTargetApplicationManifestFileName Condition="'$(OutputType)'=='library'">Native.$(AssemblyName).manifest - <_DeploymentTargetApplicationManifestFileName Condition="'$(OutputType)'=='winexe'">$(TargetFileName).manifest - - <_DeploymentTargetApplicationManifestFileName Condition="'$(OutputType)'=='exe'">$(TargetFileName).manifest - - <_DeploymentTargetApplicationManifestFileName Condition="'$(OutputType)'=='appcontainerexe'">$(TargetFileName).manifest + <_DeploymentTargetApplicationManifestFileName Condition="'$(OutputType)'=='winexe' or '$(OutputType)'=='exe' or '$(OutputType)'=='appcontainerexe'">$(TargetFileName).manifest $(AssemblyName).application @@ -276,9 +275,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. $(GenerateManifests) <_DeploymentApplicationManifestIdentity Condition="'$(OutputType)'=='library'">Native.$(AssemblyName) - <_DeploymentApplicationManifestIdentity Condition="'$(OutputType)'=='winexe'">$(AssemblyName).exe - <_DeploymentApplicationManifestIdentity Condition="'$(OutputType)'=='exe'">$(AssemblyName).exe - <_DeploymentApplicationManifestIdentity Condition="'$(OutputType)'=='appcontainerexe'">$(AssemblyName).exe + <_DeploymentApplicationManifestIdentity Condition="'$(OutputType)'=='winexe' or '$(OutputType)'=='exe' or '$(OutputType)'=='appcontainerexe'">$(AssemblyName).exe <_DeploymentDeployManifestIdentity Condition="'$(HostInBrowser)' != 'true'">$(AssemblyName).application <_DeploymentDeployManifestIdentity Condition="'$(HostInBrowser)' == 'true'">$(AssemblyName).xbap @@ -319,7 +316,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. Condition intentionally omitted on this one, because it causes problems when we pick up the value of an environment variable named TargetDir --> - $([MSBuild]::Escape($([System.IO.Path]::GetFullPath(`$([System.IO.Path]::Combine(`$(MSBuildProjectDirectory)`, `$(OutDir)`))`)))) + $([MSBuild]::NormalizeDirectory('$(MSBuildProjectDirectory)', '$(OutDir)')) $(TargetDir)$(TargetFileName) @@ -330,6 +327,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. $([MSBuild]::EnsureTrailingSlash($(MSBuildProjectDirectory))) + $(MSBuildProjectFullPath) $(ProjectDir)$(ProjectFileName) @@ -406,12 +404,12 @@ Copyright (C) Microsoft Corporation. All rights reserved. $(IntermediateOutputPath)$(TargetName).pdb - <_WinMDDebugSymbolsOutputPath>$([System.IO.Path]::Combine('$(OutDir)', $([System.IO.Path]::GetFileName('$(WinMDExpOutputPdb)')))) + <_WinMDDebugSymbolsOutputPath>$(OutDir)$([System.IO.Path]::GetFileName('$(WinMDExpOutputPdb)')) $(IntermediateOutputPath)$(TargetName).xml - <_WinMDDocFileOutputPath>$([System.IO.Path]::Combine('$(OutDir)', $([System.IO.Path]::GetFileName('$(WinMDOutputDocumentationFile)')))) + <_WinMDDocFileOutputPath>$(OutDir)$([System.IO.Path]::GetFileName('$(WinMDOutputDocumentationFile)')) @@ -483,8 +481,9 @@ Copyright (C) Microsoft Corporation. All rights reserved. - $(PublishDir)\ - $(OutputPath)app.publish\ + publish + $([MSBuild]::EnsureTrailingSlash($([MSBuild]::ValueOrDefault('$(PublishDir)', '$(PublishDirName)')))) + $([MSBuild]::NormalizeDirectory('$(PublishDir)', '$(MSBuildProjectName)')) + --> @@ -798,67 +797,90 @@ Copyright (C) Microsoft Corporation. All rights reserved. + --> - <_InvalidConfigurationMessageText>The BaseOutputPath/OutputPath property is not set for project '$(MSBuildProjectFile)'. Please check to make sure that you have specified a valid combination of Configuration and Platform for this project. Configuration='$(_OriginalConfiguration)' Platform='$(_OriginalPlatform)'. + <_InvalidConfigurationMessageText>The 'BaseOutputPath'/'OutputPath' property is not set for project '$(MSBuildProjectFile)'. Please check to make sure that you have specified a valid combination of Configuration and Platform for this project. Configuration='$(_OriginalConfiguration)' Platform='$(_OriginalPlatform)'. <_InvalidConfigurationMessageText Condition="'$(BuildingInsideVisualStudio)' == 'true'">$(_InvalidConfigurationMessageText) This error may also appear if some other project is trying to follow a project-to-project reference to this project, this project has been unloaded or is not included in the solution, and the referencing project does not build using the same or an equivalent Configuration or Platform. <_InvalidConfigurationMessageText Condition="'$(BuildingInsideVisualStudio)' != 'true'">$(_InvalidConfigurationMessageText) You may be seeing this message because you are trying to build a project without a solution file, and have specified a non-default Configuration or Platform that doesn't exist for this project. - - + + - - - - - - + x86 + + + + + + + + + + + + <_SetBuildDirsCommonText>To set this property, you must do so before 'Microsoft.Common.props' is imported, for example by using 'Directory.Build.props'. For more information, please visit https://go.microsoft.com/fwlink/?linkid=869650 + <_ProjectExtensionsPathMismatchErrorText>The value of the property 'MSBuildProjectExtensionsPath' was modified after it was used by MSBuild which can lead to unexpected build results. + <_BuildDirMismatchWarningText>The value of the property 'BuildDir' was modified after it was used by MSBuild which can lead to unexpected build results. Tools such as NuGet will write outputs to the path specified by the 'MSBuildProjectExtensionsPath' instead. + + - + - + 1. $(EnableBuildDirMismatchWarning) is 'true' + 2. $(BuildDir) was set in the body of a project after its default value was set in 'Microsoft.Common.props' + 3. $(BuildDir) is not the same as $(MSBuildProjectExtensionsPath) + + Similar to the error above, there are cases when users set $(BuildDir) in the body of their project and things build but only by coincidence. + MSBuild does not know if $(BuildDir) changing would cause problems so tools like NuGet must set $(EnableBuildDirMismatchWarning) to 'true'. + --> + + true - $([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)')) + $(IntermediateOutputPath)$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension) @@ -4781,7 +4803,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. - + @@ -5653,15 +5675,19 @@ Copyright (C) Microsoft Corporation. All rights reserved. + --> + + <_DefaultPublishDir>$([MSBuild]::EnsureTrailingSlash('publish')) + + + Condition="'$(PublishDir)' == '$(_DefaultPublishDir)' and Exists('$(PublishDir)')"/> diff --git a/src/Tasks/Microsoft.Common.props b/src/Tasks/Microsoft.Common.props index b08b6558352..d22ca369760 100644 --- a/src/Tasks/Microsoft.Common.props +++ b/src/Tasks/Microsoft.Common.props @@ -21,100 +21,106 @@ Copyright (C) Microsoft Corporation. All rights reserved. + 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) + --> <_DirectoryBuildPropsFile Condition="'$(_DirectoryBuildPropsFile)' == ''">Directory.Build.props <_DirectoryBuildPropsBasePath Condition="'$(_DirectoryBuildPropsBasePath)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), '$(_DirectoryBuildPropsFile)')) - $([System.IO.Path]::Combine('$(_DirectoryBuildPropsBasePath)', '$(_DirectoryBuildPropsFile)')) + $([MSBuild]::NormalizePath('$(_DirectoryBuildPropsBasePath)', '$(_DirectoryBuildPropsFile)')) + + build + $([MSBuild]::EnsureTrailingSlash($([MSBuild]::ValueOrDefault('$(BuildDir)', '$(BuildDirName)')))) + $([MSBuild]::NormalizeDirectory('$(BuildDir)', '$(MSBuildProjectName)')) + <_InitialBuildDir>$(BuildDir) + - 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. - --> + - - obj\ - $(BaseIntermediateOutputPath)\ - <_InitialBaseIntermediateOutputPath>$(BaseIntermediateOutputPath) - - $(BaseIntermediateOutputPath) + $([MSBuild]::EnsureTrailingSlash($([MSBuild]::ValueOrDefault('$(MSBuildProjectExtensionsPath)', '$(BuildDir)ext')))) - $([System.IO.Path]::Combine('$(MSBuildProjectDirectory)', '$(MSBuildProjectExtensionsPath)')) - $(MSBuildProjectExtensionsPath)\ + --> + $([MSBuild]::NormalizeDirectory('$(MSBuildProjectDirectory)', '$(MSBuildProjectExtensionsPath)')) true - <_InitialMSBuildProjectExtensionsPath Condition=" '$(ImportProjectExtensionProps)' == 'true' ">$(MSBuildProjectExtensionsPath) + <_InitialMSBuildProjectExtensionsPath Condition="'$(ImportProjectExtensionProps)' == 'true'">$(MSBuildProjectExtensionsPath) + Import wildcard "ImportBefore" props files if we're actually in a 12.0+ project (rather than a project being + treated as 4.0) + --> + Wildcard imports come from $(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props.d folder. + This is very similar to the same extension point used in Microsoft.Common.targets, which is located in + the $(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.targets\ directory. Unfortunately, there + is already a file named "Microsoft.Common.props" in this directory so we have to have a slightly different + directory name to hold extensions. + --> + In VS 2010 SP1 and VS 2012, both supported for asset compatibility, the MSBuild installed + as part of them did not enforce using the local ToolsVersion (4.0) in all cases, but instead + just used whatever ToolsVersion was in the project file if it existed on the machine, and + only forced 4.0 if that ToolsVersion did not exist. + + Moving forward, we do want to enforce a single acting ToolsVersion per version of Visual Studio, + but in order to approximate this behavior on VS 2010 SP1 and VS 2012 as well, we've redirected + the targets: If we're building using 4.X MSBuild (which doesn't define the new reserved + property, MSBuildAssemblyVersion), we'll point right back at the 4.0 targets, which still exist + as part of the .NET Framework. Only if we're using the new MSBuild will we point to the current + targets. + --> + Reset VisualStudioVersion if it's 12.0+: Should be 10.0 if VS 2010 is installed or 11.0 otherwise, + but since we don't have a good way of telling whether VS 2010 is installed, make it 11.0 if + VS 2012 is installed or 10.0 otherwise. The reset should be safe because if it was already + set to something (e.g. 11.0 in a VS 2012 command prompt) then MSBuild's internal + VisualStudioVersion-defaulting code should never come into the picture, so the only way it could + be 12.0+ when building a TV 12.0 project (because we're in this file) using MSBuild 4.5 (because + MSBuildAssemblyVersion hasn't been set) is if it's a TV 12.0 project on an empty command prompt. + --> 11.0 10.0 - + $(MSBuildExtensionsPath)\v4.0\Custom.Before.$(MSBuildThisFile) $(MSBuildExtensionsPath)\v4.0\Custom.After.$(MSBuildThisFile) - + @@ -123,16 +129,18 @@ Copyright (C) Microsoft Corporation. All rights reserved. + Only import the extension targets if we're actually in a 12.0 project here (rather than one we're attempting + to treat as 4.0) OR if the Dev11 Microsoft.Common.props don't exist. If it's a 12.0 project we're redirecting + to 4.0 and the Dev11 Microsoft.Common.props do exist, the extension targets will have been imported already + so there's no need to import them twice. + --> - + true @@ -150,25 +158,25 @@ Copyright (C) Microsoft Corporation. All rights reserved. + Only import the extension targets if we're actually in a 12.0 project here (rather than one we're attempting + to treat as 4.0) OR if the Dev11 Microsoft.Common.props don't exist. If it's a 12.0 project we're redirecting + to 4.0 and the Dev11 Microsoft.Common.props do exist, the extension targets will have been imported already + so there's no need to import them twice. + --> + Import wildcard "ImportAfter" props files if we're actually in a 12.0+ project (rather than a project being + treated as 4.0) + --> + Import NuGet.props file. + --> $([MSBuild]::IsRunningFromVisualStudio()) $([MSBuild]::GetToolsDirectory32())\..\..\..\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.props diff --git a/src/Tasks/Microsoft.Common.targets b/src/Tasks/Microsoft.Common.targets index 753dad7cfaf..f14a2810499 100644 --- a/src/Tasks/Microsoft.Common.targets +++ b/src/Tasks/Microsoft.Common.targets @@ -138,7 +138,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. <_DirectoryBuildTargetsFile Condition="'$(_DirectoryBuildTargetsFile)' == ''">Directory.Build.targets <_DirectoryBuildTargetsBasePath Condition="'$(_DirectoryBuildTargetsBasePath)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), '$(_DirectoryBuildTargetsFile)')) - $([System.IO.Path]::Combine('$(_DirectoryBuildTargetsBasePath)', '$(_DirectoryBuildTargetsFile)')) + $([MSBuild]::NormalizePath('$(_DirectoryBuildTargetsBasePath)', '$(_DirectoryBuildTargetsFile)'))