From 7fcec2b6bd9aa6f11377b3bd0cbbef0bd7dfae0c Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Mon, 29 Mar 2021 07:42:02 -0700 Subject: [PATCH 1/9] Use CSProj for packing Platforms package --- .../pkg/Microsoft.NETCore.Platforms.pkgproj | 18 ------------------ .../src/Microsoft.NETCore.Platforms.csproj | 17 +++++++++++++++-- .../Microsoft.NETCore.Platforms/src/_._ | 0 .../{pkg => src}/runtime.compatibility.json | 0 .../{pkg => src}/runtime.json | 0 .../{pkg => src}/runtimeGroups.props | 12 +++++++++--- 6 files changed, 24 insertions(+), 23 deletions(-) delete mode 100644 src/libraries/Microsoft.NETCore.Platforms/pkg/Microsoft.NETCore.Platforms.pkgproj create mode 100644 src/libraries/Microsoft.NETCore.Platforms/src/_._ rename src/libraries/Microsoft.NETCore.Platforms/{pkg => src}/runtime.compatibility.json (100%) rename src/libraries/Microsoft.NETCore.Platforms/{pkg => src}/runtime.json (100%) rename src/libraries/Microsoft.NETCore.Platforms/{pkg => src}/runtimeGroups.props (91%) diff --git a/src/libraries/Microsoft.NETCore.Platforms/pkg/Microsoft.NETCore.Platforms.pkgproj b/src/libraries/Microsoft.NETCore.Platforms/pkg/Microsoft.NETCore.Platforms.pkgproj deleted file mode 100644 index e24d21ae013bb4..00000000000000 --- a/src/libraries/Microsoft.NETCore.Platforms/pkg/Microsoft.NETCore.Platforms.pkgproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - true - - false - Provides runtime information required to resolve target framework, platform, and runtime specific implementations of .NETCore packages. - - - - - - lib/netstandard1.0 - - - - - diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj b/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj index 987d53fcabe74f..7c13e1da0f8a7f 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj +++ b/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj @@ -3,7 +3,20 @@ netstandard1.0 true - - false + + false + true + true + $(NoWarn);NU5128 + Provides runtime information required to resolve target framework, platform, and runtime specific implementations of .NETCore packages. + + + + + + + + + \ No newline at end of file diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/_._ b/src/libraries/Microsoft.NETCore.Platforms/src/_._ new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/src/libraries/Microsoft.NETCore.Platforms/pkg/runtime.compatibility.json b/src/libraries/Microsoft.NETCore.Platforms/src/runtime.compatibility.json similarity index 100% rename from src/libraries/Microsoft.NETCore.Platforms/pkg/runtime.compatibility.json rename to src/libraries/Microsoft.NETCore.Platforms/src/runtime.compatibility.json diff --git a/src/libraries/Microsoft.NETCore.Platforms/pkg/runtime.json b/src/libraries/Microsoft.NETCore.Platforms/src/runtime.json similarity index 100% rename from src/libraries/Microsoft.NETCore.Platforms/pkg/runtime.json rename to src/libraries/Microsoft.NETCore.Platforms/src/runtime.json diff --git a/src/libraries/Microsoft.NETCore.Platforms/pkg/runtimeGroups.props b/src/libraries/Microsoft.NETCore.Platforms/src/runtimeGroups.props similarity index 91% rename from src/libraries/Microsoft.NETCore.Platforms/pkg/runtimeGroups.props rename to src/libraries/Microsoft.NETCore.Platforms/src/runtimeGroups.props index 5d4a3edf9e9561..caea5c79540ca5 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/pkg/runtimeGroups.props +++ b/src/libraries/Microsoft.NETCore.Platforms/src/runtimeGroups.props @@ -262,15 +262,21 @@ - + + <_generateRuntimeGraphTask Condition="'$(MSBuildRuntimeType)' == 'core'">$(NuGetPackageRoot)microsoft.dotnet.build.tasks.packaging\$(MicrosoftDotNetBuildTasksPackagingVersion)\tools\netcoreapp3.1\Microsoft.DotNet.Build.Tasks.Packaging.dll + <_generateRuntimeGraphTask Condition="'$(MSBuildRuntimeType)' != 'core'">$(NuGetPackageRoot)microsoft.dotnet.build.tasks.packaging\$(MicrosoftDotNetBuildTasksPackagingVersion)\tools\net472\Microsoft.DotNet.Build.Tasks.Packaging.dll + + + + - + From d3d6392c3151097d3f5f3a2cc02081ee35a1c6e6 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Tue, 30 Mar 2021 08:39:05 -0700 Subject: [PATCH 2/9] Port GenerateRuntimeGraph task This ports the GenerateRuntimeGraph task from arcade as of https://github.com/dotnet/arcade/tree/d005a8ce6bb81129f9c3a4a907e1b7ccbf2504d5 --- eng/Versions.props | 6 +- .../src/AssemblyResolver.cs | 108 +++ .../src/BuildTask.Desktop.cs | 13 + .../src/BuildTask.cs | 151 ++++ .../src/Extensions.cs | 46 ++ .../src/GenerateRuntimeGraph.cs | 696 ++++++++++++++++++ .../src/Microsoft.NETCore.Platforms.csproj | 28 +- .../src/runtimeGroups.props | 5 +- 8 files changed, 1043 insertions(+), 10 deletions(-) create mode 100644 src/libraries/Microsoft.NETCore.Platforms/src/AssemblyResolver.cs create mode 100644 src/libraries/Microsoft.NETCore.Platforms/src/BuildTask.Desktop.cs create mode 100644 src/libraries/Microsoft.NETCore.Platforms/src/BuildTask.cs create mode 100644 src/libraries/Microsoft.NETCore.Platforms/src/Extensions.cs create mode 100644 src/libraries/Microsoft.NETCore.Platforms/src/GenerateRuntimeGraph.cs diff --git a/eng/Versions.props b/eng/Versions.props index bdb9ffcb33dbe7..d74990f09b32d7 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -139,12 +139,12 @@ These are used as reference assemblies only, so they must not take a ProdCon/source-build version. Insert "RefOnly" to avoid assignment via PVP. --> - 15.7.179 + 16.8.0 $(RefOnlyMicrosoftBuildVersion) $(RefOnlyMicrosoftBuildVersion) $(RefOnlyMicrosoftBuildVersion) - 4.9.4 - 4.9.4 + 5.8.0 + 5.8.0 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/AssemblyResolver.cs b/src/libraries/Microsoft.NETCore.Platforms/src/AssemblyResolver.cs new file mode 100644 index 00000000000000..a0f4aaad29bc0c --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/src/AssemblyResolver.cs @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; + +namespace Microsoft.NETCore.Platforms.BuildTasks +{ + /// + /// Used to enable app-local assembly unification. + /// + internal static class AssemblyResolver + { + static AssemblyResolver() + { + AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; + } + + /// + /// Call to enable the assembly resolver for the current AppDomain. + /// + public static void Enable() + { + // intentionally empty. This is just meant to ensure the static constructor + // has run. + } + + private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + // apply any existing policy + AssemblyName referenceName = new AssemblyName(AppDomain.CurrentDomain.ApplyPolicy(args.Name)); + + string fileName = referenceName.Name + ".dll"; + string assemblyPath = null; + string probingPath = null; + Assembly assm = null; + + // look next to requesting assembly + assemblyPath = args.RequestingAssembly?.Location; + if (!string.IsNullOrEmpty(assemblyPath)) + { + probingPath = Path.Combine(Path.GetDirectoryName(assemblyPath), fileName); + Debug.WriteLine($"Considering {probingPath} based on RequestingAssembly"); + if (Probe(probingPath, referenceName.Version, out assm)) + { + return assm; + } + } + + // look next to the executing assembly + assemblyPath = Assembly.GetExecutingAssembly().Location; + if (!string.IsNullOrEmpty(assemblyPath)) + { + probingPath = Path.Combine(Path.GetDirectoryName(assemblyPath), fileName); + + Debug.WriteLine($"Considering {probingPath} based on ExecutingAssembly"); + if (Probe(probingPath, referenceName.Version, out assm)) + { + return assm; + } + } + + // look in AppDomain base directory + probingPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); + Debug.WriteLine($"Considering {probingPath} based on BaseDirectory"); + if (Probe(probingPath, referenceName.Version, out assm)) + { + return assm; + } + + // look in current directory + Debug.WriteLine($"Considering {fileName}"); + if (Probe(fileName, referenceName.Version, out assm)) + { + return assm; + } + + return null; + } + + /// + /// Considers a path to load for satisfying an assembly ref and loads it + /// if the file exists and version is sufficient. + /// + /// Path to consider for load + /// Minimum version to consider + /// loaded assembly + /// true if assembly was loaded + private static bool Probe(string filePath, Version minimumVersion, out Assembly assembly) + { + if (File.Exists(filePath)) + { + AssemblyName name = AssemblyName.GetAssemblyName(filePath); + + if (name.Version >= minimumVersion) + { + assembly = Assembly.Load(name); + return true; + } + } + + assembly = null; + return false; + } + } +} diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/BuildTask.Desktop.cs b/src/libraries/Microsoft.NETCore.Platforms/src/BuildTask.Desktop.cs new file mode 100644 index 00000000000000..29af5f186cfb41 --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/src/BuildTask.Desktop.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.NETCore.Platforms.BuildTasks +{ + public partial class BuildTask + { + static BuildTask() + { + AssemblyResolver.Enable(); + } + } +} diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/BuildTask.cs b/src/libraries/Microsoft.NETCore.Platforms/src/BuildTask.cs new file mode 100644 index 00000000000000..fa7dd7edabda9a --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/src/BuildTask.cs @@ -0,0 +1,151 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using System; + +namespace Microsoft.NETCore.Platforms.BuildTasks +{ + public abstract partial class BuildTask : ITask + { + private Log _log; + + internal Log Log + { + get { return _log ?? (_log = new Log(new TaskLoggingHelper(this))); } + } + + public BuildTask() + { + } + + public IBuildEngine BuildEngine + { + get; + set; + } + + public ITaskHost HostObject + { + get; + set; + } + + public abstract bool Execute(); + } + + internal class Log : ILog + { + private readonly TaskLoggingHelper _logger; + public Log(TaskLoggingHelper logger) + { + _logger = logger; + } + + public void LogError(string message, params object[] messageArgs) + { + _logger.LogError(message, messageArgs); + } + + public void LogErrorFromException(Exception exception, bool showStackTrace) + { + _logger.LogErrorFromException(exception, showStackTrace); + } + + public void LogMessage(string message, params object[] messageArgs) + { + _logger.LogMessage(message, messageArgs); + } + + public void LogMessage(LogImportance importance, string message, params object[] messageArgs) + { + _logger.LogMessage((MessageImportance)importance, message, messageArgs); + } + + public void LogWarning(string message, params object[] messageArgs) + { + _logger.LogWarning(message, messageArgs); + } + + public bool HasLoggedErrors { get { return _logger.HasLoggedErrors; } } + } + + public enum LogImportance + { + Low = MessageImportance.Low, + Normal = MessageImportance.Normal, + High = MessageImportance.High + } + + + public interface ILog + { + // + // Summary: + // Logs an error with the specified message. + // + // Parameters: + // message: + // The message. + // + // messageArgs: + // Optional arguments for formatting the message string. + // + // Exceptions: + // T:System.ArgumentNullException: + // message is null. + void LogError(string message, params object[] messageArgs); + + // + // Summary: + // Logs a message with the specified string. + // + // Parameters: + // message: + // The message. + // + // messageArgs: + // The arguments for formatting the message. + // + // Exceptions: + // T:System.ArgumentNullException: + // message is null. + void LogMessage(string message, params object[] messageArgs); + + // + // Summary: + // Logs a message with the specified string and importance. + // + // Parameters: + // importance: + // One of the enumeration values that specifies the importance of the message. + // + // message: + // The message. + // + // messageArgs: + // The arguments for formatting the message. + // + // Exceptions: + // T:System.ArgumentNullException: + // message is null. + void LogMessage(LogImportance importance, string message, params object[] messageArgs); + + // + // Summary: + // Logs a warning with the specified message. + // + // Parameters: + // message: + // The message. + // + // messageArgs: + // Optional arguments for formatting the message string. + // + // Exceptions: + // T:System.ArgumentNullException: + // message is null. + void LogWarning(string message, params object[] messageArgs); + } +} diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/Extensions.cs b/src/libraries/Microsoft.NETCore.Platforms/src/Extensions.cs new file mode 100644 index 00000000000000..128b56082e1fec --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/src/Extensions.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.Build.Framework; + +namespace Microsoft.NETCore.Platforms.BuildTasks +{ + public static class Extensions + { + public static string GetString(this ITaskItem taskItem, string metadataName) + { + var metadataValue = taskItem.GetMetadata(metadataName)?.Trim(); + return string.IsNullOrEmpty(metadataValue) ? null : metadataValue; + } + + public static bool GetBoolean(this ITaskItem taskItem, string metadataName, bool defaultValue = false) + { + bool result = false; + var metadataValue = taskItem.GetMetadata(metadataName); + if (!bool.TryParse(metadataValue, out result)) + { + result = defaultValue; + } + return result; + } + + public static IEnumerable GetStrings(this ITaskItem taskItem, string metadataName) + { + var metadataValue = taskItem.GetMetadata(metadataName)?.Trim(); + if (!string.IsNullOrEmpty(metadataValue)) + { + return metadataValue.Split(';').Where(v => !string.IsNullOrEmpty(v.Trim())).ToArray(); + } + + return Enumerable.Empty(); + } + + public static IEnumerable NullAsEmpty(this IEnumerable source) + { + return source ?? Enumerable.Empty(); + } + + } +} diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/GenerateRuntimeGraph.cs b/src/libraries/Microsoft.NETCore.Platforms/src/GenerateRuntimeGraph.cs new file mode 100644 index 00000000000000..3ae2e5cda926ef --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/src/GenerateRuntimeGraph.cs @@ -0,0 +1,696 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml.Linq; +using Microsoft.Build.Framework; +using Newtonsoft.Json; +using NuGet.RuntimeModel; + +namespace Microsoft.NETCore.Platforms.BuildTasks +{ + public class GenerateRuntimeGraph : BuildTask + { + + /// + /// A set of RuntimeGroups that can be used to generate a runtime graph + /// Identity: the base string for the RID, without version architecture, or qualifiers. + /// Parent: the base string for the parent of this RID. This RID will be imported by the baseRID, architecture-specific, + /// and qualifier-specific RIDs (with the latter two appending appropriate architecture and qualifiers). + /// Versions: A list of strings delimited by semi-colons that represent the versions for this RID. + /// TreatVersionsAsCompatible: Default is true. When true, version-specific RIDs will import the previous + /// version-specific RID in the Versions list, with the first version importing the version-less RID. + /// When false all version-specific RIDs will import the version-less RID (bypassing previous version-specific RIDs) + /// OmitVersionDelimiter: Default is false. When true no characters will separate the base RID and version (EG: win7). + /// When false a '.' will separate the base RID and version (EG: osx.10.12). + /// ApplyVersionsToParent: Default is false. When true, version-specific RIDs will import version-specific Parent RIDs + /// similar to is done for architecture and qualifier (see Parent above). + /// Architectures: A list of strings delimited by semi-colons that represent the architectures for this RID. + /// AdditionalQualifiers: A list of strings delimited by semi-colons that represent the additional qualifiers for this RID. + /// Additional qualifers do not stack, each only applies to the qualifier-less RIDs (so as not to cause combinatorial + /// exponential growth of RIDs). + /// + /// The following options can be used under special circumstances but break the normal precedence rules we try to establish + /// by generating the RID graph from common logic. These options make it possible to create a RID fallback chain that doesn't + /// match the rest of the RIDs and therefore is hard for developers/package authors to reason about. + /// Only use these options for cases where you know what you are doing and have carefully reviewed the resulting RID fallbacks + /// using the CompatibliltyMap. + /// OmitRIDs: A list of strings delimited by semi-colons that represent RIDs calculated from this RuntimeGroup that should + /// be omitted from the RuntimeGraph. These RIDs will not be referenced nor defined. + /// OmitRIDDefinitions: A list of strings delimited by semi-colons that represent RIDs calculated from this RuntimeGroup + /// that should be omitted from the RuntimeGraph. These RIDs will not be defined by this RuntimeGroup, but will be + /// referenced: useful in case some other RuntimeGroup (or runtime.json template) defines them. + /// OmitRIDReferences: A list of strings delimited by semi-colons that represent RIDs calculated from this RuntimeGroup + /// that should be omitted from the RuntimeGraph. These RIDs will be defined but not referenced by this RuntimeGroup. + /// + public ITaskItem[] RuntimeGroups + { + get; + set; + } + + /// + /// Optional source Runtime.json to use as a starting point when merging additional RuntimeGroups + /// + public string SourceRuntimeJson + { + get; + set; + } + + /// + /// Where to write the final runtime.json + /// + public string RuntimeJson + { + get; + set; + } + + /// + /// Optionally, other runtime.jsons which may contain imported RIDs + /// + public string[] ExternalRuntimeJsons + { + get; + set; + } + + /// + /// When defined, specifies the file to write compatibility precedence for each RID in the graph. + /// + public string CompatibilityMap + { + get; + set; + } + + + /// + /// True to write the generated runtime.json to RuntimeJson and compatibility map to CompatibilityMap, otherwise files are read and diffed + /// with generated versions and an error is emitted if they differ. + /// Setting UpdateRuntimeFiles will overwrite files even when the file is marked ReadOnly. + /// + public bool UpdateRuntimeFiles + { + get; + set; + } + + /// + /// When defined, specifies the file to write a DGML representation of the runtime graph. + /// + public string RuntimeDirectedGraph + { + get; + set; + } + + public override bool Execute() + { + if (RuntimeGroups != null && RuntimeGroups.Any() && RuntimeJson == null) + { + Log.LogError($"{nameof(RuntimeJson)} argument must be specified when {nameof(RuntimeGroups)} is specified."); + return false; + } + + RuntimeGraph runtimeGraph; + if (!string.IsNullOrEmpty(SourceRuntimeJson)) + { + if (!File.Exists(SourceRuntimeJson)) + { + Log.LogError($"{nameof(SourceRuntimeJson)} did not exist at {SourceRuntimeJson}."); + return false; + } + + runtimeGraph = JsonRuntimeFormat.ReadRuntimeGraph(SourceRuntimeJson); + } + else + { + runtimeGraph = new RuntimeGraph(); + } + + foreach (var runtimeGroup in RuntimeGroups.NullAsEmpty().Select(i => new RuntimeGroup(i))) + { + runtimeGraph = SafeMerge(runtimeGraph, runtimeGroup); + } + + Dictionary externalRids = new Dictionary(); + if (ExternalRuntimeJsons != null) + { + foreach (var externalRuntimeJson in ExternalRuntimeJsons) + { + RuntimeGraph externalRuntimeGraph = JsonRuntimeFormat.ReadRuntimeGraph(externalRuntimeJson); + + foreach (var runtime in externalRuntimeGraph.Runtimes.Keys) + { + // don't check for duplicates, we merely care what is external + externalRids.Add(runtime, externalRuntimeJson); + } + } + } + + ValidateImports(runtimeGraph, externalRids); + + if (!string.IsNullOrEmpty(RuntimeJson)) + { + if (UpdateRuntimeFiles) + { + EnsureWritable(RuntimeJson); + WriteRuntimeGraph(RuntimeJson, runtimeGraph); + + } + else + { + // validate that existing file matches generated file + if (!File.Exists(RuntimeJson)) + { + Log.LogError($"{nameof(RuntimeJson)} did not exist at {RuntimeJson} and {nameof(UpdateRuntimeFiles)} was not specified."); + } + else + { + var existingRuntimeGraph = JsonRuntimeFormat.ReadRuntimeGraph(RuntimeJson); + + if (!existingRuntimeGraph.Equals(runtimeGraph)) + { + Log.LogError($"The generated {nameof(RuntimeJson)} differs from {RuntimeJson} and {nameof(UpdateRuntimeFiles)} was not specified. Please specify {nameof(UpdateRuntimeFiles)}=true to commit the changes."); + } + } + } + } + + if (!string.IsNullOrEmpty(CompatibilityMap)) + { + var compatibilityMap = GetCompatibilityMap(runtimeGraph); + if (UpdateRuntimeFiles) + { + EnsureWritable(CompatibilityMap); + WriteCompatibilityMap(compatibilityMap, CompatibilityMap); + } + else + { + // validate that existing file matches generated file + if (!File.Exists(CompatibilityMap)) + { + Log.LogError($"{nameof(CompatibilityMap)} did not exist at {CompatibilityMap} and {nameof(UpdateRuntimeFiles)} was not specified."); + } + else + { + var existingCompatibilityMap = ReadCompatibilityMap(CompatibilityMap); + + if (!CompatibilityMapEquals(existingCompatibilityMap, compatibilityMap)) + { + Log.LogError($"The generated {nameof(CompatibilityMap)} differs from {CompatibilityMap} and {nameof(UpdateRuntimeFiles)} was not specified. Please specify {nameof(UpdateRuntimeFiles)}=true to commit the changes."); + } + } + } + } + + if (!string.IsNullOrEmpty(RuntimeDirectedGraph)) + { + WriteRuntimeGraph(runtimeGraph, RuntimeDirectedGraph); + } + + return !Log.HasLoggedErrors; + } + + private void EnsureWritable(string file) + { + if (File.Exists(file)) + { + var existingAttributes = File.GetAttributes(file); + + if ((existingAttributes & FileAttributes.ReadOnly) != 0) + { + File.SetAttributes(file, existingAttributes &= ~FileAttributes.ReadOnly); + } + } + } + + public static void WriteRuntimeGraph(string filePath, RuntimeGraph runtimeGraph) + { + using (var fileStream = new FileStream(filePath, FileMode.Create)) + using (var textWriter = new StreamWriter(fileStream)) + using (var jsonWriter = new JsonTextWriter(textWriter)) + using (var writer = new JsonObjectWriter(jsonWriter)) + { + jsonWriter.Formatting = Formatting.Indented; + + // workaround https://github.com/NuGet/Home/issues/9532 + writer.WriteObjectStart(); + + JsonRuntimeFormat.WriteRuntimeGraph(writer, runtimeGraph); + + writer.WriteObjectEnd(); + } + } + + private RuntimeGraph SafeMerge(RuntimeGraph existingGraph, RuntimeGroup runtimeGroup) + { + var runtimeGraph = runtimeGroup.GetRuntimeGraph(); + + foreach (var existingRuntimeDescription in existingGraph.Runtimes.Values) + { + RuntimeDescription newRuntimeDescription; + + if (runtimeGraph.Runtimes.TryGetValue(existingRuntimeDescription.RuntimeIdentifier, out newRuntimeDescription)) + { + // overlapping RID, ensure that the imports match (same ordering and content) + if (!existingRuntimeDescription.InheritedRuntimes.SequenceEqual(newRuntimeDescription.InheritedRuntimes)) + { + Log.LogError($"RuntimeGroup {runtimeGroup.BaseRID} defines RID {newRuntimeDescription.RuntimeIdentifier} with imports {string.Join(";", newRuntimeDescription.InheritedRuntimes)} which differ from existing imports {string.Join(";", existingRuntimeDescription.InheritedRuntimes)}. You may avoid this by specifying {nameof(RuntimeGroup.OmitRIDDefinitions)} metadata with {newRuntimeDescription.RuntimeIdentifier}."); + } + } + } + + return RuntimeGraph.Merge(existingGraph, runtimeGraph); + } + + private void ValidateImports(RuntimeGraph runtimeGraph, IDictionary externalRIDs) + { + foreach (var runtimeDescription in runtimeGraph.Runtimes.Values) + { + string externalRuntimeJson; + + if (externalRIDs.TryGetValue(runtimeDescription.RuntimeIdentifier, out externalRuntimeJson)) + { + Log.LogError($"Runtime {runtimeDescription.RuntimeIdentifier} is defined in both this RuntimeGraph and {externalRuntimeJson}."); + } + + foreach (var import in runtimeDescription.InheritedRuntimes) + { + if (!runtimeGraph.Runtimes.ContainsKey(import) && !externalRIDs.ContainsKey(import)) + { + Log.LogError($"Runtime {runtimeDescription.RuntimeIdentifier} imports {import} which is not defined."); + } + } + } + } + + internal class RuntimeGroup + { + private const string rootRID = "any"; + private const char VersionDelimiter = '.'; + private const char ArchitectureDelimiter = '-'; + private const char QualifierDelimiter = '-'; + + public RuntimeGroup(ITaskItem item) + { + BaseRID = item.ItemSpec; + Parent = item.GetString(nameof(Parent)); + Versions = item.GetStrings(nameof(Versions)); + TreatVersionsAsCompatible = item.GetBoolean(nameof(TreatVersionsAsCompatible), true); + OmitVersionDelimiter = item.GetBoolean(nameof(OmitVersionDelimiter)); + ApplyVersionsToParent = item.GetBoolean(nameof(ApplyVersionsToParent)); + Architectures = item.GetStrings(nameof(Architectures)); + AdditionalQualifiers = item.GetStrings(nameof(AdditionalQualifiers)); + OmitRIDs = new HashSet(item.GetStrings(nameof(OmitRIDs))); + OmitRIDDefinitions = new HashSet(item.GetStrings(nameof(OmitRIDDefinitions))); + OmitRIDReferences = new HashSet(item.GetStrings(nameof(OmitRIDReferences))); + } + + public string BaseRID { get; } + public string Parent { get; } + public IEnumerable Versions { get; } + public bool TreatVersionsAsCompatible { get; } + public bool OmitVersionDelimiter { get; } + public bool ApplyVersionsToParent { get; } + public IEnumerable Architectures { get; } + public IEnumerable AdditionalQualifiers { get; } + public ICollection OmitRIDs { get; } + public ICollection OmitRIDDefinitions { get; } + public ICollection OmitRIDReferences { get; } + + private class RIDMapping + { + public RIDMapping(RID runtimeIdentifier) + { + RuntimeIdentifier = runtimeIdentifier; + Imports = Enumerable.Empty(); + } + + public RIDMapping(RID runtimeIdentifier, IEnumerable imports) + { + RuntimeIdentifier = runtimeIdentifier; + Imports = imports; + } + + public RID RuntimeIdentifier { get; } + + public IEnumerable Imports { get; } + } + + private class RID + { + public string BaseRID { get; set; } + public string VersionDelimiter { get; set; } + public string Version { get; set; } + public string ArchitectureDelimiter { get; set; } + public string Architecture { get; set; } + public string QualifierDelimiter { get; set; } + public string Qualifier { get; set; } + + public override string ToString() + { + StringBuilder builder = new StringBuilder(BaseRID); + + if (HasVersion()) + { + builder.Append(VersionDelimiter); + builder.Append(Version); + } + + if (HasArchitecture()) + { + builder.Append(ArchitectureDelimiter); + builder.Append(Architecture); + } + + if (HasQualifier()) + { + builder.Append(QualifierDelimiter); + builder.Append(Qualifier); + } + + return builder.ToString(); + } + + public bool HasVersion() + { + return Version != null; + } + + public bool HasArchitecture() + { + return Architecture != null; + } + + public bool HasQualifier() + { + return Qualifier != null; + } + } + + private RID CreateRuntime(string baseRid, string version = null, string architecture = null, string qualifier = null) + { + return new RID() + { + BaseRID = baseRid, + VersionDelimiter = OmitVersionDelimiter ? string.Empty : VersionDelimiter.ToString(), + Version = version, + ArchitectureDelimiter = ArchitectureDelimiter.ToString(), + Architecture = architecture, + QualifierDelimiter = QualifierDelimiter.ToString(), + Qualifier = qualifier + }; + } + + private IEnumerable GetRIDMappings() + { + // base => + // Parent + yield return Parent == null ? + new RIDMapping(CreateRuntime(BaseRID)) : + new RIDMapping(CreateRuntime(BaseRID), new[] { CreateRuntime(Parent) }); + + foreach (var architecture in Architectures) + { + // base + arch => + // base, + // parent + arch + var imports = new List() + { + CreateRuntime(BaseRID) + }; + + if (!IsNullOrRoot(Parent)) + { + imports.Add(CreateRuntime(Parent, architecture: architecture)); + } + + yield return new RIDMapping(CreateRuntime(BaseRID, architecture: architecture), imports); + } + + string lastVersion = null; + foreach (var version in Versions) + { + // base + version => + // base + lastVersion, + // parent + version (optionally) + var imports = new List() + { + CreateRuntime(BaseRID, version: lastVersion) + }; + + if (ApplyVersionsToParent) + { + imports.Add(CreateRuntime(Parent, version: version)); + } + + yield return new RIDMapping(CreateRuntime(BaseRID, version: version), imports); + + foreach (var architecture in Architectures) + { + // base + version + architecture => + // base + version, + // base + lastVersion + architecture, + // parent + version + architecture (optionally) + var archImports = new List() + { + CreateRuntime(BaseRID, version: version), + CreateRuntime(BaseRID, version: lastVersion, architecture: architecture) + }; + + if (ApplyVersionsToParent) + { + archImports.Add(CreateRuntime(Parent, version: version, architecture: architecture)); + } + + yield return new RIDMapping(CreateRuntime(BaseRID, version: version, architecture: architecture), archImports); + } + + if (TreatVersionsAsCompatible) + { + lastVersion = version; + } + } + + foreach (var qualifier in AdditionalQualifiers) + { + // base + qual => + // base, + // parent + qual + yield return new RIDMapping(CreateRuntime(BaseRID, qualifier: qualifier), + new[] + { + CreateRuntime(BaseRID), + IsNullOrRoot(Parent) ? CreateRuntime(qualifier) : CreateRuntime(Parent, qualifier:qualifier) + }); + + foreach (var architecture in Architectures) + { + // base + arch + qualifier => + // base + qualifier, + // base + arch + // parent + arch + qualifier + var imports = new List() + { + CreateRuntime(BaseRID, qualifier: qualifier), + CreateRuntime(BaseRID, architecture: architecture) + }; + + if (!IsNullOrRoot(Parent)) + { + imports.Add(CreateRuntime(Parent, architecture: architecture, qualifier: qualifier)); + } + + yield return new RIDMapping(CreateRuntime(BaseRID, architecture: architecture, qualifier: qualifier), imports); + } + + lastVersion = null; + foreach (var version in Versions) + { + // base + version + qualifier => + // base + version, + // base + lastVersion + qualifier + // parent + version + qualifier (optionally) + var imports = new List() + { + CreateRuntime(BaseRID, version: version), + CreateRuntime(BaseRID, version: lastVersion, qualifier: qualifier) + }; + + if (ApplyVersionsToParent) + { + imports.Add(CreateRuntime(Parent, version: version, qualifier: qualifier)); + } + + yield return new RIDMapping(CreateRuntime(BaseRID, version: version, qualifier: qualifier), imports); + + foreach (var architecture in Architectures) + { + // base + version + architecture + qualifier => + // base + version + qualifier, + // base + version + architecture, + // base + version, + // base + lastVersion + architecture + qualifier, + // parent + version + architecture + qualifier (optionally) + var archImports = new List() + { + CreateRuntime(BaseRID, version: version, qualifier: qualifier), + CreateRuntime(BaseRID, version: version, architecture: architecture), + CreateRuntime(BaseRID, version: version), + CreateRuntime(BaseRID, version: lastVersion, architecture: architecture, qualifier: qualifier) + }; + + if (ApplyVersionsToParent) + { + imports.Add(CreateRuntime(Parent, version: version, architecture: architecture, qualifier: qualifier)); + } + + yield return new RIDMapping(CreateRuntime(BaseRID, version: version, architecture: architecture, qualifier: qualifier), archImports); + } + + if (TreatVersionsAsCompatible) + { + lastVersion = version; + } + } + } + } + + private bool IsNullOrRoot(string rid) + { + return rid == null || rid == rootRID; + } + + + public IEnumerable GetRuntimeDescriptions() + { + foreach (var mapping in GetRIDMappings()) + { + var rid = mapping.RuntimeIdentifier.ToString(); + + if (OmitRIDs.Contains(rid) || OmitRIDDefinitions.Contains(rid)) + { + continue; + } + + var imports = mapping.Imports + .Select(i => i.ToString()) + .Where(i => !OmitRIDs.Contains(i) && !OmitRIDReferences.Contains(i)) + .ToArray(); + + yield return new RuntimeDescription(rid, imports); + } + } + + public RuntimeGraph GetRuntimeGraph() + { + return new RuntimeGraph(GetRuntimeDescriptions()); + } + } + + private static IDictionary> GetCompatibilityMap(RuntimeGraph graph) + { + Dictionary> compatibilityMap = new Dictionary>(); + + foreach (var rid in graph.Runtimes.Keys.OrderBy(rid => rid, StringComparer.Ordinal)) + { + compatibilityMap.Add(rid, graph.ExpandRuntime(rid)); + } + + return compatibilityMap; + } + + private static IDictionary> ReadCompatibilityMap(string mapFile) + { + var serializer = new JsonSerializer(); + using (var file = File.OpenText(mapFile)) + using (var jsonTextReader = new JsonTextReader(file)) + { + return serializer.Deserialize>>(jsonTextReader); + } + } + + private static void WriteCompatibilityMap(IDictionary> compatibilityMap, string mapFile) + { + var serializer = new JsonSerializer() + { + Formatting = Formatting.Indented, + StringEscapeHandling = StringEscapeHandling.EscapeNonAscii + }; + + string directory = Path.GetDirectoryName(mapFile); + if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + using (var file = File.CreateText(mapFile)) + { + serializer.Serialize(file, compatibilityMap); + } + } + + private static bool CompatibilityMapEquals(IDictionary> left, IDictionary> right) + { + if (left.Count != right.Count) + { + return false; + } + + foreach (var leftPair in left) + { + IEnumerable rightValue; + + if (!right.TryGetValue(leftPair.Key, out rightValue)) + { + return false; + } + + if (!rightValue.SequenceEqual(leftPair.Value)) + { + return false; + } + } + + return true; + } + + private static XNamespace s_dgmlns = @"http://schemas.microsoft.com/vs/2009/dgml"; + private static void WriteRuntimeGraph(RuntimeGraph graph, string dependencyGraphFilePath) + { + + var doc = new XDocument(new XElement(s_dgmlns + "DirectedGraph")); + var nodesElement = new XElement(s_dgmlns + "Nodes"); + var linksElement = new XElement(s_dgmlns + "Links"); + doc.Root.Add(nodesElement); + doc.Root.Add(linksElement); + + var nodeIds = new HashSet(StringComparer.OrdinalIgnoreCase); + + foreach (var runtimeDescription in graph.Runtimes.Values) + { + nodesElement.Add(new XElement(s_dgmlns + "Node", + new XAttribute("Id", runtimeDescription.RuntimeIdentifier))); + + foreach (var import in runtimeDescription.InheritedRuntimes) + { + linksElement.Add(new XElement(s_dgmlns + "Link", + new XAttribute("Source", runtimeDescription.RuntimeIdentifier), + new XAttribute("Target", import))); + } + } + + using (var file = File.Create(dependencyGraphFilePath)) + { + doc.Save(file); + } + } + } +} diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj b/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj index 7c13e1da0f8a7f..de9a3b2616c9ec 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj +++ b/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj @@ -1,22 +1,40 @@ - + - netstandard1.0 - true + $(NetCoreAppToolCurrent);net472 + $(MSBuildProjectName) + true + Microsoft.NETCore.Platforms.BuildTasks false true + false true $(NoWarn);NU5128 Provides runtime information required to resolve target framework, platform, and runtime specific implementations of .NETCore packages. + + + + + + + + + + + - - + + + + + + \ No newline at end of file diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/runtimeGroups.props b/src/libraries/Microsoft.NETCore.Platforms/src/runtimeGroups.props index caea5c79540ca5..75f41db11693e4 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/src/runtimeGroups.props +++ b/src/libraries/Microsoft.NETCore.Platforms/src/runtimeGroups.props @@ -263,8 +263,9 @@ - <_generateRuntimeGraphTask Condition="'$(MSBuildRuntimeType)' == 'core'">$(NuGetPackageRoot)microsoft.dotnet.build.tasks.packaging\$(MicrosoftDotNetBuildTasksPackagingVersion)\tools\netcoreapp3.1\Microsoft.DotNet.Build.Tasks.Packaging.dll - <_generateRuntimeGraphTask Condition="'$(MSBuildRuntimeType)' != 'core'">$(NuGetPackageRoot)microsoft.dotnet.build.tasks.packaging\$(MicrosoftDotNetBuildTasksPackagingVersion)\tools\net472\Microsoft.DotNet.Build.Tasks.Packaging.dll + <_generateRuntimeGraphTargetFramework Condition="'$(MSBuildRuntimeType)' == 'core'">$(NetCoreAppToolCurrent) + <_generateRuntimeGraphTargetFramework Condition="'$(MSBuildRuntimeType)' != 'core'">net472 + <_generateRuntimeGraphTask>$([MSBuild]::NormalizePath('$(BaseOutputPath)', '$(_generateRuntimeGraphTargetFramework)-$(Configuration)', '$(AssemblyName).dll')) From b82c38f822b71e93f42b57afb725ad868d1ed4f3 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Tue, 30 Mar 2021 12:49:54 -0700 Subject: [PATCH 3/9] Refactor GenerateRuntimeGraph into separate files --- .../src/GenerateRuntimeGraph.cs | 304 ------------------ .../src/Microsoft.NETCore.Platforms.csproj | 2 + .../Microsoft.NETCore.Platforms/src/RID.cs | 59 ++++ .../src/RuntimeGroup.cs | 264 +++++++++++++++ 4 files changed, 325 insertions(+), 304 deletions(-) create mode 100644 src/libraries/Microsoft.NETCore.Platforms/src/RID.cs create mode 100644 src/libraries/Microsoft.NETCore.Platforms/src/RuntimeGroup.cs diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/GenerateRuntimeGraph.cs b/src/libraries/Microsoft.NETCore.Platforms/src/GenerateRuntimeGraph.cs index 3ae2e5cda926ef..cbee78b71a6b06 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/src/GenerateRuntimeGraph.cs +++ b/src/libraries/Microsoft.NETCore.Platforms/src/GenerateRuntimeGraph.cs @@ -291,310 +291,6 @@ private void ValidateImports(RuntimeGraph runtimeGraph, IDictionary(item.GetStrings(nameof(OmitRIDs))); - OmitRIDDefinitions = new HashSet(item.GetStrings(nameof(OmitRIDDefinitions))); - OmitRIDReferences = new HashSet(item.GetStrings(nameof(OmitRIDReferences))); - } - - public string BaseRID { get; } - public string Parent { get; } - public IEnumerable Versions { get; } - public bool TreatVersionsAsCompatible { get; } - public bool OmitVersionDelimiter { get; } - public bool ApplyVersionsToParent { get; } - public IEnumerable Architectures { get; } - public IEnumerable AdditionalQualifiers { get; } - public ICollection OmitRIDs { get; } - public ICollection OmitRIDDefinitions { get; } - public ICollection OmitRIDReferences { get; } - - private class RIDMapping - { - public RIDMapping(RID runtimeIdentifier) - { - RuntimeIdentifier = runtimeIdentifier; - Imports = Enumerable.Empty(); - } - - public RIDMapping(RID runtimeIdentifier, IEnumerable imports) - { - RuntimeIdentifier = runtimeIdentifier; - Imports = imports; - } - - public RID RuntimeIdentifier { get; } - - public IEnumerable Imports { get; } - } - - private class RID - { - public string BaseRID { get; set; } - public string VersionDelimiter { get; set; } - public string Version { get; set; } - public string ArchitectureDelimiter { get; set; } - public string Architecture { get; set; } - public string QualifierDelimiter { get; set; } - public string Qualifier { get; set; } - - public override string ToString() - { - StringBuilder builder = new StringBuilder(BaseRID); - - if (HasVersion()) - { - builder.Append(VersionDelimiter); - builder.Append(Version); - } - - if (HasArchitecture()) - { - builder.Append(ArchitectureDelimiter); - builder.Append(Architecture); - } - - if (HasQualifier()) - { - builder.Append(QualifierDelimiter); - builder.Append(Qualifier); - } - - return builder.ToString(); - } - - public bool HasVersion() - { - return Version != null; - } - - public bool HasArchitecture() - { - return Architecture != null; - } - - public bool HasQualifier() - { - return Qualifier != null; - } - } - - private RID CreateRuntime(string baseRid, string version = null, string architecture = null, string qualifier = null) - { - return new RID() - { - BaseRID = baseRid, - VersionDelimiter = OmitVersionDelimiter ? string.Empty : VersionDelimiter.ToString(), - Version = version, - ArchitectureDelimiter = ArchitectureDelimiter.ToString(), - Architecture = architecture, - QualifierDelimiter = QualifierDelimiter.ToString(), - Qualifier = qualifier - }; - } - - private IEnumerable GetRIDMappings() - { - // base => - // Parent - yield return Parent == null ? - new RIDMapping(CreateRuntime(BaseRID)) : - new RIDMapping(CreateRuntime(BaseRID), new[] { CreateRuntime(Parent) }); - - foreach (var architecture in Architectures) - { - // base + arch => - // base, - // parent + arch - var imports = new List() - { - CreateRuntime(BaseRID) - }; - - if (!IsNullOrRoot(Parent)) - { - imports.Add(CreateRuntime(Parent, architecture: architecture)); - } - - yield return new RIDMapping(CreateRuntime(BaseRID, architecture: architecture), imports); - } - - string lastVersion = null; - foreach (var version in Versions) - { - // base + version => - // base + lastVersion, - // parent + version (optionally) - var imports = new List() - { - CreateRuntime(BaseRID, version: lastVersion) - }; - - if (ApplyVersionsToParent) - { - imports.Add(CreateRuntime(Parent, version: version)); - } - - yield return new RIDMapping(CreateRuntime(BaseRID, version: version), imports); - - foreach (var architecture in Architectures) - { - // base + version + architecture => - // base + version, - // base + lastVersion + architecture, - // parent + version + architecture (optionally) - var archImports = new List() - { - CreateRuntime(BaseRID, version: version), - CreateRuntime(BaseRID, version: lastVersion, architecture: architecture) - }; - - if (ApplyVersionsToParent) - { - archImports.Add(CreateRuntime(Parent, version: version, architecture: architecture)); - } - - yield return new RIDMapping(CreateRuntime(BaseRID, version: version, architecture: architecture), archImports); - } - - if (TreatVersionsAsCompatible) - { - lastVersion = version; - } - } - - foreach (var qualifier in AdditionalQualifiers) - { - // base + qual => - // base, - // parent + qual - yield return new RIDMapping(CreateRuntime(BaseRID, qualifier: qualifier), - new[] - { - CreateRuntime(BaseRID), - IsNullOrRoot(Parent) ? CreateRuntime(qualifier) : CreateRuntime(Parent, qualifier:qualifier) - }); - - foreach (var architecture in Architectures) - { - // base + arch + qualifier => - // base + qualifier, - // base + arch - // parent + arch + qualifier - var imports = new List() - { - CreateRuntime(BaseRID, qualifier: qualifier), - CreateRuntime(BaseRID, architecture: architecture) - }; - - if (!IsNullOrRoot(Parent)) - { - imports.Add(CreateRuntime(Parent, architecture: architecture, qualifier: qualifier)); - } - - yield return new RIDMapping(CreateRuntime(BaseRID, architecture: architecture, qualifier: qualifier), imports); - } - - lastVersion = null; - foreach (var version in Versions) - { - // base + version + qualifier => - // base + version, - // base + lastVersion + qualifier - // parent + version + qualifier (optionally) - var imports = new List() - { - CreateRuntime(BaseRID, version: version), - CreateRuntime(BaseRID, version: lastVersion, qualifier: qualifier) - }; - - if (ApplyVersionsToParent) - { - imports.Add(CreateRuntime(Parent, version: version, qualifier: qualifier)); - } - - yield return new RIDMapping(CreateRuntime(BaseRID, version: version, qualifier: qualifier), imports); - - foreach (var architecture in Architectures) - { - // base + version + architecture + qualifier => - // base + version + qualifier, - // base + version + architecture, - // base + version, - // base + lastVersion + architecture + qualifier, - // parent + version + architecture + qualifier (optionally) - var archImports = new List() - { - CreateRuntime(BaseRID, version: version, qualifier: qualifier), - CreateRuntime(BaseRID, version: version, architecture: architecture), - CreateRuntime(BaseRID, version: version), - CreateRuntime(BaseRID, version: lastVersion, architecture: architecture, qualifier: qualifier) - }; - - if (ApplyVersionsToParent) - { - imports.Add(CreateRuntime(Parent, version: version, architecture: architecture, qualifier: qualifier)); - } - - yield return new RIDMapping(CreateRuntime(BaseRID, version: version, architecture: architecture, qualifier: qualifier), archImports); - } - - if (TreatVersionsAsCompatible) - { - lastVersion = version; - } - } - } - } - - private bool IsNullOrRoot(string rid) - { - return rid == null || rid == rootRID; - } - - - public IEnumerable GetRuntimeDescriptions() - { - foreach (var mapping in GetRIDMappings()) - { - var rid = mapping.RuntimeIdentifier.ToString(); - - if (OmitRIDs.Contains(rid) || OmitRIDDefinitions.Contains(rid)) - { - continue; - } - - var imports = mapping.Imports - .Select(i => i.ToString()) - .Where(i => !OmitRIDs.Contains(i) && !OmitRIDReferences.Contains(i)) - .ToArray(); - - yield return new RuntimeDescription(rid, imports); - } - } - - public RuntimeGraph GetRuntimeGraph() - { - return new RuntimeGraph(GetRuntimeDescriptions()); - } - } - private static IDictionary> GetCompatibilityMap(RuntimeGraph graph) { Dictionary> compatibilityMap = new Dictionary>(); diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj b/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj index de9a3b2616c9ec..3c00187ddb18e1 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj +++ b/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj @@ -23,6 +23,8 @@ + + diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/RID.cs b/src/libraries/Microsoft.NETCore.Platforms/src/RID.cs new file mode 100644 index 00000000000000..357790fbfdd54d --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/src/RID.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text; + +namespace Microsoft.NETCore.Platforms.BuildTasks +{ + internal class RID + { + public string BaseRID { get; set; } + public string VersionDelimiter { get; set; } + public string Version { get; set; } + public string ArchitectureDelimiter { get; set; } + public string Architecture { get; set; } + public string QualifierDelimiter { get; set; } + public string Qualifier { get; set; } + + public override string ToString() + { + StringBuilder builder = new StringBuilder(BaseRID); + + if (HasVersion()) + { + builder.Append(VersionDelimiter); + builder.Append(Version); + } + + if (HasArchitecture()) + { + builder.Append(ArchitectureDelimiter); + builder.Append(Architecture); + } + + if (HasQualifier()) + { + builder.Append(QualifierDelimiter); + builder.Append(Qualifier); + } + + return builder.ToString(); + } + + public bool HasVersion() + { + return Version != null; + } + + public bool HasArchitecture() + { + return Architecture != null; + } + + public bool HasQualifier() + { + return Qualifier != null; + } + } + +} diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/RuntimeGroup.cs b/src/libraries/Microsoft.NETCore.Platforms/src/RuntimeGroup.cs new file mode 100644 index 00000000000000..2ddd60022c426a --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/src/RuntimeGroup.cs @@ -0,0 +1,264 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.Build.Framework; +using NuGet.RuntimeModel; + +namespace Microsoft.NETCore.Platforms.BuildTasks +{ + + internal class RuntimeGroup + { + private const string rootRID = "any"; + private const char VersionDelimiter = '.'; + private const char ArchitectureDelimiter = '-'; + private const char QualifierDelimiter = '-'; + + public RuntimeGroup(ITaskItem item) + { + BaseRID = item.ItemSpec; + Parent = item.GetString(nameof(Parent)); + Versions = item.GetStrings(nameof(Versions)); + TreatVersionsAsCompatible = item.GetBoolean(nameof(TreatVersionsAsCompatible), true); + OmitVersionDelimiter = item.GetBoolean(nameof(OmitVersionDelimiter)); + ApplyVersionsToParent = item.GetBoolean(nameof(ApplyVersionsToParent)); + Architectures = item.GetStrings(nameof(Architectures)); + AdditionalQualifiers = item.GetStrings(nameof(AdditionalQualifiers)); + OmitRIDs = new HashSet(item.GetStrings(nameof(OmitRIDs))); + OmitRIDDefinitions = new HashSet(item.GetStrings(nameof(OmitRIDDefinitions))); + OmitRIDReferences = new HashSet(item.GetStrings(nameof(OmitRIDReferences))); + } + + public string BaseRID { get; } + public string Parent { get; } + public IEnumerable Versions { get; } + public bool TreatVersionsAsCompatible { get; } + public bool OmitVersionDelimiter { get; } + public bool ApplyVersionsToParent { get; } + public IEnumerable Architectures { get; } + public IEnumerable AdditionalQualifiers { get; } + public ICollection OmitRIDs { get; } + public ICollection OmitRIDDefinitions { get; } + public ICollection OmitRIDReferences { get; } + + private class RIDMapping + { + public RIDMapping(RID runtimeIdentifier) + { + RuntimeIdentifier = runtimeIdentifier; + Imports = Enumerable.Empty(); + } + + public RIDMapping(RID runtimeIdentifier, IEnumerable imports) + { + RuntimeIdentifier = runtimeIdentifier; + Imports = imports; + } + + public RID RuntimeIdentifier { get; } + + public IEnumerable Imports { get; } + } + + private RID CreateRuntime(string baseRid, string version = null, string architecture = null, string qualifier = null) + { + return new RID() + { + BaseRID = baseRid, + VersionDelimiter = OmitVersionDelimiter ? string.Empty : VersionDelimiter.ToString(), + Version = version, + ArchitectureDelimiter = ArchitectureDelimiter.ToString(), + Architecture = architecture, + QualifierDelimiter = QualifierDelimiter.ToString(), + Qualifier = qualifier + }; + } + + private IEnumerable GetRIDMappings() + { + // base => + // Parent + yield return Parent == null ? + new RIDMapping(CreateRuntime(BaseRID)) : + new RIDMapping(CreateRuntime(BaseRID), new[] { CreateRuntime(Parent) }); + + foreach (var architecture in Architectures) + { + // base + arch => + // base, + // parent + arch + var imports = new List() + { + CreateRuntime(BaseRID) + }; + + if (!IsNullOrRoot(Parent)) + { + imports.Add(CreateRuntime(Parent, architecture: architecture)); + } + + yield return new RIDMapping(CreateRuntime(BaseRID, architecture: architecture), imports); + } + + string lastVersion = null; + foreach (var version in Versions) + { + // base + version => + // base + lastVersion, + // parent + version (optionally) + var imports = new List() + { + CreateRuntime(BaseRID, version: lastVersion) + }; + + if (ApplyVersionsToParent) + { + imports.Add(CreateRuntime(Parent, version: version)); + } + + yield return new RIDMapping(CreateRuntime(BaseRID, version: version), imports); + + foreach (var architecture in Architectures) + { + // base + version + architecture => + // base + version, + // base + lastVersion + architecture, + // parent + version + architecture (optionally) + var archImports = new List() + { + CreateRuntime(BaseRID, version: version), + CreateRuntime(BaseRID, version: lastVersion, architecture: architecture) + }; + + if (ApplyVersionsToParent) + { + archImports.Add(CreateRuntime(Parent, version: version, architecture: architecture)); + } + + yield return new RIDMapping(CreateRuntime(BaseRID, version: version, architecture: architecture), archImports); + } + + if (TreatVersionsAsCompatible) + { + lastVersion = version; + } + } + + foreach (var qualifier in AdditionalQualifiers) + { + // base + qual => + // base, + // parent + qual + yield return new RIDMapping(CreateRuntime(BaseRID, qualifier: qualifier), + new[] + { + CreateRuntime(BaseRID), + IsNullOrRoot(Parent) ? CreateRuntime(qualifier) : CreateRuntime(Parent, qualifier:qualifier) + }); + + foreach (var architecture in Architectures) + { + // base + arch + qualifier => + // base + qualifier, + // base + arch + // parent + arch + qualifier + var imports = new List() + { + CreateRuntime(BaseRID, qualifier: qualifier), + CreateRuntime(BaseRID, architecture: architecture) + }; + + if (!IsNullOrRoot(Parent)) + { + imports.Add(CreateRuntime(Parent, architecture: architecture, qualifier: qualifier)); + } + + yield return new RIDMapping(CreateRuntime(BaseRID, architecture: architecture, qualifier: qualifier), imports); + } + + lastVersion = null; + foreach (var version in Versions) + { + // base + version + qualifier => + // base + version, + // base + lastVersion + qualifier + // parent + version + qualifier (optionally) + var imports = new List() + { + CreateRuntime(BaseRID, version: version), + CreateRuntime(BaseRID, version: lastVersion, qualifier: qualifier) + }; + + if (ApplyVersionsToParent) + { + imports.Add(CreateRuntime(Parent, version: version, qualifier: qualifier)); + } + + yield return new RIDMapping(CreateRuntime(BaseRID, version: version, qualifier: qualifier), imports); + + foreach (var architecture in Architectures) + { + // base + version + architecture + qualifier => + // base + version + qualifier, + // base + version + architecture, + // base + version, + // base + lastVersion + architecture + qualifier, + // parent + version + architecture + qualifier (optionally) + var archImports = new List() + { + CreateRuntime(BaseRID, version: version, qualifier: qualifier), + CreateRuntime(BaseRID, version: version, architecture: architecture), + CreateRuntime(BaseRID, version: version), + CreateRuntime(BaseRID, version: lastVersion, architecture: architecture, qualifier: qualifier) + }; + + if (ApplyVersionsToParent) + { + imports.Add(CreateRuntime(Parent, version: version, architecture: architecture, qualifier: qualifier)); + } + + yield return new RIDMapping(CreateRuntime(BaseRID, version: version, architecture: architecture, qualifier: qualifier), archImports); + } + + if (TreatVersionsAsCompatible) + { + lastVersion = version; + } + } + } + } + + private bool IsNullOrRoot(string rid) + { + return rid == null || rid == rootRID; + } + + + public IEnumerable GetRuntimeDescriptions() + { + foreach (var mapping in GetRIDMappings()) + { + var rid = mapping.RuntimeIdentifier.ToString(); + + if (OmitRIDs.Contains(rid) || OmitRIDDefinitions.Contains(rid)) + { + continue; + } + + var imports = mapping.Imports + .Select(i => i.ToString()) + .Where(i => !OmitRIDs.Contains(i) && !OmitRIDReferences.Contains(i)) + .ToArray(); + + yield return new RuntimeDescription(rid, imports); + } + } + + public RuntimeGraph GetRuntimeGraph() + { + return new RuntimeGraph(GetRuntimeDescriptions()); + } + } +} From f7ce36b812425d5cc90dce27eb6e13394e765e01 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Tue, 30 Mar 2021 22:17:53 -0700 Subject: [PATCH 4/9] Add test for GenerateRuntimeGraph --- .../Microsoft.NETCore.Platforms.sln | 31 ++++++++++ .../tests/GenerateRuntimeGraphTests.cs | 58 +++++++++++++++++++ .../Microsoft.NETCore.Platforms/tests/Log.cs | 56 ++++++++++++++++++ .../Microsoft.NETCore.Platforms.Tests.csproj | 22 +++++++ .../tests/TestBuildEngine.cs | 56 ++++++++++++++++++ 5 files changed, 223 insertions(+) create mode 100644 src/libraries/Microsoft.NETCore.Platforms/Microsoft.NETCore.Platforms.sln create mode 100644 src/libraries/Microsoft.NETCore.Platforms/tests/GenerateRuntimeGraphTests.cs create mode 100644 src/libraries/Microsoft.NETCore.Platforms/tests/Log.cs create mode 100644 src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj create mode 100644 src/libraries/Microsoft.NETCore.Platforms/tests/TestBuildEngine.cs diff --git a/src/libraries/Microsoft.NETCore.Platforms/Microsoft.NETCore.Platforms.sln b/src/libraries/Microsoft.NETCore.Platforms/Microsoft.NETCore.Platforms.sln new file mode 100644 index 00000000000000..a31d754662018e --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/Microsoft.NETCore.Platforms.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31004.24 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NETCore.Platforms", "src\Microsoft.NETCore.Platforms.csproj", "{BFFF96CC-06AA-4291-9F93-3E77F23DBB11}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NETCore.Platforms.Tests", "tests\Microsoft.NETCore.Platforms.Tests.csproj", "{0C60F372-5C73-4BFA-9B91-5659C88F9750}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BFFF96CC-06AA-4291-9F93-3E77F23DBB11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BFFF96CC-06AA-4291-9F93-3E77F23DBB11}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BFFF96CC-06AA-4291-9F93-3E77F23DBB11}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BFFF96CC-06AA-4291-9F93-3E77F23DBB11}.Release|Any CPU.Build.0 = Release|Any CPU + {0C60F372-5C73-4BFA-9B91-5659C88F9750}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0C60F372-5C73-4BFA-9B91-5659C88F9750}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0C60F372-5C73-4BFA-9B91-5659C88F9750}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0C60F372-5C73-4BFA-9B91-5659C88F9750}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E946A528-C3E7-48EC-AD6D-AE84ED2B11AC} + EndGlobalSection +EndGlobal diff --git a/src/libraries/Microsoft.NETCore.Platforms/tests/GenerateRuntimeGraphTests.cs b/src/libraries/Microsoft.NETCore.Platforms/tests/GenerateRuntimeGraphTests.cs new file mode 100644 index 00000000000000..652ca7f87ec334 --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/tests/GenerateRuntimeGraphTests.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Linq; +using Microsoft.Build.Evaluation; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.NETCore.Platforms.BuildTasks.Tests +{ + public class GenerateRuntimeGraphTests + { + private Log _log; + private TestBuildEngine _engine; + + public GenerateRuntimeGraphTests(ITestOutputHelper output) + { + _log = new Log(output); + _engine = new TestBuildEngine(_log); + } + + [Fact] + public void CanCreateRuntimeGraph() + { + string runtimeFile = "runtime.json"; + + Project runtimeGroupProps = new Project("runtimeGroups.props"); + + ITaskItem[] runtimeGroups = runtimeGroupProps.GetItems("RuntimeGroupWithQualifiers") + .Select(i => CreateItem(i)).ToArray(); + + Assert.NotEmpty(runtimeGroups); + + // will generate and compare to existing file. + GenerateRuntimeGraph task = new GenerateRuntimeGraph() + { + BuildEngine = _engine, + RuntimeGroups = runtimeGroups, + RuntimeJson = runtimeFile + }; + task.Execute(); + + _log.AssertNoErrorsOrWarnings(); + } + + private static ITaskItem CreateItem(ProjectItem projectItem) + { + TaskItem item = new TaskItem(projectItem.EvaluatedInclude); + foreach(var metadatum in projectItem.Metadata) + { + item.SetMetadata(metadatum.Name, metadatum.EvaluatedValue); + } + return item; + } + } +} diff --git a/src/libraries/Microsoft.NETCore.Platforms/tests/Log.cs b/src/libraries/Microsoft.NETCore.Platforms/tests/Log.cs new file mode 100644 index 00000000000000..d1e7368ec0882b --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/tests/Log.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.NETCore.Platforms.BuildTasks.Tests +{ + internal class Log : ILog + { + private readonly ITestOutputHelper _output; + + public Log(ITestOutputHelper output) + { + _output = output; + Reset(); + } + + public int ErrorsLogged { get; set; } + public int WarningsLogged { get; set; } + + public void LogError(string message, params object[] messageArgs) + { + ErrorsLogged++; + _output.WriteLine("Error: " + message, messageArgs); + } + + public void LogMessage(string message, params object[] messageArgs) + { + _output.WriteLine(message, messageArgs); + } + + public void LogMessage(LogImportance importance, string message, params object[] messageArgs) + { + _output.WriteLine(message, messageArgs); + } + + public void LogWarning(string message, params object[] messageArgs) + { + WarningsLogged++; + _output.WriteLine("Warning: " + message, messageArgs); + } + + public void Reset() + { + ErrorsLogged = 0; + WarningsLogged = 0; + } + + public void AssertNoErrorsOrWarnings() + { + Assert.Equal(0, ErrorsLogged); + Assert.Equal(0, WarningsLogged); + } + } +} diff --git a/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj b/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj new file mode 100644 index 00000000000000..d3757f225cc3b8 --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj @@ -0,0 +1,22 @@ + + + $(NetCoreAppCurrent);net472 + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/libraries/Microsoft.NETCore.Platforms/tests/TestBuildEngine.cs b/src/libraries/Microsoft.NETCore.Platforms/tests/TestBuildEngine.cs new file mode 100644 index 00000000000000..67dfefbe8c8c9b --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/tests/TestBuildEngine.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Build.Framework; +using System; +using System.Collections; + +namespace Microsoft.NETCore.Platforms.BuildTasks.Tests +{ + public class TestBuildEngine : IBuildEngine + { + private ILog _log; + + public TestBuildEngine(ILog log) + { + ColumnNumberOfTaskNode = 0; + ContinueOnError = true; + LineNumberOfTaskNode = 0; + ProjectFileOfTaskNode = "test"; + _log = log; + } + + public int ColumnNumberOfTaskNode { get; set; } + + public bool ContinueOnError { get; set; } + + public int LineNumberOfTaskNode { get; set; } + + public string ProjectFileOfTaskNode { get; set; } + + public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs) + { + throw new NotImplementedException(); + } + + public void LogCustomEvent(CustomBuildEventArgs e) + { + _log.LogMessage(e.Message); + } + + public void LogErrorEvent(BuildErrorEventArgs e) + { + _log.LogError(e.Message); + } + + public void LogMessageEvent(BuildMessageEventArgs e) + { + _log.LogMessage((LogImportance)e.Importance, e.Message); + } + + public void LogWarningEvent(BuildWarningEventArgs e) + { + _log.LogWarning(e.Message); + } + } +} From 63adcb338f86d092e29de5fc2228ba1658c4d973 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Wed, 31 Mar 2021 10:20:13 -0700 Subject: [PATCH 5/9] Fix up path to runtime.json --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 784fa37b6f2b78..be3d42e18ba93f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -228,7 +228,7 @@ https://github.com/dotnet/$(GitHubRepositoryName) https://dot.net microsoft,dotnetframework - $([MSBuild]::NormalizePath('$(LibrariesProjectRoot)', 'Microsoft.NETCore.Platforms', 'pkg', 'runtime.json')) + $([MSBuild]::NormalizePath('$(LibrariesProjectRoot)', 'Microsoft.NETCore.Platforms', 'src', 'runtime.json')) $(MSBuildThisFileDirectory)LICENSE.TXT MIT $(CopyrightNetFoundation) From 4682bceb870f79b94f95f80b1350613e1d55d232 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Wed, 31 Mar 2021 12:17:40 -0700 Subject: [PATCH 6/9] Don't filter inner builds when building Platforms task --- src/libraries/Directory.Build.props | 2 +- src/libraries/Directory.Build.targets | 2 +- .../Microsoft.NETCore.Platforms/src/Directory.Build.props | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 src/libraries/Microsoft.NETCore.Platforms/src/Directory.Build.props diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index 7803ca3e6f1c5b..f36f83de45246b 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -21,7 +21,7 @@ $(RepositoryEngineeringDir)LicenseHeader.txt - + $([System.Text.RegularExpressions.Regex]::Replace('$(TargetFramework)', '(-[^;]+)', '')) diff --git a/src/libraries/Directory.Build.targets b/src/libraries/Directory.Build.targets index 96fc0a734f8fa3..a04a93dfb0d6be 100644 --- a/src/libraries/Directory.Build.targets +++ b/src/libraries/Directory.Build.targets @@ -116,7 +116,7 @@ - + diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/Directory.Build.props b/src/libraries/Microsoft.NETCore.Platforms/src/Directory.Build.props new file mode 100644 index 00000000000000..ea706165b9f58f --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/src/Directory.Build.props @@ -0,0 +1,6 @@ + + + false + + + \ No newline at end of file From 0b8d0e05746fbb91aec9b2c03c4b42d18a7597a0 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Wed, 31 Mar 2021 12:20:51 -0700 Subject: [PATCH 7/9] Skip tests RuntimeGraph task tests on some platforms --- .../tests/Microsoft.NETCore.Platforms.Tests.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj b/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj index d3757f225cc3b8..dfa3f25c893b9f 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj +++ b/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj @@ -1,6 +1,8 @@  $(NetCoreAppCurrent);net472 + + true From 7a6ccaf0d7b16f96c8f6fce0425a5ca495c5f0f5 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Fri, 2 Apr 2021 06:16:12 -0700 Subject: [PATCH 8/9] Exclude browser from testing instead of wasm --- .../tests/Microsoft.NETCore.Platforms.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj b/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj index dfa3f25c893b9f..b22e6ac4485e77 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj +++ b/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj @@ -2,7 +2,7 @@ $(NetCoreAppCurrent);net472 - true + true From 5aec6a7cfa8a7a8dd546a163af3ac31f91fed3b9 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Fri, 2 Apr 2021 10:33:16 -0700 Subject: [PATCH 9/9] Another try at suppressing testing on Browser --- .../Microsoft.NETCore.Platforms/tests/AssemblyInfo.cs | 6 ++++++ .../tests/Microsoft.NETCore.Platforms.Tests.csproj | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 src/libraries/Microsoft.NETCore.Platforms/tests/AssemblyInfo.cs diff --git a/src/libraries/Microsoft.NETCore.Platforms/tests/AssemblyInfo.cs b/src/libraries/Microsoft.NETCore.Platforms/tests/AssemblyInfo.cs new file mode 100644 index 00000000000000..baad52408c410d --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/tests/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +[assembly: SkipOnMono("MSBuild is not supported on Browser", TestPlatforms.Browser)] \ No newline at end of file diff --git a/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj b/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj index b22e6ac4485e77..cb254a5cb5070b 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj +++ b/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj @@ -1,8 +1,7 @@  $(NetCoreAppCurrent);net472 - - true + true @@ -12,6 +11,7 @@ +