Implement TagExtension as Binder extension

This commit is contained in:
Rob Mensching 2021-02-23 09:53:15 -08:00
Родитель e82ac3e6e4
Коммит b253e5f9f8
15 изменённых файлов: 555 добавлений и 362 удалений

Просмотреть файл

@ -1,5 +1,5 @@
{
"msbuild-sdks": {
"WixToolset.Sdk": "4.0.0-build-0170"
"WixToolset.Sdk": "4.0.0-build-0191"
}
}

Просмотреть файл

@ -0,0 +1,75 @@
// 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.
namespace WixToolsetTest.Tag
{
using System;
using System.IO;
using System.Xml.Linq;
using WixBuildTools.TestSupport;
using WixToolset.Core.TestPackage;
using WixToolset.Data;
using WixToolset.Tag;
using Xunit;
public class BundleTagExtensionFixture
{
private static readonly XNamespace SwidTagNamespace = "http://standards.iso.org/iso/19770/-2/2009/schema.xsd";
[Fact]
public void CanBuildBundleWithTag()
{
var testDataFolder = TestData.Get(@"TestData");
using (var fs = new DisposableFileSystem())
{
var baseFolder = fs.GetFolder();
var intermediateFolder = Path.Combine(baseFolder, "obj");
var extPath = Path.GetFullPath(new Uri(typeof(TagExtensionFactory).Assembly.CodeBase).LocalPath);
var result = WixRunner.Execute(new[]
{
"build",
Path.Combine(testDataFolder, "ProductTag", "PackageWithTag.wxs"),
Path.Combine(testDataFolder, "ProductTag", "PackageComponents.wxs"),
"-loc", Path.Combine(testDataFolder, "ProductTag", "Package.en-us.wxl"),
"-bindpath", Path.Combine(testDataFolder, "ProductTag"),
"-ext", extPath,
"-intermediateFolder", Path.Combine(intermediateFolder, "package"),
"-o", Path.Combine(baseFolder, "package", @"test.msi")
});
result.AssertSuccess();
result = WixRunner.Execute(new[]
{
"build",
Path.Combine(testDataFolder, "BundleTag", "BundleWithTag.wxs"),
"-ext", extPath,
"-bindpath", Path.Combine(testDataFolder, "BundleTag"),
"-bindpath", Path.Combine(baseFolder, "package"),
"-intermediateFolder", intermediateFolder,
"-o", Path.Combine(baseFolder, @"bin\test.exe")
});
result.AssertSuccess();
Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\test.exe")));
Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\test.wixpdb")));
using var ouput = WixOutput.Read(Path.Combine(baseFolder, @"bin\test.wixpdb"));
var badata = ouput.GetDataStream("wix-badata.xml");
var doc = XDocument.Load(badata);
var swidTag = doc.Root.Element("WixSoftwareTag").Value;
var docTag = XDocument.Parse(swidTag);
var title = docTag.Root.Element(SwidTagNamespace + "product_title").Value;
var version = docTag.Root.Element(SwidTagNamespace + "product_version").Element(SwidTagNamespace + "name").Value;
Assert.Equal("~TagTestBundle", title);
Assert.Equal("4.3.2.1", version);
}
}
}
}

Просмотреть файл

