From a627ca9b720047e633a8fe72003ab9bee31006c5 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Tue, 26 Jul 2022 17:20:39 -0700 Subject: Create WixToolset.Heat.nupkg to distribute heat.exe and Heat targets Moves Heat functionality to the "tools" layer and packages it all up in WixToolset.Heat.nupkg for distribution in WiX v4. Completes 6838 --- .../heat/Extensibility/BaseMutatorExtension.cs | 202 +++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 src/tools/heat/Extensibility/BaseMutatorExtension.cs (limited to 'src/tools/heat/Extensibility/BaseMutatorExtension.cs') diff --git a/src/tools/heat/Extensibility/BaseMutatorExtension.cs b/src/tools/heat/Extensibility/BaseMutatorExtension.cs new file mode 100644 index 00000000..c36a8ed1 --- /dev/null +++ b/src/tools/heat/Extensibility/BaseMutatorExtension.cs @@ -0,0 +1,202 @@ +// 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.Harvesters.Extensibility +{ + using System; + using System.Collections.Generic; + using System.Text; + using Wix = WixToolset.Harvesters.Serialize; + + /// + /// The base mutator extension. Any of these methods can be overridden to change + /// the behavior of the mutator. + /// + public abstract class BaseMutatorExtension : IMutatorExtension + { + /// + /// Gets or sets the mutator core for the extension. + /// + /// The mutator core for the extension. + public IHarvesterCore Core { get; set; } + + /// + /// Gets the sequence of the extension. + /// + /// The sequence of the extension. + public abstract int Sequence { get; } + + /// + /// Mutate a WiX document. + /// + /// The Wix document element. + public virtual void Mutate(Wix.Wix wix) + { + } + + /// + /// Mutate a WiX document as a string. + /// + /// The Wix document element as a string. + /// The mutated Wix document as a string. + public virtual string Mutate(string wixString) + { + return wixString; + } + + /// + /// Generate unique MSI identifiers. + /// + protected class IdentifierGenerator + { + /// + /// + /// + public const int MaxProductIdentifierLength = 72; + + /// + /// + /// + public const int MaxModuleIdentifierLength = 35; + + private string baseName; + private int maxLength; + private Dictionary existingIdentifiers; + private Dictionary possibleIdentifiers; + private IHarvesterCore harvesterCore; + + /// + /// Instantiate a new IdentifierGenerator. + /// + /// The base resource name to use if a resource name contains no usable characters. + /// + public IdentifierGenerator(string baseName, IHarvesterCore harvesterCore) + { + this.baseName = baseName; + this.maxLength = IdentifierGenerator.MaxProductIdentifierLength; + this.existingIdentifiers = new Dictionary(StringComparer.OrdinalIgnoreCase); + this.possibleIdentifiers = new Dictionary(StringComparer.OrdinalIgnoreCase); + this.harvesterCore = harvesterCore; + } + + /// + /// Gets or sets the maximum length for generated identifiers. + /// + /// Maximum length for generated identifiers. (Default is 72.) + public int MaxIdentifierLength + { + get { return this.maxLength; } + set { this.maxLength = value; } + } + + /// + /// Index an existing identifier for collision detection. + /// + /// The identifier. + public void IndexExistingIdentifier(string identifier) + { + if (null == identifier) + { + throw new ArgumentNullException("identifier"); + } + + this.existingIdentifiers[identifier] = null; + } + + /// + /// Index a resource name for collision detection. + /// + /// The resource name. + public void IndexName(string name) + { + if (null == name) + { + throw new ArgumentNullException("name"); + } + + string identifier = this.CreateIdentifier(name, 0); + + if (this.possibleIdentifiers.ContainsKey(identifier)) + { + this.possibleIdentifiers[identifier] = String.Empty; + } + else + { + this.possibleIdentifiers.Add(identifier, null); + } + } + + /// + /// Get the identifier for the given resource name. + /// + /// The resource name. + /// A legal MSI identifier. + public string GetIdentifier(string name) + { + if (null == name) + { + throw new ArgumentNullException("name"); + } + + for (int i = 0; i <= Int32.MaxValue; i++) + { + string identifier = this.CreateIdentifier(name, i); + + if (this.existingIdentifiers.ContainsKey(identifier) || // already used + (0 == i && 0 != this.possibleIdentifiers.Count && null != this.possibleIdentifiers[identifier]) || // needs an index because its duplicated + (0 != i && this.possibleIdentifiers.ContainsKey(identifier))) // collides with another possible identifier + { + continue; + } + else // use this identifier + { + this.existingIdentifiers.Add(identifier, null); + + return identifier; + } + } + + throw new InvalidOperationException("Could not find a unique identifier for the given resource name."); + } + + /// + /// Create a legal MSI identifier from a resource name and an index. + /// + /// The name of the resource for which an identifier should be created. + /// An index to append to the end of the identifier to make it unique. + /// A legal MSI identifier. + public string CreateIdentifier(string name, int index) + { + if (null == name) + { + throw new ArgumentNullException("name"); + } + + StringBuilder identifier = new StringBuilder(); + + // Convert the name to a standard MSI identifier + identifier.Append(this.harvesterCore.CreateIdentifierFromFilename(name)); + + // no legal identifier characters were found, use the base id instead + if (0 == identifier.Length) + { + identifier.Append(this.baseName); + } + + // truncate the identifier if it's too long (reserve 3 characters for up to 99 collisions) + int adjustedMaxLength = this.MaxIdentifierLength - (index != 0 ? 3 : 0); + if (adjustedMaxLength < identifier.Length) + { + identifier.Length = adjustedMaxLength; + } + + // if the index is not zero, then append it to the identifier name + if (0 != index) + { + identifier.AppendFormat("_{0}", index); + } + + return identifier.ToString(); + } + } + } +} -- cgit v1.2.3-55-g6feb