diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/CheckForTargetInAssetsFile.cs b/src/Tasks/Microsoft.NET.Build.Tasks/CheckForTargetInAssetsFile.cs new file mode 100644 index 000000000000..85d16c366b56 --- /dev/null +++ b/src/Tasks/Microsoft.NET.Build.Tasks/CheckForTargetInAssetsFile.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Build.Framework; +using NuGet.ProjectModel; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.NET.Build.Tasks +{ + public class CheckForTargetInAssetsFile : TaskBase + { + [Required] + public string AssetsFilePath { get; set; } + + [Required] + public string TargetFrameworkMoniker { get; set; } + + public string RuntimeIdentifier { get; set; } + + + protected override void ExecuteCore() + { + LockFile lockFile = new LockFileCache(BuildEngine4).GetLockFile(AssetsFilePath); + + var nugetFramework = NuGetUtils.ParseFrameworkName(TargetFrameworkMoniker); + + lockFile.GetTargetAndThrowIfNotFound(nugetFramework, RuntimeIdentifier); + } + } +} diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/LockFileExtensions.cs b/src/Tasks/Microsoft.NET.Build.Tasks/LockFileExtensions.cs index 6e0fe73d4629..0440b32a621b 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/LockFileExtensions.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/LockFileExtensions.cs @@ -12,6 +12,33 @@ namespace Microsoft.NET.Build.Tasks { internal static class LockFileExtensions { + public static LockFileTarget GetTargetAndThrowIfNotFound(this LockFile lockFile, NuGetFramework framework, string runtime) + { + LockFileTarget lockFileTarget = lockFile.GetTarget(framework, runtime); + + if (lockFileTarget == null) + { + string frameworkString = framework.DotNetFrameworkName; + string targetMoniker = string.IsNullOrEmpty(runtime) ? + frameworkString : + $"{frameworkString}/{runtime}"; + + string message; + if (string.IsNullOrEmpty(runtime)) + { + message = string.Format(Strings.AssetsFileMissingTarget, lockFile.Path, targetMoniker, framework.GetShortFolderName()); + } + else + { + message = string.Format(Strings.AssetsFileMissingRuntimeIdentifier, lockFile.Path, targetMoniker, framework.GetShortFolderName(), runtime); + } + + throw new BuildErrorException(message); + } + + return lockFileTarget; + } + public static ProjectContext CreateProjectContext( this LockFile lockFile, NuGetFramework framework, @@ -28,17 +55,7 @@ public static ProjectContext CreateProjectContext( throw new ArgumentNullException(nameof(framework)); } - LockFileTarget lockFileTarget = lockFile.GetTarget(framework, runtime); - - if (lockFileTarget == null) - { - string frameworkString = framework.DotNetFrameworkName; - string targetMoniker = string.IsNullOrEmpty(runtime) ? - frameworkString : - $"{frameworkString}/{runtime}"; - - throw new BuildErrorException(Strings.AssetsFileMissingTarget, lockFile.Path, targetMoniker, framework.GetShortFolderName(), runtime); - } + var lockFileTarget = lockFile.GetTargetAndThrowIfNotFound(framework, runtime); LockFileTargetLibrary platformLibrary = lockFileTarget.GetLibrary(platformLibraryName); bool isFrameworkDependent = platformLibrary != null && (!isSelfContained || string.IsNullOrEmpty(lockFileTarget.RuntimeIdentifier)); diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj index d98ebd7e7196..186589a72497 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj +++ b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj @@ -47,8 +47,8 @@ Strings.resx - true - true + True + True Strings.resx diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/Resources/Strings.Designer.cs b/src/Tasks/Microsoft.NET.Build.Tasks/Resources/Strings.Designer.cs index 4505fe20ea87..0d6eff8eeb27 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/Resources/Strings.Designer.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/Resources/Strings.Designer.cs @@ -80,7 +80,16 @@ internal static string AssetPreprocessorMustBeConfigured { } /// - /// Looks up a localized string similar to Assets file '{0}' doesn't have a target for '{1}'. Ensure you have restored this project for TargetFramework='{2}' and RuntimeIdentifier='{3}'.. + /// Looks up a localized string similar to Assets file '{0}' doesn't have a target for '{1}'. Ensure you have included '{2}' in the TargetFrameworks for your project. You may also need to include '{3}' in your project's RuntimeIdentifiers.. + /// + internal static string AssetsFileMissingRuntimeIdentifier { + get { + return ResourceManager.GetString("AssetsFileMissingRuntimeIdentifier", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Assets file '{0}' doesn't have a target for '{1}'. Ensure you have included '{2}' in the TargetFrameworks for your project.. /// internal static string AssetsFileMissingTarget { get { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/Resources/Strings.resx b/src/Tasks/Microsoft.NET.Build.Tasks/Resources/Strings.resx index b25f732d35c5..d411f1fc2400 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/Resources/Strings.resx +++ b/src/Tasks/Microsoft.NET.Build.Tasks/Resources/Strings.resx @@ -130,7 +130,7 @@ Assets file '{0}' not found. Run a NuGet package restore to generate this file. - Assets file '{0}' doesn't have a target for '{1}'. Ensure you have restored this project for TargetFramework='{2}' and RuntimeIdentifier='{3}'. + Assets file '{0}' doesn't have a target for '{1}'. Ensure you have included '{2}' in the TargetFrameworks for your project. Assets file path '{0}' is not rooted. Only full paths are supported. @@ -297,4 +297,7 @@ The current .NET SDK does not support targeting {0} {1}. Either target {0} {2} or lower, or use a version of the .NET SDK that supports {0} {1}. + + Assets file '{0}' doesn't have a target for '{1}'. Ensure you have included '{2}' in the TargetFrameworks for your project. You may also need to include '{3}' in your project's RuntimeIdentifiers. + \ No newline at end of file diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.PackageDependencyResolution.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.PackageDependencyResolution.targets index 8ac63aaad161..284f9daff61b 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.PackageDependencyResolution.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.PackageDependencyResolution.targets @@ -145,6 +145,8 @@ Copyright (c) .NET Foundation. All rights reserved. + + + + + + { + // Set property to disable logic in Microsoft.NETCore.App package that will otherwise cause a failure + // when we remove everything under the rid-specific targets in the assets file + var ns = project.Root.Name.Namespace; + project.Root.Element(ns + "PropertyGroup") + .Add(new XElement(ns + "EnsureNETCoreAppRuntime", false)); + }) + .Restore(Log, testProject.Name); + + var buildCommand = new BuildCommand(Log, testAsset.TestRoot, testProject.Name); + + // Test that compilation doesn't depend on any rid-specific assets by removing them from the assets file after it's been restored + var assetsFilePath = Path.Combine(buildCommand.GetBaseIntermediateDirectory().FullName, "project.assets.json"); + + JObject assetsContents = JObject.Parse(File.ReadAllText(assetsFilePath)); + foreach (JProperty target in assetsContents["targets"]) + { + if (target.Name.Contains("/")) + { + // This is a target element with a RID specified, so remove all its contents + target.Value = new JObject(); + } + } + string newContents = assetsContents.ToString(); + File.WriteAllText(assetsFilePath, newContents); - // compile should still pass with unknown RID because references are always pulled - // from RIDLess target - var buildCommand = new MSBuildCommand(Log, "Compile", fullPathProjectFile); buildCommand - .Execute("/p:RuntimeIdentifier=unkownrid") + .Execute() .Should() .Pass(); }