@ -8,18 +8,20 @@ namespace WixToolsetTest.Tag
using WixToolset.Tag;
using Xunit;
public class TagExtensionFixture
public class PackageTagExtensionFixture
{
[Fact(Skip = "Currently fails")]
public void CanBuildUsingProductTag()
[Fact]
public void CanBuildPackageWithTag()
{
var folder = TestData.Get(@"TestData\ProductTag");
var build = new Builder(folder, typeof(TagExtensionFactory), new[] { folder });
var results = build.BuildAndQuery(Build, "Property");
Assert.Equal(new[]
var results = build.BuildAndQuery(Build, "File", "SoftwareIdentificationTag");
WixAssert.CompareLineByLine(new[]
{
"Property:",
"File:filF5_pLhBuF5b4N9XEo52g_hUM5Lo\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\texample.txt\t20\t\t\t512\t1",
"File:tagkVqdRrA2JRwvrWCPR6pTv7eOzoE\ttagkVqdRrA2JRwvrWCPR6pTv7eOzoE\tth_a8yhh|regid.2008-09.org.wixtoolset ~TagTestPackage.swidtag\t910\t\t\t1\t2",
"SoftwareIdentificationTag:tagkVqdRrA2JRwvrWCPR6pTv7eOzoE\tregid.2008-09.org.wixtoolset\t8738B0C5-C4AA-4634-8C03-11EAA2F1E15D\tComponent"
}, results.ToArray());
}

Просмотреть файл

@ -0,0 +1,15 @@
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:swid="http://wixtoolset.org/schemas/v4/wxs/tag">
<Bundle Name="~TagTestBundle" Version="4.3.2.1" Manufacturer="Example Corporation" UpgradeCode="047730A5-30FE-4A62-A520-DA9381B8226A">
<BootstrapperApplication>
<BootstrapperApplicationDll SourceFile="fakeba.dll" />
</BootstrapperApplication>
<swid:Tag Regid="regid.2008-09.org.wixtoolset" />
<Chain>
<MsiPackage SourceFile="test.msi">
<MsiProperty Name="TEST" Value="1" />
</MsiPackage>
</Chain>
</Bundle>
</Wix>

Просмотреть файл

@ -0,0 +1 @@
This is fakeba.dll.

Просмотреть файл

@ -1,5 +1,5 @@
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:swid="http://wixtoolset.org/schemas/v4/wxs/tag">
<Package Name="MsiPackage" Language="1033" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
<Package ProductCode="8738B0C5-C4AA-4634-8C03-11EAA2F1E15D" Name="~TagTestPackage" Language="1033" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
<MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />

Просмотреть файл

@ -12,10 +12,7 @@
</PropertyGroup>
<ItemGroup>
<Content Include="TestData\ProductTag\example.txt" CopyToOutputDirectory="PreserveNewest" />
<Content Include="TestData\ProductTag\Package.en-us.wxl" CopyToOutputDirectory="PreserveNewest" />
<Content Include="TestData\ProductTag\Package.wxs" CopyToOutputDirectory="PreserveNewest" />
<Content Include="TestData\ProductTag\PackageComponents.wxs" CopyToOutputDirectory="PreserveNewest" />
<Content Include="TestData\**" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -0,0 +1,167 @@
// 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.
namespace WixToolset.Tag
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using WixToolset.Data;
using WixToolset.Data.Symbols;
using WixToolset.Dtf.WindowsInstaller;
using WixToolset.Extensibility;
using WixToolset.Tag.Symbols;
using WixToolset.Tag.Writer;
/// <summary>
/// The Binder for the WiX Toolset Software Id Tag Extension.
/// </summary>
public sealed class TagBurnBackendBinderExtension : BaseBurnBackendBinderExtension
{
/// <summary>
/// Called for each extension symbol that hasn't been handled yet.
/// </summary>
/// <param name="section">The linked section.</param>
/// <param name="symbol">The current symbol.</param>
/// <returns>True if the symbol is a Bundle tag symbol.</returns>
public override bool TryProcessSymbol(IntermediateSection section, IntermediateSymbol symbol)
{
if (symbol is WixBundleTagSymbol tagSymbol)
{
tagSymbol.Xml = CalculateTagXml(section, tagSymbol);
var fragment = new XDocument(
new XElement("WixSoftwareTag",
new XAttribute("Filename", tagSymbol.Filename),
new XAttribute("Regid", tagSymbol.Regid),
new XCData(tagSymbol.Xml)
)
);
this.BackendHelper.AddBootstrapperApplicationData(fragment.Root.ToString(SaveOptions.DisableFormatting | SaveOptions.OmitDuplicateNamespaces));
return true;
}
return false;
}
private static string CalculateTagXml(IntermediateSection section, WixBundleTagSymbol tagSymbol)
{
var bundleSymbol = section.Symbols.OfType<WixBundleSymbol>().Single();
var uniqueId = Guid.Parse(bundleSymbol.BundleId).ToString("D");
var bundleVersion = TagWriter.CreateFourPartVersion(bundleSymbol.Version);
var packageTags = CollectPackageTags(section);
var licensed = (tagSymbol.Attributes == 1);
if (!Enum.TryParse(tagSymbol.Type, out TagType type))
{
type = TagType.Application;
}
var containedTags = CalculateContainedTagsAndType(packageTags, ref type);
using (var ms = new MemoryStream())
{
TagWriter.CreateTagFile(ms, tagSymbol.Regid, uniqueId.ToUpperInvariant(), bundleSymbol.Name, bundleVersion, bundleSymbol.Manufacturer, licensed, type, containedTags);
// Use StreamReader to "eat" the BOM if present.
ms.Position = 0;
using (var streamReader = new StreamReader(ms, Encoding.UTF8))
{
return streamReader.ReadToEnd();
}
}
}
private static IList<SoftwareTag> CollectPackageTags(IntermediateSection section)
{
var tags = new List<SoftwareTag>();
var msiPackages = section.Symbols.OfType<WixBundlePackageSymbol>().Where(s => s.Type == WixBundlePackageType.Msi).ToList();
if (msiPackages.Any())
{
var payloadSymbolsById = section.Symbols.OfType<WixBundlePayloadSymbol>().ToDictionary(s => s.Id.Id);
foreach (var msiPackage in msiPackages)
{
var payload = payloadSymbolsById[msiPackage.PayloadRef];
using (var db = new Database(payload.SourceFile.Path))
{
using (var view = db.OpenView("SELECT `Regid`, `UniqueId`, `Type` FROM `SoftwareIdentificationTag`"))
{
view.Execute();
while (true)
{
using (var record = view.Fetch())
{
if (null == record)
{
break;
}
if (!Enum.TryParse(record.GetString(3), out TagType type))
{
type = TagType.Unknown;
}
tags.Add(new SoftwareTag() { Regid = record.GetString(1), Id = record.GetString(2), Type = type });
}
}
}
}
}
}
return tags;
}
private static IEnumerable<SoftwareTag> CalculateContainedTagsAndType(IEnumerable<SoftwareTag> allTags, ref TagType type)
{
var containedTags = new List<SoftwareTag>();
foreach (var tag in allTags)
{
// If this tag type is an Application or Group then try to coerce our type to a Group.
if (TagType.Application == tag.Type || TagType.Group == tag.Type)
{
// If the type is still unknown, change our tag type and clear any contained tags that might have already
// been colllected.
if (TagType.Unknown == type)
{
type = TagType.Group;
containedTags = new List<SoftwareTag>();
}
// If we are a Group then we can add this as a contained tag, otherwise skip it.
if (TagType.Group == type)
{
containedTags.Add(tag);
}
// TODO: should we warn if this bundle is typed as a non-Group software id tag but is actually
// carrying Application or Group software tags?
}
else if (TagType.Component == tag.Type || TagType.Feature == tag.Type)
{
// We collect Component and Feature tags only if the our tag is an Application or might still default to an Application.
if (TagType.Application == type || TagType.Unknown == type)
{
containedTags.Add(tag);
}
}
}
// If our type was not set by now, we'll default to an Application.
if (TagType.Unknown == type)
{
type = TagType.Application;
}
return containedTags;
}
}
}

Просмотреть файл

@ -256,6 +256,7 @@ namespace WixToolset.Tag
this.ParseHelper.EnsureTable(section, sourceLineNumbers, TagTableDefinitions.SoftwareIdentificationTag);
var symbol = section.AddSymbol(new WixProductTagSymbol(sourceLineNumbers, fileId)
{
FileRef = fileId.Id,
Regid = regid,
Name = name,
Type = type,

Просмотреть файл

@ -12,6 +12,11 @@ namespace WixToolset.Data
return Message(sourceLineNumbers, Ids.IllegalName, "The Tag/@Name attribute value, '{1}', contains invalid filename identifiers. The Tag/@Name may have defaulted from the {0}/@Name attrbute. If so, use the Tag/@Name attribute to provide a valid filename. Any character except for the follow may be used: \\ ? | > < : / * \".", parentElement, name);
}
public static Message SingleRegIdPerProduct(SourceLineNumber sourceLineNumbers, string regid, string firstRegid, SourceLineNumber firstSourceLineNumbers)
{
return Message(sourceLineNumbers, Ids.SingleRegIdPerProduct, "All of the Tag/@Regid attribute values in a package must match. The RegId '{0}' does not match the first RegId '{1}' found at: {2}.", regid, firstRegid, firstSourceLineNumbers.ToString());
}
private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
{
return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args);
@ -25,6 +30,7 @@ namespace WixToolset.Data
public enum Ids
{
IllegalName = 6601,
SingleRegIdPerProduct = 6602,
}
}
}

