public void ItCanOverrideDefaultValues() { WorkloadManifest manifest = Create("WorkloadManifest.json"); WorkloadDefinition definition = manifest.Workloads.FirstOrDefault().Value; ITaskItem[] resources = new ITaskItem[] { new TaskItem("microsoft-net-sdk-blazorwebassembly-aot", new Dictionary <string, string> { { "Title", "AOT" }, { "Description", "A long wordy description." }, { "Category", "Compilers, build tools, and runtimes" } }) }; VisualStudioComponent component = VisualStudioComponent.Create(null, manifest, definition, "1.2.3.4", NoItems, resources, NoItems); string swixProjDirectory = RandomPath; Directory.CreateDirectory(swixProjDirectory); component.Generate(swixProjDirectory); string componentResSwr = File.ReadAllText(Path.Combine(swixProjDirectory, "component.res.swr")); Assert.Contains(@"title=""AOT""", componentResSwr); Assert.Contains(@"description=""A long wordy description.""", componentResSwr); Assert.Contains(@"category=""Compilers, build tools, and runtimes""", componentResSwr); }
/// <summary> /// Extracts the workload manifest from the manifest package and generate a SWIX project for a Visual Studio component /// matching the manifests dependencies. /// </summary> /// <param name="workloadManifestPackage">The path of the workload package containing the manifest.</param> /// <returns>A set of items containing the generated SWIX projects.</returns> internal IEnumerable <ITaskItem> ProcessWorkloadManifest(string workloadManifestPackage) { NugetPackage workloadPackage = new(workloadManifestPackage, Log); string packageContentPath = Path.Combine(PackageDirectory, $"{workloadPackage.Identity}"); workloadPackage.Extract(packageContentPath, Enumerable.Empty <string>()); string workloadManifestJsonPath = Directory.GetFiles(packageContentPath, "WorkloadManifest.json").FirstOrDefault(); if (string.IsNullOrWhiteSpace(workloadManifestJsonPath)) { throw new FileNotFoundException($"Unable to locate WorkloadManifest.json under '{packageContentPath}'."); } WorkloadManifest manifest = WorkloadManifestReader.ReadWorkloadManifest(File.OpenRead(workloadManifestJsonPath)); List <TaskItem> swixProjects = new(); foreach (WorkloadDefinition workloadDefinition in manifest.Workloads.Values) { VisualStudioComponent component = VisualStudioComponent.Create(manifest, workloadDefinition); swixProjects.Add(component.Generate(Path.Combine(SourceDirectory, $"{workloadDefinition.Id}.{manifest.Version}.0"))); } return(swixProjects); }
internal IEnumerable <ITaskItem> ProcessWorkloadManifestFile(string workloadManifestJsonPath) { WorkloadManifest manifest = WorkloadManifestReader.ReadWorkloadManifest(Path.GetFileNameWithoutExtension(workloadManifestJsonPath), File.OpenRead(workloadManifestJsonPath)); List <TaskItem> swixProjects = new(); foreach (WorkloadDefinition workloadDefinition in manifest.Workloads.Values) { 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)})."); continue; } // Each workload maps to a Visual Studio component. VisualStudioComponent component = VisualStudioComponent.Create(Log, manifest, workloadDefinition, ComponentVersions, ShortNames, ComponentResources, MissingPacks); // If there are no dependencies, regardless of whether we are generating MSIs, we'll report an // error as we'd produce invalid SWIX. if (!component.HasDependencies) { Log?.LogError($"Visual Studio components '{component.Name}' must have at least one dependency."); } string vsPayloadRelativePath = $"{component.Name},version={component.Version}\\_package.json"; CheckRelativePayloadPath(vsPayloadRelativePath); swixProjects.Add(component.Generate(Path.Combine(SourceDirectory, $"{workloadDefinition.Id}.{manifest.Version}.0"))); } return(swixProjects); }
public IReadOnlyCollection <IOptionalSdkTemplatePackageInfo> GetDotnetSdkTemplatePackages( string sdkVersion, string dotnetRootPath) { if (string.IsNullOrWhiteSpace(sdkVersion)) { throw new ArgumentException($"'{nameof(sdkVersion)}' cannot be null or whitespace", nameof(sdkVersion)); } if (string.IsNullOrWhiteSpace(dotnetRootPath)) { throw new ArgumentException($"'{nameof(dotnetRootPath)}' cannot be null or whitespace", nameof(dotnetRootPath)); } if (!Version.TryParse(sdkVersion.Split('-')[0], out var sdkVersionParsed)) { throw new ArgumentException($"'{nameof(sdkVersion)}' should be a version, but get {sdkVersion}"); } bool FindSameVersionBand(DirectoryInfo manifestDirectory) { var directoryVersion = Version.Parse(manifestDirectory.Name); return(directoryVersion.Major == sdkVersionParsed.Major && directoryVersion.Minor == sdkVersionParsed.Minor && (directoryVersion.Revision / 100) == (sdkVersionParsed.Revision / 100)); } var bondManifestDirectory = new DirectoryInfo(Path.Combine(dotnetRootPath, "workloadmanifests")).GetDirectories().Where(FindSameVersionBand) .OrderByDescending(d => Version.Parse(d.Name)).FirstOrDefault(); if (bondManifestDirectory == null) { return(Array.Empty <IOptionalSdkTemplatePackageInfo>()); } var manifestContent = WorkloadManifest.LoadFromFolder(bondManifestDirectory.FullName); var dotnetSdkTemplatePackages = new List <IOptionalSdkTemplatePackageInfo>(); foreach (var pack in manifestContent.SdkPackDetail) { if (pack.Value.kind.Equals("Template", StringComparison.OrdinalIgnoreCase)) { var optionalSdkTemplatePackageInfo = new OptionalSdkTemplatePackageInfo( pack.Key, pack.Value.version, Path.Combine(dotnetRootPath, "template-packs", pack.Key.ToLower() + "." + pack.Value.version.ToLower() + ".nupkg")); dotnetSdkTemplatePackages.Add(optionalSdkTemplatePackageInfo); } } return(dotnetSdkTemplatePackages); }
/// <summary> /// Creates a <see cref="VisualStudioComponent"/> using a workload definition. /// </summary> /// <param name="manifest"></param> /// <param name="workload"></param> /// <param name="componentVersions"></param> /// <param name="shortNames"></param> /// <param name="shortNameMetadata"></param> /// <param name="componentResources"></param> /// <returns></returns> public static VisualStudioComponent Create(TaskLoggingHelper log, WorkloadManifest manifest, WorkloadDefinition workload, ITaskItem[] componentVersions, ITaskItem[] shortNames, ITaskItem[] componentResources, ITaskItem[] missingPacks) { log?.LogMessage("Creating Visual Studio component"); string workloadId = $"{workload.Id}"; // If there's an explicit version mapping we use that, otherwise we fall back to the manifest version // and normalize it since it can have semantic information and Visual Studio components do not support that. ITaskItem versionItem = componentVersions?.Where(v => string.Equals(v.ItemSpec, workloadId, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); Version version = (versionItem != null) && !string.IsNullOrWhiteSpace(versionItem.GetMetadata(Metadata.Version)) ? new Version(versionItem.GetMetadata(Metadata.Version)) : (new NuGetVersion(manifest.Version)).Version; ITaskItem resourceItem = componentResources?.Where( r => string.Equals(r.ItemSpec, workloadId, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); // Workload definitions do not have separate title/description fields so the only option // is to default to the workload description for both. 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, isUiGroup, shortNames, category); IEnumerable <string> 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 <WorkloadPackId> packIds = workload.Packs.Where(p => !missingPackIds.Contains($"{p}", StringComparer.OrdinalIgnoreCase)); log?.LogMessage(MessageImportance.Low, $"Packs: {string.Join(", ", packIds.Select(p=>$"{p}"))}"); foreach (WorkloadPackId packId in packIds) { log?.LogMessage(MessageImportance.Low, $"Adding component dependency for {packId} "); component.AddDependency(manifest.Packs[packId]); } return(component); }
/// <summary> /// Creates a <see cref="VisualStudioComponent"/> using a workload definition. /// </summary> /// <param name="Definition"></param> /// <param name="packs"></param> /// <returns></returns> public static VisualStudioComponent Create(WorkloadManifest manifest, WorkloadDefinition definition) { VisualStudioComponent package = new(Utils.ToSafeId(definition.Id.ToString()), definition.Description, definition.Description, new Version($"{manifest.Version}.0")); foreach (WorkloadPackId packId in definition.Packs) { package.AddDependency(manifest.Packs[packId]); } return(package); }
public void ItCreatesSafeComponentIds() { WorkloadManifest manifest = Create("WorkloadManifest.json"); WorkloadDefinition definition = manifest.Workloads.FirstOrDefault().Value; VisualStudioComponent component = VisualStudioComponent.Create(null, manifest, definition, "1.2.3.4", NoItems, NoItems, NoItems); string swixProjDirectory = RandomPath; Directory.CreateDirectory(swixProjDirectory); component.Generate(swixProjDirectory); string componentSwr = File.ReadAllText(Path.Combine(swixProjDirectory, "component.swr")); Assert.Contains(@"microsoft.net.sdk.blazorwebassembly.aot", componentSwr); }
public void ItCreatesComponentsWhenWorkloadsDoNotIncludePacks() { WorkloadManifest manifest = Create("mauiWorkloadManifest.json"); WorkloadDefinition definition = (WorkloadDefinition)manifest.Workloads.FirstOrDefault().Value; VisualStudioComponent component = VisualStudioComponent.Create(null, manifest, definition, NoItems, NoItems, NoItems, NoItems); string swixProjDirectory = RandomPath; Directory.CreateDirectory(swixProjDirectory); component.Generate(swixProjDirectory); string componentSwr = File.ReadAllText(Path.Combine(swixProjDirectory, "component.swr")); Assert.Contains(@"vs.dependency id=maui.mobile", componentSwr); Assert.Contains(@"vs.dependency id=maui.desktop", componentSwr); }
public void ItAssignsDefaultValues() { WorkloadManifest manifest = Create("WorkloadManifest.json"); WorkloadDefinition definition = manifest.Workloads.FirstOrDefault().Value; VisualStudioComponent component = VisualStudioComponent.Create(null, manifest, definition, "1.2.3.4", NoItems, NoItems, NoItems); string swixProjDirectory = RandomPath; Directory.CreateDirectory(swixProjDirectory); component.Generate(swixProjDirectory); string componentResSwr = File.ReadAllText(Path.Combine(swixProjDirectory, "component.res.swr")); Assert.Contains(@"title=""Blazor WebAssembly AOT workload""", componentResSwr); Assert.Contains(@"description=""Blazor WebAssembly AOT workload""", componentResSwr); Assert.Contains(@"category="".NET""", componentResSwr); }
internal IEnumerable <ITaskItem> ProcessWorkloadManifestFile(string workloadManifestJsonPath) { WorkloadManifest manifest = WorkloadManifestReader.ReadWorkloadManifest(Path.GetFileNameWithoutExtension(workloadManifestJsonPath), File.OpenRead(workloadManifestJsonPath)); List <TaskItem> swixProjects = new(); foreach (WorkloadDefinition workloadDefinition in manifest.Workloads.Values) { // Each workload maps to a Visual Studio component. VisualStudioComponent component = VisualStudioComponent.Create(Log, manifest, workloadDefinition, ComponentVersion, ShortNames, ComponentResources, MissingPacks); // If there are no dependencies, regardless of whether we are generating MSIs, we'll report an // error as we'd produce invalid SWIX. if (!component.HasDependencies) { Log?.LogError($"Visual Studio components '{component.Name}' must have at least one dependency."); } swixProjects.Add(component.Generate(Path.Combine(SourceDirectory, $"{workloadDefinition.Id}.{manifest.Version}.0"))); } return(swixProjects); }
/// <summary> /// Creates a <see cref="VisualStudioComponent"/> using a workload definition. /// </summary> /// <param name="manifest"></param> /// <param name="workload"></param> /// <param name="componentVersion"></param> /// <param name="shortNames"></param> /// <param name="shortNameMetadata"></param> /// <param name="componentResources"></param> /// <returns></returns> public static VisualStudioComponent Create(TaskLoggingHelper log, WorkloadManifest manifest, WorkloadDefinition workload, string componentVersion, ITaskItem[] shortNames, ITaskItem[] componentResources, ITaskItem[] missingPacks) { log?.LogMessage("Creating Visual Studio component"); Version version = string.IsNullOrWhiteSpace(componentVersion) ? new Version($"{manifest.Version}.0") : new Version(componentVersion); ITaskItem resourceItem = componentResources?.Where( r => string.Equals(r.ItemSpec, workload.Id.ToString(), StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); // Workload definitions do not have separate title/description fields so the only option // is to default to the workload description for both. string title = resourceItem?.GetMetadata(Metadata.Title) ?? workload.Description; string description = resourceItem?.GetMetadata(Metadata.Description) ?? workload.Description; string category = resourceItem?.GetMetadata(Metadata.Category) ?? ".NET"; VisualStudioComponent component = new(Utils.ToSafeId(workload.Id.ToString()), description, title, version, shortNames, category); IEnumerable <string> missingPackIds = missingPacks.Select(p => p.ItemSpec); log?.LogMessage(MessageImportance.Low, $"Missing packs: {string.Join(", ", missingPackIds)}"); // Visual Studio is case-insensitive. IEnumerable <WorkloadPackId> packIds = workload.Packs.Where(p => !missingPackIds.Contains($"{p}", StringComparer.OrdinalIgnoreCase)); log?.LogMessage(MessageImportance.Low, $"Packs: {string.Join(", ", packIds.Select(p=>$"{p}"))}"); foreach (WorkloadPackId packId in packIds) { log?.LogMessage(MessageImportance.Low, $"Adding component dependency for {packId} "); component.AddDependency(manifest.Packs[packId]); } return(component); }
public override bool Execute() { try { Directory.CreateDirectory(Path.GetDirectoryName(ProjectFile)); List <string> packs = new(); var excludedPackIds = ExcludedPackIds.Select(i => i.ItemSpec); XmlWriterSettings settings = new XmlWriterSettings { Indent = true, IndentChars = " " }; XmlWriter writer = XmlWriter.Create(ProjectFile, settings); writer.WriteStartElement("Project"); writer.WriteAttributeString("Sdk", "Microsoft.NET.Sdk"); writer.WriteStartElement("PropertyGroup"); writer.WriteElementString("TargetFramework", "net6.0"); writer.WriteElementString("IncludeBuildOutput", "false"); writer.WriteEndElement(); writer.WriteStartElement("ItemGroup"); foreach (var manifestFile in ManifestFiles) { WorkloadManifest manifest = WorkloadManifestReader.ReadWorkloadManifest( Path.GetFileNameWithoutExtension(manifestFile.ItemSpec), File.OpenRead(manifestFile.ItemSpec), manifestFile.ItemSpec); foreach (var pack in manifest.Packs.Values) { if (pack.IsAlias) { foreach (var alias in pack.AliasTo.Keys.Where(k => k.StartsWith("win"))) { if (!excludedPackIds.Contains($"{pack.AliasTo[alias]}")) { WriteItem(writer, "PackageDownload", ("Include", $"{pack.AliasTo[alias]}"), ("Version", $"[{pack.Version}]")); packs.Add($"$(NuGetPackageRoot){pack.AliasTo[alias]}\\{pack.Version}\\*.nupkg"); } } } else if (!excludedPackIds.Contains($"{pack.Id}")) { WriteItem(writer, "PackageDownload", ("Include", $"{pack.Id}"), ("Version", $"[{pack.Version}]")); packs.Add($"$(NuGetPackageRoot){pack.Id}\\{pack.Version}\\*.nupkg"); } } } writer.WriteEndElement(); writer.WriteStartElement("Target"); writer.WriteAttributeString("Name", "CopyPacks"); writer.WriteAttributeString("AfterTargets", "Build"); writer.WriteStartElement("ItemGroup"); foreach (var pack in packs) { WriteItem(writer, "Pack", ("Include", pack)); } writer.WriteEndElement(); writer.WriteStartElement("Copy"); writer.WriteAttributeString("SourceFiles", "@(Pack)"); writer.WriteAttributeString("DestinationFolder", $"{PackageSource}"); writer.WriteEndElement(); writer.WriteEndElement(); writer.WriteEndElement(); writer.Flush(); writer.Close(); } catch (Exception e) { Log.LogErrorFromException(e); } return(!Log.HasLoggedErrors); }