// 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 { using System; using System.Collections.Generic; using System.IO; using WixToolset.Data; using WixToolset.Harvesters.Data; using WixToolset.Harvesters.Extensibility; /// /// A utility heat extension for the WiX Toolset Harvester application. /// public sealed class UtilHeatExtension : BaseHeatExtension { public UtilHeatExtension(IServiceProvider serviceProvider) { } /// /// Gets the supported command line types for this extension. /// /// The supported command line types for this extension. public override HeatCommandLineOption[] CommandLineTypes { get { return new HeatCommandLineOption[] { new HeatCommandLineOption("dir", "harvest a directory"), new HeatCommandLineOption("file", "harvest a file"), new HeatCommandLineOption("perf", "harvest performance counters"), new HeatCommandLineOption("reg", "harvest a .reg file"), new HeatCommandLineOption("-ag", "autogenerate component guids at compile time"), new HeatCommandLineOption("-cg ", "component group name (cannot contain spaces e.g -cg MyComponentGroup)"), new HeatCommandLineOption("-dr ", "directory reference to root directories (cannot contain spaces e.g. -dr MyAppDirRef)"), new HeatCommandLineOption("-var ", "substitute File/@Source=\"SourceDir\" with a preprocessor or a wix variable" + Environment.NewLine + "(e.g. -var var.MySource will become File/@Source=\"$(var.MySource)\\myfile.txt\" and " + Environment.NewLine + "-var wix.MySource will become File/@Source=\"!(wix.MySource)\\myfile.txt\""), new HeatCommandLineOption("-gg", "generate guids now"), new HeatCommandLineOption("-g1", "generated guids are not in brackets"), new HeatCommandLineOption("-ke", "keep empty directories"), new HeatCommandLineOption("-scom", "suppress COM elements"), new HeatCommandLineOption("-sfrag", "suppress fragments"), new HeatCommandLineOption("-srd", "suppress harvesting the root directory as an element"), new HeatCommandLineOption("-svb6", "suppress VB6 COM elements"), new HeatCommandLineOption("-sreg", "suppress registry harvesting"), new HeatCommandLineOption("-suid", "suppress unique identifiers for files, components, & directories"), new HeatCommandLineOption("-t", "transform harvested output with XSL file"), new HeatCommandLineOption("-template", "use template, one of: fragment,module,product"), }; } } /// /// Parse the command line options for this extension. /// /// The active harvester type. /// The option arguments. public override void ParseOptions(string type, string[] args) { var active = false; IHarvesterExtension harvesterExtension = null; var suppressHarvestingRegistryValues = false; var utilFinalizeHarvesterMutator = new UtilFinalizeHarvesterMutator(); var utilMutator = new UtilMutator(); var transformMutators = new List(); var generateType = GenerateType.Components; // select the harvester switch (type) { case "dir": harvesterExtension = new DirectoryHarvester(); active = true; break; case "file": harvesterExtension = new FileHarvester(); active = true; break; case "perf": harvesterExtension = new PerformanceCategoryHarvester(); active = true; break; case "reg": harvesterExtension = new RegFileHarvester(); active = true; break; } // set default settings utilMutator.CreateFragments = true; utilMutator.SetUniqueIdentifiers = true; // parse the options for (var i = 0; i < args.Length; i++) { var commandSwitch = args[i]; if (null == commandSwitch || 0 == commandSwitch.Length) // skip blank arguments { continue; } if ('-' == commandSwitch[0] || '/' == commandSwitch[0]) { var truncatedCommandSwitch = commandSwitch.Substring(1); if ("ag" == truncatedCommandSwitch) { utilMutator.AutogenerateGuids = true; } else if ("cg" == truncatedCommandSwitch) { utilMutator.ComponentGroupName = this.GetArgumentParameter(args, i); if (this.Core.Messaging.EncounteredError) { return; } } else if ("dr" == truncatedCommandSwitch) { var dr = this.GetArgumentParameter(args, i); if (this.Core.Messaging.EncounteredError) { return; } if (harvesterExtension is DirectoryHarvester directoryHarvester) { directoryHarvester.RootedDirectoryRef = dr; } else if (harvesterExtension is FileHarvester fileHarvester) { fileHarvester.RootedDirectoryRef = dr; } } else if ("gg" == truncatedCommandSwitch) { utilMutator.GenerateGuids = true; } else if ("g1" == truncatedCommandSwitch) { utilMutator.GuidFormat = "D"; } else if ("ke" == truncatedCommandSwitch) { if (harvesterExtension is DirectoryHarvester) { ((DirectoryHarvester)harvesterExtension).KeepEmptyDirectories = true; } else if (active) { // TODO: error message - not applicable to file harvester } } else if ("scom" == truncatedCommandSwitch) { if (active) { utilFinalizeHarvesterMutator.SuppressCOMElements = true; } else { // TODO: error message - not applicable } } else if ("svb6" == truncatedCommandSwitch) { if (active) { utilFinalizeHarvesterMutator.SuppressVB6COMElements = true; } else { // TODO: error message - not applicable } } else if ("sfrag" == truncatedCommandSwitch) { utilMutator.CreateFragments = false; } else if ("srd" == truncatedCommandSwitch) { if (harvesterExtension is DirectoryHarvester) { ((DirectoryHarvester)harvesterExtension).SuppressRootDirectory = true; } else if (harvesterExtension is FileHarvester) { ((FileHarvester)harvesterExtension).SuppressRootDirectory = true; } } else if ("sreg" == truncatedCommandSwitch) { suppressHarvestingRegistryValues = true; } else if ("suid" == truncatedCommandSwitch) { utilMutator.SetUniqueIdentifiers = false; if (harvesterExtension is DirectoryHarvester) { ((DirectoryHarvester)harvesterExtension).SetUniqueIdentifiers = false; } else if (harvesterExtension is FileHarvester) { ((FileHarvester)harvesterExtension).SetUniqueIdentifiers = false; } } else if (truncatedCommandSwitch.StartsWith("t:", StringComparison.Ordinal) || "t" == truncatedCommandSwitch) { string xslFile; if (truncatedCommandSwitch.StartsWith("t:", StringComparison.Ordinal)) { this.Core.Messaging.Write(WarningMessages.DeprecatedCommandLineSwitch("t:", "t")); xslFile = truncatedCommandSwitch.Substring(2); } else { xslFile = this.GetArgumentParameter(args, i, true); } if (0 <= xslFile.IndexOf('\"')) { this.Core.Messaging.Write(ErrorMessages.PathCannotContainQuote(xslFile)); return; } try { xslFile = Path.GetFullPath(xslFile); } catch (Exception e) { this.Core.Messaging.Write(ErrorMessages.InvalidCommandLineFileName(xslFile, e.Message)); return; } transformMutators.Add(new UtilTransformMutator(xslFile, transformMutators.Count)); } else if (truncatedCommandSwitch.StartsWith("template:", StringComparison.Ordinal) || "template" == truncatedCommandSwitch) { string template; if(truncatedCommandSwitch.StartsWith("template:", StringComparison.Ordinal)) { this.Core.Messaging.Write(WarningMessages.DeprecatedCommandLineSwitch("template:", "template")); template = truncatedCommandSwitch.Substring(9); } else { template = this.GetArgumentParameter(args, i); } switch (template) { case "fragment": utilMutator.TemplateType = TemplateType.Fragment; break; case "module": utilMutator.TemplateType = TemplateType.Module; break; case "package": case "product": utilMutator.TemplateType = TemplateType.Package ; break; default: // TODO: error break; } } else if ("var" == truncatedCommandSwitch) { if (active) { utilFinalizeHarvesterMutator.PreprocessorVariable = this.GetArgumentParameter(args, i); if (this.Core.Messaging.EncounteredError) { return; } } } else if ("generate" == truncatedCommandSwitch) { if (harvesterExtension is DirectoryHarvester) { var genType = this.GetArgumentParameter(args, i).ToUpperInvariant(); switch (genType) { case "COMPONENTS": generateType = GenerateType.Components; break; case "PAYLOADGROUP": generateType = GenerateType.PayloadGroup; break; default: throw new WixException(HarvesterErrors.InvalidDirectoryOutputType(genType)); } } else { // TODO: error message - not applicable } } } } // set the appropriate harvester extension if (active) { this.Core.Harvester.Extension = harvesterExtension; if (!suppressHarvestingRegistryValues) { this.Core.Mutator.AddExtension(new UtilHarvesterMutator()); } this.Core.Mutator.AddExtension(utilFinalizeHarvesterMutator); if (harvesterExtension is DirectoryHarvester directoryHarvester) { directoryHarvester.GenerateType = generateType; this.Core.Harvester.Core.RootDirectory = this.Core.Harvester.Core.ExtensionArgument; } else if (harvesterExtension is FileHarvester) { if (((FileHarvester)harvesterExtension).SuppressRootDirectory) { this.Core.Harvester.Core.RootDirectory = Path.GetDirectoryName(Path.GetFullPath(this.Core.Harvester.Core.ExtensionArgument)); } else { this.Core.Harvester.Core.RootDirectory = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetFullPath(this.Core.Harvester.Core.ExtensionArgument))); // GetDirectoryName() returns null for root paths such as "c:\", so make sure to support that as well if (null == this.Core.Harvester.Core.RootDirectory) { this.Core.Harvester.Core.RootDirectory = Path.GetPathRoot(Path.GetDirectoryName(Path.GetFullPath(this.Core.Harvester.Core.ExtensionArgument))); } } } } // set the mutator this.Core.Mutator.AddExtension(utilMutator); // add the transforms foreach (var transformMutator in transformMutators) { this.Core.Mutator.AddExtension(transformMutator); } } private string GetArgumentParameter(string[] args, int index) { return this.GetArgumentParameter(args, index, false); } private string GetArgumentParameter(string[] args, int index, bool allowSpaces) { var truncatedCommandSwitch = args[index]; var commandSwitchValue = args[index + 1]; //increment the index to the switch value index++; if (IsValidArg(args, index) && !String.IsNullOrEmpty(commandSwitchValue.Trim())) { if (!allowSpaces && commandSwitchValue.Contains(" ")) { this.Core.Messaging.Write(HarvesterErrors.SpacesNotAllowedInArgumentValue(truncatedCommandSwitch, commandSwitchValue)); } else { return commandSwitchValue; } } else { this.Core.Messaging.Write(HarvesterErrors.ArgumentRequiresValue(truncatedCommandSwitch)); } return null; } } }