Просмотреть файл

@ -12,6 +12,7 @@ namespace WixToolset.Tag
{
typeof(TagCompiler),
typeof(TagExtensionData),
typeof(TagBurnBackendBinderExtension),
typeof(TagWindowsInstallerBackendBinderExtension),
};
}

Просмотреть файл

@ -6,417 +6,213 @@ namespace WixToolset.Tag
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using WixToolset.Data;
using WixToolset.Data.Symbols;
using WixToolset.Data.WindowsInstaller;
using WixToolset.Dtf.WindowsInstaller;
using WixToolset.Extensibility;
using WixToolset.Extensibility.Data;
using WixToolset.Extensibility.Services;
using WixToolset.Tag.Symbols;
using WixToolset.Tag.Writer;
/// <summary>
/// The Binder for the WiX Toolset Software Id Tag Extension.
/// The Windows Installer backend enhancements for the WiX Toolset Software Id Tag Extension.
/// </summary>
public sealed class TagWindowsInstallerBackendBinderExtension : BaseWindowsInstallerBackendBinderExtension
public sealed class TagWindowsInstallerBackendBinderExtension : BaseWindowsInstallerBackendBinderExtension, IResolverExtension
{
public override IEnumerable<TableDefinition> TableDefinitions => TagTableDefinitions.All;
#if TODO_TAG_BINDER_EXTENSION
private string overallRegid;
private RowDictionary<Row> swidRows = new RowDictionary<Row>();
private IBackendHelper backendHelper;
private string workingFolder;
/// <summary>
/// Called before database binding occurs.
/// Ensures the first draft of the SWID Tag exists on disk when being included
/// in MSI packages.
/// </summary>
public override void Initialize(Output output)
/// <param name="context">Resolve context.</param>
public void PreResolve(IResolveContext context)
{
var section = context.IntermediateRepresentation.Sections.FirstOrDefault();
// Only process MSI packages.
if (OutputType.Product != output.Type)
if (SectionType.Product != section?.Type)
{
return;
}
this.overallRegid = null; // always reset overall regid on initialize.
this.backendHelper = context.ServiceProvider.GetService<IBackendHelper>();
// Ensure the tag files are generated to be imported by the MSI.
this.CreateProductTagFiles(output);
this.workingFolder = CalculateWorkingFolder(context.IntermediateFolder);
// Ensure any tag files are generated to be imported by the MSI.
var tagSymbols = this.CreateProductTagFiles(section);
var tagSymbol = tagSymbols.FirstOrDefault();
if (tagSymbol != null)
{
// If we created any tag files, add a WixVariable to map to the regid.
section.AddSymbol(new WixVariableSymbol(tagSymbol.SourceLineNumbers, new Identifier(AccessModifier.Private, "WixTagRegid"))
{
Value = tagSymbol.Regid,
Overridable = false
});
}
}
/// <summary>
/// Unused.
/// </summary>
public IResolveFileResult ResolveFile(string source, IntermediateSymbolDefinition symbolDefinition, SourceLineNumber sourceLineNumbers, BindStage bindStage) => null;
/// <summary>
/// Unused.
/// </summary>
public void PostResolve(IResolveResult result) { }
/// <summary>
/// Called before database binding occurs.
/// </summary>
public override void PreBackendBind(IBindContext context)
{
base.PreBackendBind(context);
this.backendHelper = base.BackendHelper;
this.workingFolder = CalculateWorkingFolder(context.IntermediateFolder);
}
/// <summary>
/// Called after database variable resolution occurs.
/// </summary>
public override void AfterResolvedFields(Output output)
public override void SymbolsFinalized(IntermediateSection section)
{
// Only process MSI packages.
if (OutputType.Product != output.Type)
if (section.Type != SectionType.Product)
{
return;
}
Table wixBindUpdateFilesTable = output.Tables["WixBindUpdatedFiles"];
var tagSymbols = this.CreateProductTagFiles(section);
// We'll end up re-writing the tag files but this time we may have the ProductCode
// now to use as the unique id.
List<WixFileRow> updatedFileRows = this.CreateProductTagFiles(output);
foreach (WixFileRow updateFileRow in updatedFileRows)
foreach (var tagSymbol in tagSymbols)
{
Row row = wixBindUpdateFilesTable.CreateRow(updateFileRow.SourceLineNumbers);
row[0] = updateFileRow.File;
section.Symbols.Add(tagSymbol);
}
}
/// <summary>
/// Called after all output changes occur and right before the output is bound into its final format.
/// </summary>
public override void Finish(Output output)
private IEnumerable<SoftwareIdentificationTagSymbol> CreateProductTagFiles(IntermediateSection section)
{
// Only finish bundles.
if (OutputType.Bundle != output.Type)
var productTags = section.Symbols.OfType<WixProductTagSymbol>().ToList();
if (!productTags.Any())
{
return;
return null;
}
this.overallRegid = null; // always reset overall regid on initialize.
string productCode = null;
string productName = null;
string productVersion = null;
string manufacturer = null;
Table tagTable = output.Tables["WixBundleTag"];
if (null != tagTable)
foreach (var property in section.Symbols.OfType<PropertySymbol>())
{
Table table = output.Tables["WixBundle"];
WixBundleRow bundleInfo = (WixBundleRow)table.Rows[0];
Version bundleVersion = TagBinder.CreateFourPartVersion(bundleInfo.Version);
// Try to collect all the software id tags from all the child packages.
IList<SoftwareTag> allTags = TagBinder.CollectPackageTags(output);
foreach (Row tagRow in tagTable.Rows)
switch (property.Id.Id)
{
string regid = (string)tagRow[1];
string name = (string)tagRow[2];
bool licensed = (null != tagRow[3] && 0 != (int)tagRow[3]);
string typeString = (string)tagRow[5];
TagType type = String.IsNullOrEmpty(typeString) ? TagType.Unknown : (TagType)Enum.Parse(typeof(TagType), typeString);
IList<SoftwareTag> containedTags = TagBinder.CalculateContainedTagsAndType(allTags, ref type);
using (MemoryStream ms = new MemoryStream())
{
TagBinder.CreateTagFile(ms, regid, bundleInfo.BundleId.ToString("D").ToUpperInvariant(), bundleInfo.Name, bundleVersion, bundleInfo.Publisher, licensed, type, containedTags);
tagRow[4] = Encoding.UTF8.GetString(ms.ToArray());
}
case "ProductCode":
productCode = property.Value;
break;
case "ProductName":
productName = property.Value;
break;
case "ProductVersion":
productVersion = TagWriter.CreateFourPartVersion(property.Value);
break;
case "Manufacturer":
manufacturer = property.Value;
break;
}
}
}
private List<WixFileRow> CreateProductTagFiles(Output output)
{
List<WixFileRow> updatedFileRows = new List<WixFileRow>();
SourceLineNumber sourceLineNumbers = null;
Table tagTable = output.Tables["WixProductTag"];
if (null != tagTable)
// If the ProductCode is available, only keep it if it is a GUID.
if (!String.IsNullOrEmpty(productCode))
{
string productCode = null;
string productName = null;
Version productVersion = null;
string manufacturer = null;
Table properties = output.Tables["Property"];
foreach (Row property in properties.Rows)
if (productCode.Equals("*"))
{
switch ((string)property[0])
{
case "ProductCode":
productCode = (string)property[1];
break;
case "ProductName":
productName = (string)property[1];
break;
case "ProductVersion":
productVersion = TagBinder.CreateFourPartVersion((string)property[1]);
break;
case "Manufacturer":
manufacturer = (string)property[1];
break;
}
productCode = null;
}
else if (Guid.TryParse(productCode, out var guid))
{
productCode = guid.ToString("D").ToUpperInvariant();
}
else // not a GUID, erase it.
{
productCode = null;
}
}
Directory.CreateDirectory(this.workingFolder);
var fileSymbols = section.Symbols.OfType<FileSymbol>().ToList();
var swidSymbols = new Dictionary<string, SoftwareIdentificationTagSymbol>();
foreach (var tagSymbol in productTags)
{
var tagFileRef = tagSymbol.FileRef;
var licensed = (tagSymbol.Attributes == 1);
var uniqueId = String.IsNullOrEmpty(productCode) ? tagSymbol.Name.Replace(" ", "-") : productCode;
if (!Enum.TryParse(tagSymbol.Type, out TagType type))
{
type = TagType.Application;
}
// If the ProductCode is available, only keep it if it is a GUID.
if (!String.IsNullOrEmpty(productCode))
// Ensure all tag symbols in this product share a regid.
var firstTagSymbol = swidSymbols.Values.FirstOrDefault();
if (firstTagSymbol != null && firstTagSymbol.Regid != tagSymbol.Regid)
{
if (productCode.Equals("*"))
this.Messaging.Write(TagErrors.SingleRegIdPerProduct(tagSymbol.SourceLineNumbers, tagSymbol.Regid, firstTagSymbol.Regid, firstTagSymbol.SourceLineNumbers));
continue;
}
// Find the FileSymbol that is referenced by this WixProductTag.
var fileSymbol = fileSymbols.FirstOrDefault(f => f.Id.Id == tagFileRef);
if (fileSymbol != null)
{
var fileName = String.IsNullOrEmpty(fileSymbol.Name) ? Path.GetFileName(fileSymbol.Source.Path) : fileSymbol.Name;
// Write the tag file.
fileSymbol.Source = new IntermediateFieldPathValue
{
productCode = null;
Path = Path.Combine(this.workingFolder, fileName)
};
this.backendHelper.TrackFile(fileSymbol.Source.Path, TrackedFileType.Temporary, fileSymbol.SourceLineNumbers);
using (var fs = new FileStream(fileSymbol.Source.Path, FileMode.Create))
{
TagWriter.CreateTagFile(fs, tagSymbol.Regid, uniqueId, productName, productVersion, manufacturer, licensed, type, null);
}
else
// Ensure the matching "SoftwareIdentificationTag" symbol exists and is populated correctly.
if (!swidSymbols.TryGetValue(tagFileRef, out var swidRow))
{
try
swidRow = new SoftwareIdentificationTagSymbol(fileSymbol.SourceLineNumbers)
{
Guid guid = new Guid(productCode);
productCode = guid.ToString("D").ToUpperInvariant();
}
catch // not a GUID, erase it.
{
productCode = null;
}
}
}
FileRef = tagFileRef,
Regid = tagSymbol.Regid
};
Table wixFileTable = output.Tables["WixFile"];
foreach (Row tagRow in tagTable.Rows)
{
string fileId = (string)tagRow[0];
string regid = (string)tagRow[1];
string name = (string)tagRow[2];
bool licensed = (null != tagRow[3] && 1 == (int)tagRow[3]);
string typeString = (string)tagRow[4];
TagType type = String.IsNullOrEmpty(typeString) ? TagType.Application : (TagType)Enum.Parse(typeof(TagType), typeString);
string uniqueId = String.IsNullOrEmpty(productCode) ? name.Replace(" ", "-") : productCode;
if (String.IsNullOrEmpty(this.overallRegid))
{
this.overallRegid = regid;
sourceLineNumbers = tagRow.SourceLineNumbers;
}
else if (!this.overallRegid.Equals(regid, StringComparison.Ordinal))
{
// TODO: display error that only one regid supported.
swidSymbols.Add(tagFileRef, swidRow);
}
// Find the WixFileRow that matches for this WixProductTag.
foreach (WixFileRow wixFileRow in wixFileTable.Rows)
{
if (fileId == wixFileRow.File)
{
// Write the tag file.
wixFileRow.Source = Path.GetTempFileName();
using (FileStream fs = new FileStream(wixFileRow.Source, FileMode.Create))
{
TagBinder.CreateTagFile(fs, regid, uniqueId, productName, productVersion, manufacturer, licensed, type, null);
}
updatedFileRows.Add(wixFileRow); // remember that we modified this file.
// Ensure the matching "SoftwareIdentificationTag" row exists and
// is populated correctly.
Row swidRow;
if (!this.swidRows.TryGetValue(fileId, out swidRow))
{
Table swid = output.Tables["SoftwareIdentificationTag"];
swidRow = swid.CreateRow(wixFileRow.SourceLineNumbers);
swidRow[0] = fileId;
swidRow[1] = this.overallRegid;
this.swidRows.Add(swidRow);
}
// Always rewrite.
swidRow[2] = uniqueId;
swidRow[3] = type.ToString();
break;
}
}
// Always rewrite.
swidRow.TagId = uniqueId;
swidRow.Type = type.ToString();
}
}
// If we remembered the source line number for the regid, then add
// a WixVariable to map to the regid.
if (null != sourceLineNumbers)
{
Table wixVariableTable = output.EnsureTable(this.Core.TableDefinitions["WixVariable"]);
WixVariableRow wixVariableRow = (WixVariableRow)wixVariableTable.CreateRow(sourceLineNumbers);
wixVariableRow.Id = "WixTagRegid";
wixVariableRow.Value = this.overallRegid;
wixVariableRow.Overridable = false;
}
return updatedFileRows;
return swidSymbols.Values;
}
private static Version CreateFourPartVersion(string versionString)
{
Version version = new Version(versionString);
return new Version(version.Major,
-1 < version.Minor ? version.Minor : 0,
-1 < version.Build ? version.Build : 0,
-1 < version.Revision ? version.Revision : 0);
}
private static IList<SoftwareTag> CollectPackageTags(Output bundle)
{
List<SoftwareTag> tags = new List<SoftwareTag>();
Table packageTable = bundle.Tables["WixBundlePackage"];
if (null != packageTable)
{
Table payloadTable = bundle.Tables["WixBundlePayload"];
RowDictionary<WixBundlePayloadRow> payloads = new RowDictionary<WixBundlePayloadRow>(payloadTable);
foreach (WixBundlePackageRow row in packageTable.RowsAs<WixBundlePackageRow>())
{
if (WixBundlePackageType.Msi == row.Type)
{
string packagePayloadId = row.PackagePayload;
WixBundlePayloadRow payload = payloads.Get(packagePayloadId);
using (Database db = new Database(payload.FullFileName))
{
if (db.Tables.Contains("SoftwareIdentificationTag"))
{
using (View view = db.OpenView("SELECT `Regid`, `UniqueId`, `Type` FROM `SoftwareIdentificationTag`"))
{
view.Execute();
while (true)
{
using (Record record = view.Fetch())
{
if (null == record)
{
break;
}
TagType type = String.IsNullOrEmpty(record.GetString(3)) ? TagType.Unknown : (TagType)Enum.Parse(typeof(TagType), record.GetString(3));
tags.Add(new SoftwareTag() { Regid = record.GetString(1), Id = record.GetString(2), Type = type });
}
}
}
}
}
}
}
}
return tags;
}
private static IList<SoftwareTag> CalculateContainedTagsAndType(IEnumerable<SoftwareTag> allTags, ref TagType type)
{
List<SoftwareTag> containedTags = new List<SoftwareTag>();
foreach (SoftwareTag tag in allTags)
{
// If this tag type is an Application or Group then try to coerce our type to a Group.
if (TagType.Application == tag.Type || TagType.Group == tag.Type)
{
// If the type is still unknown, change our tag type and clear any contained tags that might have already
// been colllected.
if (TagType.Unknown == type)
{
type = TagType.Group;
containedTags = new List<SoftwareTag>();
}
// If we are a Group then we can add this as a contained tag, otherwise skip it.
if (TagType.Group == type)
{
containedTags.Add(tag);
}
// TODO: should we warn if this bundle is typed as a non-Group software id tag but is actually
// carrying Application or Group software tags?
}
else if (TagType.Component == tag.Type || TagType.Feature == tag.Type)
{
// We collect Component and Feature tags only if the our tag is an Application or might still default to an Application.
if (TagType.Application == type || TagType.Unknown == type)
{
containedTags.Add(tag);
}
}
}
// If our type was not set by now, we'll default to an Application.
if (TagType.Unknown == type)
{
type = TagType.Application;
}
return containedTags;
}
private static void CreateTagFile(Stream stream, string regid, string uniqueId, string name, Version version, string manufacturer, bool licensed, TagType tagType, IList<SoftwareTag> containedTags)
{
using (XmlTextWriter writer = new XmlTextWriter(stream, Encoding.UTF8))
{
writer.WriteStartDocument();
writer.WriteStartElement("software_identification_tag", "http://standards.iso.org/iso/19770/-2/2009/schema.xsd");
writer.WriteElementString("entitlement_required_indicator", licensed ? "true" : "false");
writer.WriteElementString("product_title", name);
writer.WriteStartElement("product_version");
writer.WriteElementString("name", version.ToString());
writer.WriteStartElement("numeric");
writer.WriteElementString("major", version.Major.ToString());
writer.WriteElementString("minor", version.Minor.ToString());
writer.WriteElementString("build", version.Build.ToString());
writer.WriteElementString("review", version.Revision.ToString());
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteStartElement("software_creator");
writer.WriteElementString("name", manufacturer);
writer.WriteElementString("regid", regid);
writer.WriteEndElement();
if (licensed)
{
writer.WriteStartElement("software_licensor");
writer.WriteElementString("name", manufacturer);
writer.WriteElementString("regid", regid);
writer.WriteEndElement();
}
writer.WriteStartElement("software_id");
writer.WriteElementString("unique_id", uniqueId);
writer.WriteElementString("tag_creator_regid", regid);
writer.WriteEndElement();
writer.WriteStartElement("tag_creator");
writer.WriteElementString("name", manufacturer);
writer.WriteElementString("regid", regid);
writer.WriteEndElement();
if (null != containedTags && 0 < containedTags.Count)
{
writer.WriteStartElement("complex_of");
foreach (SoftwareTag tag in containedTags)
{
writer.WriteStartElement("software_id");
writer.WriteElementString("unique_id", tag.Id);
writer.WriteElementString("tag_creator_regid", tag.Regid);
writer.WriteEndElement(); // </software_id>
}
writer.WriteEndElement(); // </complex_of>
}
if (TagType.Unknown != tagType)
{
writer.WriteStartElement("extended_information");
writer.WriteStartElement("tag_type", "http://www.tagvault.org/tv_extensions.xsd");
writer.WriteValue(tagType.ToString());
writer.WriteEndElement(); // </tag_type>
writer.WriteEndElement(); // </extended_information>
}
writer.WriteEndElement(); // </software_identification_tag>
}
}
private enum TagType
{
Unknown,
Application,
Component,
Feature,
Group,
Patch,
}
private class SoftwareTag
{
public string Regid { get; set; }
public string Id { get; set; }
public TagType Type { get; set; }
}
#endif
private static string CalculateWorkingFolder(string intermediateFolder) => Path.Combine(intermediateFolder, "_swidtag");
}
}

