main
cs 177 lines 6.44 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.Tasks
4 {
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Linq;
9 using System.Security.Cryptography;
10 using System.Text.Json;
11 using System.Text.Json.Serialization;
12 using Microsoft.Build.Framework;
13 using Microsoft.Build.Utilities;
14 using WixToolset.Data;
15 using WixToolset.Data.Symbols;
16
17 public class GenerateMetadata : Task
18 {
19 private static readonly JsonSerializerOptions SerializerOptions = new JsonSerializerOptions
20 {
21 PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
22 DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
23 WriteIndented = true,
24 Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) },
25 };
26
27 [Required]
28 public string TargetFile { get; set; }
29
30 [Required]
31 public string WixpdbFile { get; set; }
32
33 public override bool Execute()
34 {
35 var intermediate = Intermediate.Load(this.WixpdbFile);
36
37 var section = intermediate.Sections.Single();
38
39 Metadata metadata;
40 SourceLineNumber sourceLineNumber;
41
42 if (section.Type == SectionType.Bundle)
43 {
44 (metadata, sourceLineNumber) = this.GetBundleMetadata(section, "WixToolset.AdditionalTools");
45 }
46 else if (section.Type == SectionType.Package)
47 {
48 (metadata, sourceLineNumber) = this.GetPackageMetadata(section, "WixToolset.CommandLineTools");
49 }
50 else
51 {
52 return false;
53 }
54
55 if (metadata != null)
56 {
57 this.PopulateFileInfo(metadata);
58
59 this.SaveMetadata(metadata, sourceLineNumber);
60 }
61
62 return true;
63 }
64
65 private (Metadata, SourceLineNumber) GetBundleMetadata(IntermediateSection section, string defaultId)
66 {
67 var bundleSymbol = section.Symbols.OfType<WixBundleSymbol>().Single();
68
69 var metadata = new Metadata
70 {
71 Id = bundleSymbol.Id?.Id ?? defaultId,
72 Type = MetadataType.Burn,
73 Name = bundleSymbol.Name,
74 Version = bundleSymbol.Version,
75 Publisher = bundleSymbol.Manufacturer,
76 Description = "Installation for " + bundleSymbol.Name,
77 License = "MS-RL",
78 SupportUrl = bundleSymbol.HelpUrl,
79 BundleCode = bundleSymbol.BundleId,
80 UpgradeCode = bundleSymbol.UpgradeCode,
81 AboutUrl = bundleSymbol.AboutUrl,
82 Architecture = PlatformToArchitecture(bundleSymbol.Platform),
83 };
84
85 return (metadata, bundleSymbol.SourceLineNumbers);
86 }
87
88 private (Metadata, SourceLineNumber) GetPackageMetadata(IntermediateSection section, string defaultId)
89 {
90 var packageSymbol = section.Symbols.OfType<WixPackageSymbol>().Single();
91 var propertySymbols = section.Symbols.OfType<PropertySymbol>().ToDictionary(p => p.Id.Id);
92 var platform = GetPlatformFromSummaryInformation(section.Symbols.OfType<SummaryInformationSymbol>());
93
94 var metadata = new Metadata
95 {
96 Id = packageSymbol.Id?.Id ?? defaultId,
97 Type = MetadataType.Msi,
98 Name = packageSymbol.Name,
99 Version = packageSymbol.Version,
100 Publisher = packageSymbol.Manufacturer,
101 Description = "Installation for " + packageSymbol.Name,
102 License = "MS-RL",
103 SupportUrl = propertySymbols["ARPHELPLINK"].Value,
104 ProductCode = propertySymbols["ProductCode"].Value,
105 UpgradeCode = propertySymbols["UpgradeCode"].Value,
106 AboutUrl = propertySymbols["ARPURLINFOABOUT"].Value,
107 Architecture = PlatformToArchitecture(platform),
108 };
109
110 return (metadata, packageSymbol.SourceLineNumbers);
111 }
112
113 private void PopulateFileInfo(Metadata metadata)
114 {
115 var fi = new FileInfo(this.TargetFile);
116
117 using (var sha256 = SHA256.Create())
118 using (var stream = fi.OpenRead())
119 {
120 var hash = sha256.ComputeHash(stream);
121
122 metadata.File = fi.Name;
123 metadata.Created = fi.CreationTimeUtc.ToString("O");
124 metadata.Size = fi.Length;
125 metadata.Sha256 = BitConverter.ToString(hash).Replace("-", String.Empty);
126 }
127 }
128
129 private void SaveMetadata(Metadata metadata, SourceLineNumber sourceLineNumber)
130 {
131 var metadataFilePath = Path.ChangeExtension(this.TargetFile, "metadata.json");
132
133 var json = JsonSerializer.Serialize(metadata, SerializerOptions);
134
135 File.WriteAllText(metadataFilePath, json);
136 }
137
138 private static Platform GetPlatformFromSummaryInformation(IEnumerable<SummaryInformationSymbol> symbols)
139 {
140 foreach (var symbol in symbols)
141 {
142 if (symbol.PropertyId == SummaryInformationType.PlatformAndLanguage)
143 {
144 var value = symbol.Value;
145 var separatorIndex = value.IndexOf(';');
146 var platformValue = separatorIndex > 0 ? value.Substring(0, separatorIndex) : value;
147
148 switch (platformValue)
149 {
150 case "x64":
151 return Platform.X64;
152
153 case "Arm64":
154 return Platform.ARM64;
155
156 case "Intel":
157 default:
158 return Platform.X86;
159 }
160 }
161 }
162
163 return Platform.X86;
164 }
165
166 private static ArchitectureType PlatformToArchitecture(Platform platform)
167 {
168 switch (platform)
169 {
170 case Platform.X86: return ArchitectureType.X86;
171 case Platform.X64: return ArchitectureType.X64;
172 case Platform.ARM64: return ArchitectureType.Arm64;
173 default: throw new ArgumentException($"Unknown platform {platform}");
174 }
175 }
176 }
177 }