From 743296de7f2b42102615854651a85f22b8426bb1 Mon Sep 17 00:00:00 2001 From: Jacques Eloff Date: Mon, 14 Jun 2021 20:38:52 -0700 Subject: [PATCH] Workloads: Tooling changes to MSI and SWIX generation --- .../GenerateVisualStudioWorkloadTests.cs | 13 +++-- .../VisualStudioDependencyTests.cs | 29 ++++++++++++ .../src/GenerateMsiBase.cs | 3 +- .../src/GenerateVisualStudioWorkload.cs | 8 ---- .../src/MsiProperties.cs | 6 +++ .../src/SwixTemplate/component.swixproj | 5 +- .../src/SwixTemplate/component.swr | 2 +- .../src/VisualStudioComponent.cs | 47 ++++++++++++++++--- .../src/VisualStudioDependency.cs | 47 ++++++++++++++++--- 9 files changed, 128 insertions(+), 32 deletions(-) create mode 100644 src/Microsoft.DotNet.Build.Tasks.Workloads.Tests/VisualStudioDependencyTests.cs diff --git a/src/Microsoft.DotNet.Build.Tasks.Workloads.Tests/GenerateVisualStudioWorkloadTests.cs b/src/Microsoft.DotNet.Build.Tasks.Workloads.Tests/GenerateVisualStudioWorkloadTests.cs index 9d84c83e341..da88129a328 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Workloads.Tests/GenerateVisualStudioWorkloadTests.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Workloads.Tests/GenerateVisualStudioWorkloadTests.cs @@ -117,7 +117,7 @@ public void ItGeneratesASwixProjectFromAWorkloadManifestPackage() } [Fact] - public void ItSkipsAbstractManifests() + public void ItIncludesAbstractManifests() { var buildTask = new GenerateVisualStudioWorkload() { @@ -132,11 +132,14 @@ public void ItSkipsAbstractManifests() }; Assert.True(buildTask.Execute()); - string outputPath = Path.GetDirectoryName(buildTask.SwixProjects[0].GetMetadata("FullPath")); - string componentSwr = File.ReadAllText(Path.Combine(outputPath, "component.swr")); - Assert.Single(buildTask.SwixProjects); + string blazorOutputPath = Path.GetDirectoryName(buildTask.SwixProjects[0].GetMetadata("FullPath")); + string blazorComponentSwr = File.ReadAllText(Path.Combine(blazorOutputPath, "component.swr")); Assert.Contains(@"package name=microsoft.net.sdk.blazorwebassembly.aot - version=6.0.0.0", componentSwr); + version=6.0.0.0", blazorComponentSwr); + string androidOutputPath = Path.GetDirectoryName(buildTask.SwixProjects[1].GetMetadata("FullPath")); + string androidComponentSwr = File.ReadAllText(Path.Combine(androidOutputPath, "component.swr")); + Assert.Contains(@"package name=microsoft.net.runtime.android + version=6.0.0.0", androidComponentSwr); } [Fact] diff --git a/src/Microsoft.DotNet.Build.Tasks.Workloads.Tests/VisualStudioDependencyTests.cs b/src/Microsoft.DotNet.Build.Tasks.Workloads.Tests/VisualStudioDependencyTests.cs new file mode 100644 index 00000000000..be810f26e5b --- /dev/null +++ b/src/Microsoft.DotNet.Build.Tasks.Workloads.Tests/VisualStudioDependencyTests.cs @@ -0,0 +1,29 @@ +// 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.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.DotNet.Build.Tasks.Workloads.Tests +{ + public class VisualStudioDependencyTests + { + [Theory] + [InlineData("1.0.0", null, "[1.0.0,)")] + [InlineData("1.0.0", "2.0.0", "[1.0.0,2.0.0)")] + [InlineData("1.0.0", "1.0.0", "[1.0.0]")] + public void ItGeneratesVersionRanges(string minVersion, string maxVersion, string expectedVersionRange) + { + Version v1 = string.IsNullOrWhiteSpace(minVersion) ? null : new Version(minVersion); + Version v2 = string.IsNullOrWhiteSpace(maxVersion) ? null : new Version(maxVersion); + + VisualStudioDependency dep = new VisualStudioDependency("foo", v1, v2); + + Assert.Equal(expectedVersionRange, dep.GetVersion()); + } + } +} diff --git a/src/Microsoft.DotNet.Build.Tasks.Workloads/src/GenerateMsiBase.cs b/src/Microsoft.DotNet.Build.Tasks.Workloads/src/GenerateMsiBase.cs index 4a8db6efd96..947f863a8ea 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Workloads/src/GenerateMsiBase.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Workloads/src/GenerateMsiBase.cs @@ -236,6 +236,7 @@ protected IEnumerable Generate(string sourcePackage, string swixPacka MsiProperties msiProps = new MsiProperties { InstallSize = MsiUtils.GetInstallSize(msiPath), + Payload = Path.GetFileName(msiPath), ProductCode = MsiUtils.GetProperty(msiPath, "ProductCode"), ProductVersion = MsiUtils.GetProperty(msiPath, "ProductVersion"), ProviderKeyName = $"{nupkg.Id},{nupkg.Version},{platform}", @@ -330,7 +331,7 @@ private string GeneratePackageProject(string msiPath, string msiJsonPath, string writer.WriteStartElement("ItemGroup"); WriteItem(writer, "None", msiPath, @"\data"); - WriteItem(writer, "None", msiJsonPath, @"\data"); + WriteItem(writer, "None", msiJsonPath, @"\data\msi.json"); WriteItem(writer, "None", licenseTextPath, @"\"); writer.WriteEndElement(); diff --git a/src/Microsoft.DotNet.Build.Tasks.Workloads/src/GenerateVisualStudioWorkload.cs b/src/Microsoft.DotNet.Build.Tasks.Workloads/src/GenerateVisualStudioWorkload.cs index 92f9a43eb5f..a3eb28d860c 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Workloads/src/GenerateVisualStudioWorkload.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Workloads/src/GenerateVisualStudioWorkload.cs @@ -222,14 +222,6 @@ internal IEnumerable ProcessWorkloadManifestFile(string workloadManif foreach (WorkloadDefinition workloadDefinition in manifest.Workloads.Values) { - // Abstract workloads can only be extended, so we can't generate items for this yet. Might need to do a second pass - // if there are other manifests that extend the workload. - if (workloadDefinition.IsAbstract) - { - Log?.LogMessage(MessageImportance.High, $"{workloadDefinition.Id} is abstract and will be skipped."); - continue; - } - if ((workloadDefinition.Platforms?.Count > 0) && (!workloadDefinition.Platforms.Any(p => p.StartsWith("win")))) { Log?.LogMessage(MessageImportance.High, $"{workloadDefinition.Id} platforms does not support Windows and will be skipped ({string.Join(", ", workloadDefinition.Platforms)})."); diff --git a/src/Microsoft.DotNet.Build.Tasks.Workloads/src/MsiProperties.cs b/src/Microsoft.DotNet.Build.Tasks.Workloads/src/MsiProperties.cs index 2bacc28619a..b3f4ab15d23 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Workloads/src/MsiProperties.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Workloads/src/MsiProperties.cs @@ -17,6 +17,12 @@ public long InstallSize set; } + public string Payload + { + get; + set; + } + public string ProductCode { get; diff --git a/src/Microsoft.DotNet.Build.Tasks.Workloads/src/SwixTemplate/component.swixproj b/src/Microsoft.DotNet.Build.Tasks.Workloads/src/SwixTemplate/component.swixproj index df5c7962909..1408da52e9b 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Workloads/src/SwixTemplate/component.swixproj +++ b/src/Microsoft.DotNet.Build.Tasks.Workloads/src/SwixTemplate/component.swixproj @@ -5,13 +5,10 @@ false manifest $(ManifestOutputPath) - - - no - $(PackagePreprocessorDefinitions);IsUIGroup=$(IsUIGroup) + $(PackagePreprocessorDefinitions) diff --git a/src/Microsoft.DotNet.Build.Tasks.Workloads/src/SwixTemplate/component.swr b/src/Microsoft.DotNet.Build.Tasks.Workloads/src/SwixTemplate/component.swr index 3ae9a504aa0..771ec0b7c68 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Workloads/src/SwixTemplate/component.swr +++ b/src/Microsoft.DotNet.Build.Tasks.Workloads/src/SwixTemplate/component.swr @@ -5,6 +5,6 @@ package name=__VS_PACKAGE_NAME__ vs.package.type=component vs.properties - isUiGroup=$(IsUIGroup) + isUiGroup=__VS_IS_UI_GROUP__ vs.dependencies diff --git a/src/Microsoft.DotNet.Build.Tasks.Workloads/src/VisualStudioComponent.cs b/src/Microsoft.DotNet.Build.Tasks.Workloads/src/VisualStudioComponent.cs index 1728ac61216..ee7e9d6d39c 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Workloads/src/VisualStudioComponent.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Workloads/src/VisualStudioComponent.cs @@ -39,6 +39,15 @@ public string Description /// public bool HasDependencies => Dependencies.Count > 0; + /// + /// When "no", the component is visible in both the workloads and individual components tab. + /// When "yes", the component is only visible in the workloads tab. + /// + public string IsUiGroup + { + get; + } = "no"; + /// /// The component name (ID). /// @@ -74,7 +83,7 @@ public Version Version private ICollection Dependencies = new List(); - public VisualStudioComponent(string name, string description, string title, Version version, ITaskItem[] shortNames, + public VisualStudioComponent(string name, string description, string title, Version version, string isUiGroup, ITaskItem[] shortNames, string category) { Name = name; @@ -83,6 +92,17 @@ public VisualStudioComponent(string name, string description, string title, Vers Version = version; ShortNames = shortNames; Category = category; + IsUiGroup = isUiGroup; + } + + /// + /// Add a component dependency using the provided name and version. + /// + /// The name (ID) of the dependency. + /// The version of the dependency. + public void AddDependency(string name, Version exactVersion) + { + AddDependency(new VisualStudioDependency(name, exactVersion, exactVersion)); } /// @@ -90,9 +110,9 @@ public VisualStudioComponent(string name, string description, string title, Vers /// /// The name (ID) of the dependency. /// The version of the dependency. - public void AddDependency(string name, Version version) + public void AddDependency(string name, Version minVersion, Version maxVersion) { - AddDependency(new VisualStudioDependency(name, version)); + AddDependency(new VisualStudioDependency(name, minVersion, maxVersion)); } /// @@ -119,7 +139,7 @@ public void AddDependency(ITaskItem dependency) /// The dependency to add to this component. public void AddDependency(WorkloadPack pack) { - AddDependency($"{pack.Id.ToString().Replace(ShortNames)}.{pack.Version}", new NuGetVersion(pack.Version).Version); + AddDependency($"{pack.Id.ToString().Replace(ShortNames)}.{pack.Version}", new NuGetVersion(pack.Version).Version, maxVersion: null); } public IEnumerable GetAliasedDependencies(WorkloadPack pack) @@ -164,7 +184,7 @@ public TaskItem Generate(string projectPath) // version=[1.2.3.4] swrWriter.WriteLine($" vs.dependency id={dependency.Id}"); - swrWriter.WriteLine($" version=[{dependency.Version}]"); + swrWriter.WriteLine($" version={dependency.GetVersion()}"); swrWriter.WriteLine($" behaviors=IgnoreApplicabilityFailures"); } @@ -179,7 +199,8 @@ private Dictionary GetReplacementTokens() {"__VS_PACKAGE_VERSION__", Version.ToString() }, {"__VS_COMPONENT_TITLE__", Title }, {"__VS_COMPONENT_DESCRIPTION__", Description }, - {"__VS_COMPONENT_CATEGORY__", Category ?? ".NET" } + {"__VS_COMPONENT_CATEGORY__", Category ?? ".NET" }, + {"__VS_IS_UI_GROUP__", IsUiGroup ?? "no" } }; } @@ -214,13 +235,25 @@ public static VisualStudioComponent Create(TaskLoggingHelper log, WorkloadManife string title = resourceItem?.GetMetadata(Metadata.Title) ?? workload.Description; string description = resourceItem?.GetMetadata(Metadata.Description) ?? workload.Description; string category = resourceItem?.GetMetadata(Metadata.Category) ?? ".NET"; + string isUiGroup = workload.IsAbstract ? "yes" : "no"; VisualStudioComponent component = new(Utils.ToSafeId(workloadId), description, - title, version, shortNames, category); + title, version, isUiGroup, shortNames, category); IEnumerable missingPackIds = missingPacks.Select(p => p.ItemSpec); log?.LogMessage(MessageImportance.Low, $"Missing packs: {string.Join(", ", missingPackIds)}"); + // If the work extends other workloads, we add those as component dependencies before + // processing direct pack dependencies + if (workload.Extends?.Count() > 0) + { + foreach (WorkloadDefinitionId dependency in workload.Extends) + { + // Component dependencies, aka. workload extensions only have minimum version dependencies. + component.AddDependency($"{Utils.ToSafeId(dependency.ToString())}", new Version("1.0.0.0"), maxVersion: null); + } + } + // Visual Studio is case-insensitive. IEnumerable packIds = workload.Packs.Where(p => !missingPackIds.Contains($"{p}", StringComparer.OrdinalIgnoreCase)); log?.LogMessage(MessageImportance.Low, $"Packs: {string.Join(", ", packIds.Select(p=>$"{p}"))}"); diff --git a/src/Microsoft.DotNet.Build.Tasks.Workloads/src/VisualStudioDependency.cs b/src/Microsoft.DotNet.Build.Tasks.Workloads/src/VisualStudioDependency.cs index db80a3d70fe..0f26d13e0c0 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Workloads/src/VisualStudioDependency.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Workloads/src/VisualStudioDependency.cs @@ -18,18 +18,53 @@ public string Id get; } - /// - /// The version of the dependent. - /// - public Version Version + public Version MinVersion { get; } - public VisualStudioDependency(string id, Version version) + public Version MaxVersion + { + get; + } + + /// + /// Creates a dependency with an exact version. + /// + /// + /// + public VisualStudioDependency(string id, Version version) : this(id, version, version) + { + + } + + /// + /// Creates a dependency with a minimum and maximum versions. + /// + /// The Visual Studio package ID. The ID applies to packages, components, component groups, etc. + /// The minimum required version, inclusive. + /// The maximum version, exclusive. May be if there is only a minimum requirement. If + /// equal to , an exact version requirement is created, e.g. [1.2.0]. + public VisualStudioDependency(string id, Version minVersion, Version maxVersion) { Id = id; - Version = version; + MinVersion = minVersion; + MaxVersion = maxVersion; + } + + public string GetVersion() + { + if ((MaxVersion != null) && (MinVersion == MaxVersion)) + { + return $"[{MinVersion}]"; + } + + if (MaxVersion == null) + { + return $"[{MinVersion},)"; + } + + return $"[{MinVersion},{MaxVersion})"; } } }