Просмотреть файл

@ -0,0 +1,13 @@
// 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.
namespace WixToolset.Tag.Writer
{
public class SoftwareTag
{
public string Regid { get; set; }
public string Id { get; set; }
public TagType Type { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,14 @@
// 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.
namespace WixToolset.Tag.Writer
{
public enum TagType
{
Unknown,
Application,
Component,
Feature,
Group,
Patch,
}
}

Просмотреть файл

@ -0,0 +1,105 @@
// 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.
namespace WixToolset.Tag.Writer
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
public static class TagWriter
{
private static readonly XmlWriterSettings WriterSettings = new XmlWriterSettings
{
CloseOutput = false,
Encoding = Encoding.UTF8
};
public static string CreateFourPartVersion(string versionString)
{
if (Version.TryParse(versionString, out var version))
{
versionString = new Version(version.Major,
-1 < version.Minor ? version.Minor : 0,
-1 < version.Build ? version.Build : 0,
-1 < version.Revision ? version.Revision : 0).ToString();
}
return versionString;
}
public static void CreateTagFile(Stream stream, string regid, string uniqueId, string name, string version, string manufacturer, bool licensed, TagType tagType, IEnumerable<SoftwareTag> containedTags)
{
using (var writer = XmlWriter.Create(stream, WriterSettings))
{
writer.WriteStartDocument();
writer.WriteStartElement("software_identification_tag", "http://standards.iso.org/iso/19770/-2/2009/schema.xsd");
writer.WriteElementString("entitlement_required_indicator", licensed ? "true" : "false");
writer.WriteElementString("product_title", name);
writer.WriteStartElement("product_version");
writer.WriteElementString("name", version.ToString());
if (Version.TryParse(version, out var parsedVersion))
{
writer.WriteStartElement("numeric");
writer.WriteElementString("major", parsedVersion.Major.ToString());
writer.WriteElementString("minor", parsedVersion.Minor.ToString());
writer.WriteElementString("build", parsedVersion.Build.ToString());
writer.WriteElementString("review", parsedVersion.Revision.ToString());
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.WriteStartElement("software_creator");
writer.WriteElementString("name", manufacturer);
writer.WriteElementString("regid", regid);
writer.WriteEndElement();
if (licensed)
{
writer.WriteStartElement("software_licensor");
writer.WriteElementString("name", manufacturer);
writer.WriteElementString("regid", regid);
writer.WriteEndElement();
}
writer.WriteStartElement("software_id");
writer.WriteElementString("unique_id", uniqueId);
writer.WriteElementString("tag_creator_regid", regid);
writer.WriteEndElement();
writer.WriteStartElement("tag_creator");
writer.WriteElementString("name", manufacturer);
writer.WriteElementString("regid", regid);
writer.WriteEndElement();
if (containedTags?.Any() == true)
{
writer.WriteStartElement("complex_of");
foreach (var tag in containedTags)
{
writer.WriteStartElement("software_id");
writer.WriteElementString("unique_id", tag.Id);
writer.WriteElementString("tag_creator_regid", tag.Regid);
writer.WriteEndElement(); // </software_id>
}
writer.WriteEndElement(); // </complex_of>
}
if (TagType.Unknown != tagType)
{
writer.WriteStartElement("extended_information");
writer.WriteStartElement("tag_type", "http://www.tagvault.org/tv_extensions.xsd");
writer.WriteValue(tagType.ToString());
writer.WriteEndElement(); // </tag_type>
writer.WriteEndElement(); // </extended_information>
}
writer.WriteEndElement(); // </software_identification_tag>
}
}
}
}