From ecf3a0cca5a424a91ab98557d963d2535963d582 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Fri, 22 Dec 2017 15:53:01 -0800 Subject: Reintroduce binder extensions and light.exe for binding .wixouts --- src/light/App.ico | Bin 0 -> 1078 bytes src/light/AssemblyInfo.cs | 9 + src/light/LightCommandLine.cs | 485 ++++++++++++++++++++++++++++++ src/light/LightStrings.Designer.cs | 104 +++++++ src/light/LightStrings.resx | 174 +++++++++++ src/light/app.config | 9 + src/light/light.cs | 595 +++++++++++++++++++++++++++++++++++++ src/light/light.csproj | 21 ++ 8 files changed, 1397 insertions(+) create mode 100644 src/light/App.ico create mode 100644 src/light/AssemblyInfo.cs create mode 100644 src/light/LightCommandLine.cs create mode 100644 src/light/LightStrings.Designer.cs create mode 100644 src/light/LightStrings.resx create mode 100644 src/light/app.config create mode 100644 src/light/light.cs create mode 100644 src/light/light.csproj (limited to 'src/light') diff --git a/src/light/App.ico b/src/light/App.ico new file mode 100644 index 00000000..3a5525fd Binary files /dev/null and b/src/light/App.ico differ diff --git a/src/light/AssemblyInfo.cs b/src/light/AssemblyInfo.cs new file mode 100644 index 00000000..ab2fc0ab --- /dev/null +++ b/src/light/AssemblyInfo.cs @@ -0,0 +1,9 @@ +// 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. + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] diff --git a/src/light/LightCommandLine.cs b/src/light/LightCommandLine.cs new file mode 100644 index 00000000..9a90b9ce --- /dev/null +++ b/src/light/LightCommandLine.cs @@ -0,0 +1,485 @@ +// 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.Tools +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.IO; + using WixToolset.Core.CommandLine; + using WixToolset.Data; + using WixToolset.Extensibility.Services; + + public class LightCommandLine + { + public LightCommandLine(IMessaging messaging) + { + this.Messaging = messaging; + this.ShowLogo = true; + this.Tidy = true; + + this.CubeFiles = new List(); + this.SuppressIces = new List(); + this.Ices = new List(); + this.BindPaths = new List(); + this.Extensions = new List(); + this.Files = new List(); + this.LocalizationFiles = new List(); + this.Variables = new Dictionary(); + } + + public IMessaging Messaging { get; } + + public string PdbFile { get; private set; } + + public CompressionLevel? DefaultCompressionLevel { get; set; } + + public bool SuppressAclReset { get; private set; } + + public bool SuppressLayout { get; private set; } + + public bool SuppressWixPdb { get; private set; } + + public bool SuppressValidation { get; private set; } + + public string IntermediateFolder { get; private set; } + + public string OutputsFile { get; private set; } + + public string BuiltOutputsFile { get; private set; } + + public string WixprojectFile { get; private set; } + + public string ContentsFile { get; private set; } + + public List Ices { get; private set; } + + public string CabCachePath { get; private set; } + + public int CabbingThreadCount { get; private set; } + + public List CubeFiles { get; private set; } + + public List SuppressIces { get; private set; } + + public bool ShowLogo { get; private set; } + + public bool ShowHelp { get; private set; } + + public bool ShowPedanticMessages { get; private set; } + + public bool SuppressLocalization { get; private set; } + + public bool SuppressVersionCheck { get; private set; } + + public string[] Cultures { get; private set; } + + public string OutputFile { get; private set; } + + public bool OutputXml { get; private set; } + + public List BindPaths { get; private set; } + + public List Extensions { get; private set; } + + public List Files { get; private set; } + + public List LocalizationFiles { get; private set; } + + public bool Tidy { get; private set; } + + public string UnreferencedSymbolsFile { get; private set; } + + public IDictionary Variables { get; private set; } + + /// + /// Parse the commandline arguments. + /// + /// Commandline arguments. + public string[] Parse(string[] args) + { + List unprocessed = new List(); + + for (int i = 0; i < args.Length; ++i) + { + string arg = args[i]; + if (String.IsNullOrEmpty(arg)) // skip blank arguments + { + continue; + } + + if (1 == arg.Length) // treat '-' and '@' as filenames when by themselves. + { + unprocessed.Add(arg); + } + else if ('-' == arg[0] || '/' == arg[0]) + { + string parameter = arg.Substring(1); + if (parameter.Equals("b", StringComparison.Ordinal)) + { + if (!CommandLineHelper.IsValidArg(args, ++i)) + { + break; + } + + var bindPath = BindPath.Parse(args[i]); + + this.BindPaths.Add(bindPath); + } + else if (parameter.StartsWith("cultures:", StringComparison.Ordinal)) + { + string culturesString = arg.Substring(10).ToLower(CultureInfo.InvariantCulture); + + // When null is used treat it as if cultures wasn't specified. + // This is needed for batching over the light task when using MSBuild which doesn't + // support empty items + if (culturesString.Equals("null", StringComparison.OrdinalIgnoreCase)) + { + this.Cultures = null; + } + else + { + this.Cultures = culturesString.Split(';', ','); + + for (int c = 0; c < this.Cultures.Length; ++c) + { + // Neutral is different from null. For neutral we still want to do WXL filtering. + // Set the culture to the empty string = identifier for the invariant culture + if (this.Cultures[c].Equals("neutral", StringComparison.OrdinalIgnoreCase)) + { + this.Cultures[c] = String.Empty; + } + } + } + } + else if (parameter.StartsWith("dcl:", StringComparison.Ordinal)) + { + string defaultCompressionLevel = arg.Substring(5); + + if (String.IsNullOrEmpty(defaultCompressionLevel)) + { + break; + } + else if (Enum.TryParse(defaultCompressionLevel, true, out CompressionLevel compressionLevel)) + { + this.DefaultCompressionLevel = compressionLevel; + } + } + else if (parameter.StartsWith("d", StringComparison.Ordinal)) + { + parameter = arg.Substring(2); + string[] value = parameter.Split("=".ToCharArray(), 2); + + string preexisting; + if (1 == value.Length) + { + this.Messaging.Write(ErrorMessages.ExpectedWixVariableValue(value[0])); + } + else if (this.Variables.TryGetValue(value[0], out preexisting)) + { + this.Messaging.Write(ErrorMessages.WixVariableCollision(null, value[0])); + } + else + { + this.Variables.Add(value[0], value[1]); + } + } + else if (parameter.Equals("ext", StringComparison.Ordinal)) + { + if (!CommandLineHelper.IsValidArg(args, ++i)) + { + this.Messaging.Write(ErrorMessages.TypeSpecificationForExtensionRequired("-ext")); + break; + } + + this.Extensions.Add(args[i]); + } + else if (parameter.Equals("loc", StringComparison.Ordinal)) + { + string locFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i); + if (String.IsNullOrEmpty(locFile)) + { + break; + } + + this.LocalizationFiles.Add(locFile); + } + else if (parameter.Equals("nologo", StringComparison.Ordinal)) + { + this.ShowLogo = false; + } + else if (parameter.Equals("notidy", StringComparison.Ordinal)) + { + this.Tidy = false; + } + else if ("o" == parameter || "out" == parameter) + { + this.OutputFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i); + if (String.IsNullOrEmpty(this.OutputFile)) + { + break; + } + } + else if (parameter.Equals("pedantic", StringComparison.Ordinal)) + { + this.ShowPedanticMessages = true; + } + else if (parameter.Equals("sloc", StringComparison.Ordinal)) + { + this.SuppressLocalization = true; + } + else if (parameter.Equals("usf", StringComparison.Ordinal)) + { + this.UnreferencedSymbolsFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i); + + if (String.IsNullOrEmpty(this.UnreferencedSymbolsFile)) + { + break; + } + } + else if (parameter.Equals("xo", StringComparison.Ordinal)) + { + this.OutputXml = true; + } + else if (parameter.Equals("cc", StringComparison.Ordinal)) + { + this.CabCachePath = CommandLineHelper.GetDirectory(parameter, this.Messaging, args, ++i); + + if (String.IsNullOrEmpty(this.CabCachePath)) + { + break; + } + } + else if (parameter.Equals("ct", StringComparison.Ordinal)) + { + if (!CommandLineHelper.IsValidArg(args, ++i)) + { + this.Messaging.Write(ErrorMessages.IllegalCabbingThreadCount(String.Empty)); + break; + } + + int ct = 0; + if (!Int32.TryParse(args[i], out ct) || 0 >= ct) + { + this.Messaging.Write(ErrorMessages.IllegalCabbingThreadCount(args[i])); + break; + } + + this.CabbingThreadCount = ct; + this.Messaging.Write(VerboseMessages.SetCabbingThreadCount(this.CabbingThreadCount.ToString())); + } + else if (parameter.Equals("cub", StringComparison.Ordinal)) + { + string cubeFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i); + + if (String.IsNullOrEmpty(cubeFile)) + { + break; + } + + this.CubeFiles.Add(cubeFile); + } + else if (parameter.StartsWith("ice:", StringComparison.Ordinal)) + { + this.Ices.Add(parameter.Substring(4)); + } + else if (parameter.Equals("intermediatefolder", StringComparison.OrdinalIgnoreCase)) + { + this.IntermediateFolder = CommandLineHelper.GetDirectory(parameter, this.Messaging, args, ++i); + + if (String.IsNullOrEmpty(this.IntermediateFolder)) + { + break; + } + } + else if (parameter.Equals("contentsfile", StringComparison.Ordinal)) + { + this.ContentsFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i); + + if (String.IsNullOrEmpty(this.ContentsFile)) + { + break; + } + } + else if (parameter.Equals("outputsfile", StringComparison.Ordinal)) + { + this.OutputsFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i); + + if (String.IsNullOrEmpty(this.OutputsFile)) + { + break; + } + } + else if (parameter.Equals("builtoutputsfile", StringComparison.Ordinal)) + { + this.BuiltOutputsFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i); + + if (String.IsNullOrEmpty(this.BuiltOutputsFile)) + { + break; + } + } + else if (parameter.Equals("wixprojectfile", StringComparison.Ordinal)) + { + this.WixprojectFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i); + + if (String.IsNullOrEmpty(this.WixprojectFile)) + { + break; + } + } + else if (parameter.Equals("pdbout", StringComparison.Ordinal)) + { + this.PdbFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i); + + if (String.IsNullOrEmpty(this.PdbFile)) + { + break; + } + } + else if (parameter.StartsWith("sice:", StringComparison.Ordinal)) + { + this.SuppressIces.Add(parameter.Substring(5)); + } + else if (parameter.Equals("sl", StringComparison.Ordinal)) + { + this.SuppressLayout = true; + } + else if (parameter.Equals("spdb", StringComparison.Ordinal)) + { + this.SuppressWixPdb = true; + } + else if (parameter.Equals("sacl", StringComparison.Ordinal)) + { + this.SuppressAclReset = true; + } + else if (parameter.Equals("sval", StringComparison.Ordinal)) + { + this.SuppressValidation = true; + } + else if ("sv" == parameter) + { + this.SuppressVersionCheck = true; + } + else if (parameter.StartsWith("sw", StringComparison.Ordinal)) + { + string paramArg = parameter.Substring(2); + if (0 == paramArg.Length) + { + this.Messaging.SuppressAllWarnings = true; + } + else + { + int suppressWarning = 0; + if (!Int32.TryParse(paramArg, out suppressWarning) || 0 >= suppressWarning) + { + this.Messaging.Write(ErrorMessages.IllegalSuppressWarningId(paramArg)); + } + else + { + this.Messaging.SuppressWarningMessage(suppressWarning); + } + } + } + else if (parameter.StartsWith("wx", StringComparison.Ordinal)) + { + string paramArg = parameter.Substring(2); + if (0 == paramArg.Length) + { + this.Messaging.WarningsAsError = true; + } + else + { + int elevateWarning = 0; + if (!Int32.TryParse(paramArg, out elevateWarning) || 0 >= elevateWarning) + { + this.Messaging.Write(ErrorMessages.IllegalWarningIdAsError(paramArg)); + } + else + { + this.Messaging.ElevateWarningMessage(elevateWarning); + } + } + } + else if ("v" == parameter) + { + this.Messaging.ShowVerboseMessages = true; + } + else if ("?" == parameter || "help" == parameter) + { + this.ShowHelp = true; + break; + } + else + { + unprocessed.Add(arg); + } + } + else if ('@' == arg[0]) + { + string[] parsedArgs = CommandLineResponseFile.Parse(arg.Substring(1)); + string[] unparsedArgs = this.Parse(parsedArgs); + unprocessed.AddRange(unparsedArgs); + } + else + { + unprocessed.Add(arg); + } + } + + return unprocessed.ToArray(); + } + + public string[] ParsePostExtensions(string[] remaining) + { + List unprocessed = new List(); + + for (int i = 0; i < remaining.Length; ++i) + { + string arg = remaining[i]; + if (String.IsNullOrEmpty(arg)) // skip blank arguments + { + continue; + } + + if (1 < arg.Length && ('-' == arg[0] || '/' == arg[0])) + { + unprocessed.Add(arg); + } + else + { + this.Files.AddRange(CommandLineHelper.GetFiles(arg, "Source")); + } + } + + if (0 == this.Files.Count) + { + this.ShowHelp = true; + } + else if (String.IsNullOrEmpty(this.OutputFile)) + { + if (1 < this.Files.Count) + { + this.Messaging.Write(ErrorMessages.MustSpecifyOutputWithMoreThanOneInput()); + } + + // After the linker tells us what the output type actually is, we'll change the ".wix" to the correct extension. + this.OutputFile = Path.ChangeExtension(Path.GetFileName(this.Files[0]), ".wix"); + + // Add the directories of the input files as unnamed bind paths. + foreach (string file in this.Files) + { + BindPath bindPath = new BindPath(Path.GetDirectoryName(Path.GetFullPath(file))); + this.BindPaths.Add(bindPath); + } + } + + if (!this.SuppressWixPdb && String.IsNullOrEmpty(this.PdbFile) && !String.IsNullOrEmpty(this.OutputFile)) + { + this.PdbFile = Path.ChangeExtension(this.OutputFile, ".wixpdb"); + } + + return unprocessed.ToArray(); + } + } +} diff --git a/src/light/LightStrings.Designer.cs b/src/light/LightStrings.Designer.cs new file mode 100644 index 00000000..50e271fd --- /dev/null +++ b/src/light/LightStrings.Designer.cs @@ -0,0 +1,104 @@ +// 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.Tools { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class LightStrings { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal LightStrings() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WixToolset.Tools.LightStrings", typeof(LightStrings).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to -b <path> specify a binder path to locate all files + /// (default: current directory) + /// prefix the path with 'name=' where 'name' is the name of your + /// named bindpath. + /// -cc <path> path to cache built cabinets (will not be deleted after linking) + /// -ct <N> number of threads to use when creating cabinets + /// (default: %NUMBER_OF_PROCESSORS%) + /// -cub <file.cub> additional .cub file containing ICEs to run + /// -dcl:level set default cabinet compression l [rest of string was truncated]";. + /// + internal static string CommandLineArguments { + get { + return ResourceManager.GetString("CommandLineArguments", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The -bf (bind files) option is only applicable with the -xo option.. + /// + internal static string EXP_BindFileOptionNotApplicable { + get { + return ResourceManager.GetString("EXP_BindFileOptionNotApplicable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot link object files (.wixobj) files with an output file (.wixout). + /// + internal static string EXP_CannotLinkObjFilesWithOutpuFile { + get { + return ResourceManager.GetString("EXP_CannotLinkObjFilesWithOutpuFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to usage: light.exe [-?] [-b bindPath] [-nologo] [-out outputFile] objectFile [objectFile ...] [@responseFile] + /// + ///{0} + /// + ///Environment variables: + /// WIX_TEMP overrides the temporary directory used for cab creation, msm exploding, .... + /// + internal static string HelpMessage { + get { + return ResourceManager.GetString("HelpMessage", resourceCulture); + } + } + } +} diff --git a/src/light/LightStrings.resx b/src/light/LightStrings.resx new file mode 100644 index 00000000..3f586a5d --- /dev/null +++ b/src/light/LightStrings.resx @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + -b <path> specify a binder path to locate all files + (default: current directory) + prefix the path with 'name=' where 'name' is the name of your + named bindpath. + -cc <path> path to cache built cabinets (will not be deleted after linking) + -ct <N> number of threads to use when creating cabinets + (default: %NUMBER_OF_PROCESSORS%) + -cub <file.cub> additional .cub file containing ICEs to run + -dcl:level set default cabinet compression level + (low, medium, high, none, mszip; mszip default) + -eav exact assembly versions (breaks .NET 1.1 RTM compatibility) + -ice:<ICE> run a specific internal consistency evaluator (ICE) + -pdbout <output.wixpdb> save the WixPdb to a specific file + (default: same name as output with wixpdb extension) + -reusecab reuse cabinets from cabinet cache + -sacl suppress resetting ACLs + (useful when laying out image to a network share) + -sice:<ICE> suppress an internal consistency evaluator (ICE) + -sl suppress layout + -spdb suppress outputting the WixPdb + -sval suppress MSI/MSM validation + -cultures:<cultures> semicolon or comma delimited list of localized + string cultures to load from .wxl files and libraries. + Precedence of cultures is from left to right. + -d<name>[=<value>] define a wix variable, with or without a value. + -ext <extension> extension assembly or "class, assembly" + -loc <loc.wxl> read localization strings from .wxl file + -nologo skip printing light logo information + -notidy do not delete temporary files (useful for debugging) + -o[ut] specify output file (default: write to current directory) + -pedantic show pedantic messages + -sloc suppress localization + -sw[N] suppress all warnings or a specific message ID + (example: -sw1009 -sw1103) + -usf <output.xml> unreferenced symbols file + -v verbose output + -wx[N] treat all warnings or a specific message ID as an error + (example: -wx1009 -wx1103) + -xo output wixout format instead of MSI format + -? | -help this help information + + + Cannot link object files (.wixobj) files with an output file (.wixout) + + + usage: light.exe [-?] [-b bindPath] [-nologo] [-out outputFile] objectFile [objectFile ...] [@responseFile] + +{0} + +Environment variables: + WIX_TEMP overrides the temporary directory used for cab creation, msm exploding, ... + {0} is replaced by a list of light's arguments. + + diff --git a/src/light/app.config b/src/light/app.config new file mode 100644 index 00000000..71c529fb --- /dev/null +++ b/src/light/app.config @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/light/light.cs b/src/light/light.cs new file mode 100644 index 00000000..c0967caa --- /dev/null +++ b/src/light/light.cs @@ -0,0 +1,595 @@ +// 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.Tools +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Runtime.InteropServices; + using System.Text; + using System.Threading; + using WixToolset.Core; + using WixToolset.Data; + using WixToolset.Data.Bind; + using WixToolset.Extensibility; + using WixToolset.Extensibility.Services; + + /// + /// The main entry point for light. + /// + public sealed class Light + { + LightCommandLine commandLine; + private IEnumerable extensionData; + //private IEnumerable binderExtensions; + //private IEnumerable fileManagers; + + /// + /// The main entry point for light. + /// + /// Commandline arguments for the application. + /// Returns the application error code. + [MTAThread] + public static int Main(string[] args) + { + var serviceProvider = new WixToolsetServiceProvider(); + + var listener = new ConsoleMessageListener("WIX", "light.exe"); + + Light light = new Light(); + return light.Run(serviceProvider, listener, args); + } + + /// + /// Main running method for the application. + /// + /// Commandline arguments to the application. + /// Returns the application error code. + public int Run(IServiceProvider serviceProvider, IMessageListener listener, string[] args) + { + var messaging = serviceProvider.GetService(); + messaging.SetListener(listener); + + try + { + var unparsed = this.ParseCommandLineAndLoadExtensions(serviceProvider, messaging, args); + + if (!messaging.EncounteredError) + { + if (this.commandLine.ShowLogo) + { + AppCommon.DisplayToolHeader(); + } + + if (this.commandLine.ShowHelp) + { + PrintHelp(); + AppCommon.DisplayToolFooter(); + } + else + { + foreach (string arg in unparsed) + { + messaging.Write(WarningMessages.UnsupportedCommandLineArgument(arg)); + } + + this.Bind(serviceProvider, messaging); + } + } + } + catch (WixException we) + { + messaging.Write(we.Error); + } + catch (Exception e) + { + messaging.Write(ErrorMessages.UnexpectedException(e.Message, e.GetType().ToString(), e.StackTrace)); + if (e is NullReferenceException || e is SEHException) + { + throw; + } + } + + return messaging.LastErrorNumber; + } + + /// + /// Parse command line and load all the extensions. + /// + /// Command line arguments to be parsed. + private IEnumerable ParseCommandLineAndLoadExtensions(IServiceProvider serviceProvider, IMessaging messaging, string[] args) + { + this.commandLine = new LightCommandLine(messaging); + + string[] unprocessed = this.commandLine.Parse(args); + if (messaging.EncounteredError) + { + return unprocessed; + } + + // Load extensions. + var extensionManager = CreateExtensionManagerWithStandardBackends(serviceProvider); + foreach (string extension in this.commandLine.Extensions) + { + extensionManager.Load(extension); + } + + // Extension data command line processing. + var context = serviceProvider.GetService(); + context.Arguments = null; + context.ExtensionManager = extensionManager; + context.Messaging = messaging; + context.ParsedArguments = args; + + var commandLineExtensions = extensionManager.Create(); + foreach (var extension in commandLineExtensions) + { + extension.PreParse(context); + } + + // Process unproccessed arguments. + List actuallyUnprocessed = new List(); + foreach (var arg in unprocessed) + { + if (!this.TryParseCommandLineArgumentWithExtension(arg, commandLineExtensions)) + { + actuallyUnprocessed.Add(arg); + } + } + + return this.commandLine.ParsePostExtensions(actuallyUnprocessed.ToArray()); + } + + private void Bind(IServiceProvider serviceProvider, IMessaging messaging) + { + var output = this.LoadWixout(messaging); + + if (messaging.EncounteredError) + { + return; + } + + var intermediateFolder = this.commandLine.IntermediateFolder; + if (String.IsNullOrEmpty(intermediateFolder)) + { + intermediateFolder = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + } + + var localizations = this.LoadLocalizationFiles(messaging, this.commandLine.LocalizationFiles); + + if (messaging.EncounteredError) + { + return; + } + + ResolveResult resolveResult; + { + var resolver = new Resolver(serviceProvider); + resolver.BindPaths = this.commandLine.BindPaths; + resolver.IntermediateFolder = intermediateFolder; + resolver.IntermediateRepresentation = output; + resolver.Localizations = localizations; + + resolveResult = resolver.Execute(); + } + + if (messaging.EncounteredError) + { + return; + } + + BindResult bindResult; + { + var binder = new Binder(serviceProvider); + binder.CabbingThreadCount = this.commandLine.CabbingThreadCount; + binder.CabCachePath = this.commandLine.CabCachePath; + binder.Codepage = resolveResult.Codepage; + binder.DefaultCompressionLevel = this.commandLine.DefaultCompressionLevel; + binder.DelayedFields = resolveResult.DelayedFields; + binder.ExpectedEmbeddedFiles = resolveResult.ExpectedEmbeddedFiles; + binder.Ices = this.commandLine.Ices; + binder.IntermediateFolder = intermediateFolder; + binder.IntermediateRepresentation = resolveResult.IntermediateRepresentation; + binder.OutputPath = this.commandLine.OutputFile; + binder.OutputPdbPath = Path.ChangeExtension(this.commandLine.OutputFile, ".wixpdb"); + binder.SuppressIces = this.commandLine.SuppressIces; + binder.SuppressValidation = this.commandLine.SuppressValidation; + + bindResult = binder.Execute(); + } + + if (messaging.EncounteredError) + { + return; + } + + { + var layout = new Layout(serviceProvider); + layout.FileTransfers = bindResult.FileTransfers; + layout.ContentFilePaths = bindResult.ContentFilePaths; + layout.ContentsFile = this.commandLine.ContentsFile; + layout.OutputsFile = this.commandLine.OutputsFile; + layout.BuiltOutputsFile = this.commandLine.BuiltOutputsFile; + layout.SuppressAclReset = this.commandLine.SuppressAclReset; + + layout.Execute(); + } + } + + private void Run(IMessaging messaging) + { +#if false + // Initialize the variable resolver from the command line. + WixVariableResolver wixVariableResolver = new WixVariableResolver(); + foreach (var wixVar in this.commandLine.Variables) + { + wixVariableResolver.AddVariable(wixVar.Key, wixVar.Value); + } + + // Initialize the linker from the command line. + Linker linker = new Linker(); + linker.UnreferencedSymbolsFile = this.commandLine.UnreferencedSymbolsFile; + linker.ShowPedanticMessages = this.commandLine.ShowPedanticMessages; + linker.WixVariableResolver = wixVariableResolver; + + foreach (IExtensionData data in this.extensionData) + { + linker.AddExtensionData(data); + } + + // Initialize the binder from the command line. + WixToolset.Binder binder = new WixToolset.Binder(); + binder.CabCachePath = this.commandLine.CabCachePath; + binder.ContentsFile = this.commandLine.ContentsFile; + binder.BuiltOutputsFile = this.commandLine.BuiltOutputsFile; + binder.OutputsFile = this.commandLine.OutputsFile; + binder.WixprojectFile = this.commandLine.WixprojectFile; + binder.BindPaths.AddRange(this.commandLine.BindPaths); + binder.CabbingThreadCount = this.commandLine.CabbingThreadCount; + if (this.commandLine.DefaultCompressionLevel.HasValue) + { + binder.DefaultCompressionLevel = this.commandLine.DefaultCompressionLevel.Value; + } + binder.Ices.AddRange(this.commandLine.Ices); + binder.SuppressIces.AddRange(this.commandLine.SuppressIces); + binder.SuppressAclReset = this.commandLine.SuppressAclReset; + binder.SuppressLayout = this.commandLine.SuppressLayout; + binder.SuppressValidation = this.commandLine.SuppressValidation; + binder.PdbFile = this.commandLine.SuppressWixPdb ? null : this.commandLine.PdbFile; + binder.TempFilesLocation = AppCommon.GetTempLocation(); + binder.WixVariableResolver = wixVariableResolver; + + foreach (IBinderExtension extension in this.binderExtensions) + { + binder.AddExtension(extension); + } + + foreach (IBinderFileManager fileManager in this.fileManagers) + { + binder.AddExtension(fileManager); + } + + // Initialize the localizer. + Localizer localizer = this.InitializeLocalization(linker.TableDefinitions); + if (messaging.EncounteredError) + { + return; + } + + wixVariableResolver.Localizer = localizer; + linker.Localizer = localizer; + binder.Localizer = localizer; + + // Loop through all the believed object files. + List
sections = new List
(); + Output output = null; + foreach (string inputFile in this.commandLine.Files) + { + string inputFileFullPath = Path.GetFullPath(inputFile); + FileFormat format = FileStructure.GuessFileFormatFromExtension(Path.GetExtension(inputFileFullPath)); + bool retry; + do + { + retry = false; + + try + { + switch (format) + { + case FileFormat.Wixobj: + Intermediate intermediate = Intermediate.Load(inputFileFullPath, linker.TableDefinitions, this.commandLine.SuppressVersionCheck); + sections.AddRange(intermediate.Sections); + break; + + case FileFormat.Wixlib: + Library library = Library.Load(inputFileFullPath, linker.TableDefinitions, this.commandLine.SuppressVersionCheck); + AddLibraryLocalizationsToLocalizer(library, this.commandLine.Cultures, localizer); + sections.AddRange(library.Sections); + break; + + default: + output = Output.Load(inputFileFullPath, this.commandLine.SuppressVersionCheck); + break; + } + } + catch (WixUnexpectedFileFormatException e) + { + format = e.FileFormat; + retry = (FileFormat.Wixobj == format || FileFormat.Wixlib == format || FileFormat.Wixout == format); // .wixobj, .wixout and .wixout are supported by light. + if (!retry) + { + messaging.OnMessage(e.Error); + } + } + } while (retry); + } + + // Stop processing if any errors were found loading object files. + if (messaging.EncounteredError) + { + return; + } + + // and now for the fun part + if (null == output) + { + OutputType expectedOutputType = OutputType.Unknown; + if (!String.IsNullOrEmpty(this.commandLine.OutputFile)) + { + expectedOutputType = Output.GetOutputType(Path.GetExtension(this.commandLine.OutputFile)); + } + + output = linker.Link(sections, expectedOutputType); + + // If an error occurred during linking, stop processing. + if (null == output) + { + return; + } + } + else if (0 != sections.Count) + { + throw new InvalidOperationException(LightStrings.EXP_CannotLinkObjFilesWithOutpuFile); + } + + bool tidy = true; // clean up after ourselves by default. + try + { + // only output the xml if its a patch build or user specfied to only output wixout + string outputFile = this.commandLine.OutputFile; + string outputExtension = Path.GetExtension(outputFile); + if (this.commandLine.OutputXml || OutputType.Patch == output.Type) + { + if (String.IsNullOrEmpty(outputExtension) || outputExtension.Equals(".wix", StringComparison.Ordinal)) + { + outputExtension = (OutputType.Patch == output.Type) ? ".wixmsp" : ".wixout"; + outputFile = Path.ChangeExtension(outputFile, outputExtension); + } + + output.Save(outputFile); + } + else // finish creating the MSI/MSM + { + if (String.IsNullOrEmpty(outputExtension) || outputExtension.Equals(".wix", StringComparison.Ordinal)) + { + outputExtension = Output.GetExtension(output.Type); + outputFile = Path.ChangeExtension(outputFile, outputExtension); + } + + binder.Bind(output, outputFile); + } + } + catch (WixException we) // keep files around for debugging IDT issues. + { + if (we is WixInvalidIdtException) + { + tidy = false; + } + + throw; + } + catch (Exception) // keep files around for debugging unexpected exceptions. + { + tidy = false; + throw; + } + finally + { + if (null != binder) + { + binder.Cleanup(tidy); + } + } + + return; +#endif + } + +#if false + private Localizer InitializeLocalization(TableDefinitionCollection tableDefinitions) + { + Localizer localizer = null; + + // Instantiate the localizer and load any localization files. + if (!this.commandLine.SuppressLocalization || 0 < this.commandLine.LocalizationFiles.Count || null != this.commandLine.Cultures || !this.commandLine.OutputXml) + { + List localizations = new List(); + + // Load each localization file. + foreach (string localizationFile in this.commandLine.LocalizationFiles) + { + Localization localization = Localizer.ParseLocalizationFile(localizationFile, tableDefinitions); + if (null != localization) + { + localizations.Add(localization); + } + } + + localizer = new Localizer(); + if (null != this.commandLine.Cultures) + { + // Alocalizations in order specified in cultures. + foreach (string culture in this.commandLine.Cultures) + { + foreach (Localization localization in localizations) + { + if (culture.Equals(localization.Culture, StringComparison.OrdinalIgnoreCase)) + { + localizer.AddLocalization(localization); + } + } + } + } + else // no cultures specified, so try neutral culture and if none of those add all loc files. + { + bool neutralFound = false; + foreach (Localization localization in localizations) + { + if (String.IsNullOrEmpty(localization.Culture)) + { + // If a neutral wxl was provided use it. + localizer.AddLocalization(localization); + neutralFound = true; + } + } + + if (!neutralFound) + { + // No cultures were specified and no neutral wxl are available, include all of the loc files. + foreach (Localization localization in localizations) + { + localizer.AddLocalization(localization); + } + } + } + + // Load localizations provided by extensions with data. + foreach (IExtensionData data in this.extensionData) + { + Library library = data.GetLibrary(tableDefinitions); + if (null != library) + { + // Load the extension's default culture if it provides one and no cultures were specified. + string[] extensionCultures = this.commandLine.Cultures; + if (null == extensionCultures && null != data.DefaultCulture) + { + extensionCultures = new string[] { data.DefaultCulture }; + } + + AddLibraryLocalizationsToLocalizer(library, extensionCultures, localizer); + } + } + } + + return localizer; + } + + private void AddLibraryLocalizationsToLocalizer(Library library, string[] cultures, Localizer localizer) + { + foreach (Localization localization in library.GetLocalizations(cultures)) + { + localizer.AddLocalization(localization); + } + } +#endif + + private bool TryParseCommandLineArgumentWithExtension(string arg, IEnumerable extensions) + { + foreach (var extension in extensions) + { + // TODO: decide what to do with "IParseCommandLine" argument. + if (extension.TryParseArgument(null, arg)) + { + return true; + } + } + + return false; + } + + private IEnumerable LoadLocalizationFiles(IMessaging messaging, IEnumerable locFiles) + { + foreach (var loc in locFiles) + { + var localization = Localizer.ParseLocalizationFile(messaging, loc); + + yield return localization; + } + } + + private Intermediate LoadWixout(IMessaging messaging) + { + var path = this.commandLine.Files.Single(); + + return Intermediate.Load(path); + } + + private static IExtensionManager CreateExtensionManagerWithStandardBackends(IServiceProvider serviceProvider) + { + var extensionManager = serviceProvider.GetService(); + + foreach (var type in new[] { typeof(WixToolset.Core.Burn.WixToolsetStandardBackend), typeof(WixToolset.Core.WindowsInstaller.WixToolsetStandardBackend) }) + { + extensionManager.Add(type.Assembly); + } + + return extensionManager; + } + + private static void PrintHelp() + { + string lightArgs = LightStrings.CommandLineArguments; + + Console.WriteLine(String.Format(LightStrings.HelpMessage, lightArgs)); + } + + private class ConsoleMessageListener : IMessageListener + { + public ConsoleMessageListener(string shortName, string longName) + { + this.ShortAppName = shortName; + this.LongAppName = longName; + + PrepareConsoleForLocalization(); + } + + public string LongAppName { get; } + + public string ShortAppName { get; } + + public void Write(Message message) + { + var filename = message.SourceLineNumbers?.FileName ?? this.LongAppName; + var line = message.SourceLineNumbers?.LineNumber ?? -1; + var type = message.Level.ToString().ToLowerInvariant(); + var output = message.Level >= MessageLevel.Warning ? Console.Out : Console.Error; + + if (line > 0) + { + filename = String.Concat(filename, "(", line, ")"); + } + + output.WriteLine("{0} : {1} {2}{3:0000}: {4}", filename, type, this.ShortAppName, message.Id, message.ToString()); + } + + public void Write(string message) + { + Console.Out.WriteLine(message); + } + + private static void PrepareConsoleForLocalization() + { + Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentUICulture.GetConsoleFallbackUICulture(); + + if (Console.OutputEncoding.CodePage != Encoding.UTF8.CodePage && + Console.OutputEncoding.CodePage != Thread.CurrentThread.CurrentUICulture.TextInfo.OEMCodePage && + Console.OutputEncoding.CodePage != Thread.CurrentThread.CurrentUICulture.TextInfo.ANSICodePage) + { + Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); + } + } + } + } +} diff --git a/src/light/light.csproj b/src/light/light.csproj new file mode 100644 index 00000000..20e10b11 --- /dev/null +++ b/src/light/light.csproj @@ -0,0 +1,21 @@ + + + + + + net461 + Exe + Linker + WiX Toolset Linker + + + + + + + + + + + + -- cgit v1.2.3-55-g6feb