diff --git a/documentation/wiki/ChangeWaves.md b/documentation/wiki/ChangeWaves.md
index 1e25829f60b..b5ec4e3736b 100644
--- a/documentation/wiki/ChangeWaves.md
+++ b/documentation/wiki/ChangeWaves.md
@@ -27,6 +27,7 @@ The opt-out comes in the form of setting the environment variable `MSBuildDisabl
- [Error when a property expansion in a condition has whitespace](https://github.com/dotnet/msbuild/pull/5672)
- [Allow Custom CopyToOutputDirectory Location With TargetPath](https://github.com/dotnet/msbuild/pull/6237)
- [Allow users that have certain special characters in their username to build successfully when using exec](https://github.com/dotnet/msbuild/pull/6223)
+- [Fail restore operations when there is no `Restore` target or an SDK is unresolveable](https://github.com/dotnet/msbuild/pull/6312)
### 17.0
## Change Waves No Longer In Rotation
diff --git a/src/Build/BackEnd/Shared/BuildRequestConfiguration.cs b/src/Build/BackEnd/Shared/BuildRequestConfiguration.cs
index 8398cb68479..659066306cc 100644
--- a/src/Build/BackEnd/Shared/BuildRequestConfiguration.cs
+++ b/src/Build/BackEnd/Shared/BuildRequestConfiguration.cs
@@ -466,7 +466,7 @@ internal void LoadProjectIntoConfiguration(
projectLoadSettings |= ProjectLoadSettings.IgnoreMissingImports | ProjectLoadSettings.IgnoreInvalidImports | ProjectLoadSettings.IgnoreEmptyImports;
}
- if (buildRequestDataFlags.HasFlag(buildRequestDataFlags & BuildRequestDataFlags.FailOnUnresolvedSdk))
+ if (buildRequestDataFlags.HasFlag(BuildRequestDataFlags.FailOnUnresolvedSdk))
{
projectLoadSettings |= ProjectLoadSettings.FailOnUnresolvedSdk;
}
diff --git a/src/MSBuild.UnitTests/XMake_Tests.cs b/src/MSBuild.UnitTests/XMake_Tests.cs
index e014f5652e3..1513d20e3f9 100644
--- a/src/MSBuild.UnitTests/XMake_Tests.cs
+++ b/src/MSBuild.UnitTests/XMake_Tests.cs
@@ -19,6 +19,7 @@
using Shouldly;
using System.IO.Compression;
using System.Reflection;
+using Microsoft.Build.Utilities;
namespace Microsoft.Build.UnitTests
{
@@ -2127,6 +2128,31 @@ public void RestoreFailsOnUnresolvedSdk()
logContents.ShouldContain("error MSB4236: The SDK 'UnresolvedSdk' specified could not be found.");
}
+ ///
+ /// When specifying /t:restore under an old changewave, do not fail when an SDK can't be resolved.
+ /// Previous behavior was to try and continue anyway but then "restore" would succeed and build workflows continue on.
+ ///
+ [Fact]
+ public void RestorePassesOnUnresolvedSdkUnderChangewave()
+ {
+ string projectContents = ObjectModelHelpers.CleanupFileContents(
+$@"
+
+
+
+
+");
+
+ using TestEnvironment env = Microsoft.Build.UnitTests.TestEnvironment.Create();
+
+ string logContents = ExecuteMSBuildExeExpectSuccess(projectContents,
+ envsToCreate: new Dictionary() { ["MSBUILDDISABLEFEATURESFROMVERSION"]=ChangeWaves.Wave16_10.ToString() },
+ arguments: " /t:restore");
+
+ logContents.ShouldNotContain("MSB4236");
+ }
+
+
///
/// Verifies a non-existent target doesn't fail restore as long as its not considered an entry target, in this case Restore.
///
@@ -2181,6 +2207,26 @@ public void RestoreFailsWhenEntryTargetIsNonExistent()
logContents.ShouldContain("error MSB4057: The target \"Restore\" does not exist in the project.");
}
+ ///
+ /// Verifies restore will not fail if the entry target doesn't exist, when changewave applied.
+ ///
+ [Fact]
+ public void RestorePassesWhenEntryTargetIsNonExistentUnderChangewave()
+ {
+ string projectContents = ObjectModelHelpers.CleanupFileContents(
+@"
+
+
+
+");
+
+ string logContents = ExecuteMSBuildExeExpectSuccess(projectContents,
+ envsToCreate: new Dictionary() { ["MSBUILDDISABLEFEATURESFROMVERSION"] = ChangeWaves.Wave16_10.ToString() },
+ arguments: "/t:restore");
+
+ logContents.ShouldNotContain("MSB4057");
+ }
+
///
/// Verifies restore will run InitialTargets.
///
diff --git a/src/MSBuild/XMake.cs b/src/MSBuild/XMake.cs
index 73d7117558a..b86c58acce1 100644
--- a/src/MSBuild/XMake.cs
+++ b/src/MSBuild/XMake.cs
@@ -1440,20 +1440,31 @@ private static (BuildResultCode result, Exception exception) ExecuteRestore(stri
restoreGlobalProperties["MSBuildRestoreSessionId"] = Guid.NewGuid().ToString("D");
// Create a new request with a Restore target only and specify:
- // - BuildRequestDataFlags.ClearCachesAfterBuild to ensure the projects will be reloaded from disk for subsequent builds
- // - BuildRequestDataFlags.SkipNonexistentNonEntryTargets to ignore missing non-entry targets since Restore does not require that all targets
- // exist, only top-level ones like Restore itself
- // - BuildRequestDataFlags.IgnoreMissingEmptyAndInvalidImports to ignore imports that don't exist, are empty, or are invalid because restore might
- // make available an import that doesn't exist yet and the might be missing a condition.
- // - BuildRequestDataFlags.FailOnUnresolvedSdk to still fail in the case when an MSBuild project SDK can't be resolved since this is fatal and should
- // fail the build.
+ BuildRequestDataFlags flags;
+
+ if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave16_10))
+ {
+ flags = BuildRequestDataFlags.ClearCachesAfterBuild // ensure the projects will be reloaded from disk for subsequent builds
+ | BuildRequestDataFlags.SkipNonexistentNonEntryTargets // ignore missing non-entry targets since Restore does not require that all targets
+ // exist, only top-level ones like Restore itself
+ | BuildRequestDataFlags.IgnoreMissingEmptyAndInvalidImports // ignore imports that don't exist, are empty, or are invalid because restore might
+ // make available an import that doesn't exist yet and the might be missing a condition.
+ | BuildRequestDataFlags.FailOnUnresolvedSdk; // still fail in the case when an MSBuild project SDK can't be resolved since this is fatal and should
+ // fail the build.
+ }
+ else
+ {
+ // pre-16.10 flags allowed `-restore` to pass when there was no `Restore` target
+ flags = BuildRequestDataFlags.ClearCachesAfterBuild | BuildRequestDataFlags.SkipNonexistentTargets | BuildRequestDataFlags.IgnoreMissingEmptyAndInvalidImports;
+ }
+
BuildRequestData restoreRequest = new BuildRequestData(
projectFile,
restoreGlobalProperties,
toolsVersion,
targetsToBuild: new[] { MSBuildConstants.RestoreTargetName },
hostServices: null,
- flags: BuildRequestDataFlags.ClearCachesAfterBuild | BuildRequestDataFlags.SkipNonexistentNonEntryTargets | BuildRequestDataFlags.IgnoreMissingEmptyAndInvalidImports | BuildRequestDataFlags.FailOnUnresolvedSdk);
+ flags);
return ExecuteBuild(buildManager, restoreRequest);
}