@joebigelow / wix / commits / dc9f4c32

Support wixout and extract Resolve and Layout from Binder

Rob Mensching committed Dec 21, 2017 at 13:42 UTC dc9f4c329e6f55ce7595970463e0caf148096f4b
44 files changed +1005 -799
src/WixToolset.BuildTasks/WixToolTask.cs
+1 -2
@@ -193,8 +193,7 @@ namespace WixToolset.BuildTasks
193 string responseFile = null;
194 try
195 {
196 - string responseFileSwitch;
197 - responseFile = this.GetTemporaryResponseFile(responseFileCommands, out responseFileSwitch);
196 + responseFile = this.GetTemporaryResponseFile(responseFileCommands, out var responseFileSwitch);
197 if (!String.IsNullOrEmpty(responseFileSwitch))
198 {
199 commandLineCommands = commandLineCommands + " " + responseFileSwitch;
src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+3 -3
@@ -74,7 +74,9 @@ namespace WixToolset.Core.Burn
74 this.DelayedFields = context.DelayedFields;
75 this.ExpectedEmbeddedFiles = context.ExpectedEmbeddedFiles;
76
77 - this.BackendExtensions = context.ExtensionManager.Create<IBurnBackendExtension>();
77 + var extensionManager = context.ServiceProvider.GetService<IExtensionManager>();
78 +
79 + this.BackendExtensions = extensionManager.Create<IBurnBackendExtension>();
80 }
81
82 public CompressionLevel DefaultCompressionLevel { private get; set; }
@@ -85,8 +87,6 @@ namespace WixToolset.Core.Burn
87
88 private IEnumerable<IBurnBackendExtension> BackendExtensions { get; }
89
88 - public IEnumerable<IBinderExtension> Extensions { private get; set; }
89 -
90 public Intermediate Output { private get; set; }
91
92 public string OutputPath { private get; set; }
src/WixToolset.Core.Burn/BundleBackend.cs
+1 -2
@@ -9,7 +9,6 @@ namespace WixToolset.Core.Burn
9 using WixToolset.Data;
10 using WixToolset.Data.Bind;
11 using WixToolset.Extensibility;
12 - using WixToolset.Extensibility.Services;
12
13 internal class BundleBackend : IBackend
14 {
@@ -25,7 +24,7 @@ namespace WixToolset.Core.Burn
24 //command.WixVariableResolver = context.WixVariableResolver;
25 command.Execute();
26
28 - return new BindResult(command.FileTransfers, command.ContentFilePaths);
27 + return new BindResult { FileTransfers = command.FileTransfers, ContentFilePaths = command.ContentFilePaths };
28 }
29
30 public bool Inscribe(IInscribeContext context)
src/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs
-1
@@ -14,7 +14,6 @@ namespace WixToolset.Core.Burn.Bundles
14 using WixToolset.Extensibility;
15 using WixToolset.Core.Native;
16 using Dtf = WixToolset.Dtf.WindowsInstaller;
17 - using WixToolset.Bind;
17 using WixToolset.Data.Bind;
18
19 /// <summary>
src/WixToolset.Core.Burn/Bundles/ProcessPayloadsCommand.cs
-1
@@ -9,7 +9,6 @@ namespace WixToolset.Core.Burn.Bundles
9 using System.Security.Cryptography;
10 using System.Security.Cryptography.X509Certificates;
11 using System.Text;
12 - using WixToolset.Bind;
12 using WixToolset.Data;
13 using WixToolset.Data.Bind;
14 using WixToolset.Data.Rows;
src/WixToolset.Core.Burn/BurnBackendFactory.cs
+1 -1
@@ -9,7 +9,7 @@ namespace WixToolset.Core.Burn
9
10 internal class BurnBackendFactory : IBackendFactory
11 {
12 - public bool TryCreateBackend(string outputType, string outputFile, IBindContext context, out IBackend backend)
12 + public bool TryCreateBackend(string outputType, string outputFile, WixToolset.Extensibility.IBindContext context, out IBackend backend)
13 {
14 if (String.IsNullOrEmpty(outputType))
15 {
src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+6 -92
@@ -22,31 +22,27 @@ namespace WixToolset.Core.WindowsInstaller.Bind
22 // As outlined in RFC 4122, this is our namespace for generating name-based (version 3) UUIDs.
23 internal static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}");
24
25 - public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendExtension> backendExtension, Validator validator)
25 + public BindDatabaseCommand(WixToolset.Extensibility.IBindContext context, IEnumerable<IWindowsInstallerBackendExtension> backendExtension, Validator validator)
26 {
27 this.TableDefinitions = WindowsInstallerStandardInternal.GetTableDefinitions();
28
29 - this.BindPaths = context.BindPaths;
29 this.CabbingThreadCount = context.CabbingThreadCount;
30 this.CabCachePath = context.CabCachePath;
31 this.Codepage = context.Codepage;
32 this.DefaultCompressionLevel = context.DefaultCompressionLevel;
33 this.DelayedFields = context.DelayedFields;
34 this.ExpectedEmbeddedFiles = context.ExpectedEmbeddedFiles;
36 - this.Extensions = context.Extensions;
35 + this.FileSystemExtensions = context.FileSystemExtensions;
36 this.Intermediate = context.IntermediateRepresentation;
37 this.Messaging = context.Messaging;
38 this.OutputPath = context.OutputPath;
39 this.PdbFile = context.OutputPdbPath;
40 this.IntermediateFolder = context.IntermediateFolder;
41 this.Validator = validator;
43 - this.WixVariableResolver = context.WixVariableResolver;
44 -
42 +
43 this.BackendExtensions = backendExtension;
44 }
45
48 - private IEnumerable<BindPath> BindPaths { get; }
49 -
46 private int Codepage { get; }
47
48 private int CabbingThreadCount { get; }
@@ -59,12 +55,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
55
56 public IEnumerable<IExpectedExtractFile> ExpectedEmbeddedFiles { get; }
57
58 + public IEnumerable<IFileSystemExtension> FileSystemExtensions { get; }
59 +
60 public bool DeltaBinaryPatch { get; set; }
61
62 private IEnumerable<IWindowsInstallerBackendExtension> BackendExtensions { get; }
63
66 - private IEnumerable<IBinderExtension> Extensions { get; }
67 -
64 private string PdbFile { get; }
65
66 private Intermediate Intermediate { get; }
@@ -83,7 +79,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
79
80 private Validator Validator { get; }
81
86 - private IBindVariableResolver WixVariableResolver { get; }
82
83 public IEnumerable<FileTransfer> FileTransfers { get; private set; }
84
@@ -100,8 +95,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
95 // If there are any fields to resolve later, create the cache to populate during bind.
96 var variableCache = this.DelayedFields.Any() ? new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) : null;
97
103 - this.LocalizeUI(section);
104 -
98 // Process the summary information table before the other tables.
99 bool compressed;
100 bool longNames;
@@ -534,85 +527,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
527 intermediate.Save(Path.ChangeExtension(this.OutputPath, "wir"));
528 }
529
537 - /// <summary>
538 - /// Localize dialogs and controls.
539 - /// </summary>
540 - /// <param name="tables">The tables to localize.</param>
541 - private void LocalizeUI(IntermediateSection section)
542 - {
543 - foreach (var row in section.Tuples.OfType<DialogTuple>())
544 - {
545 - string dialog = row.Dialog;
546 -
547 - if (this.WixVariableResolver.TryGetLocalizedControl(dialog, null, out LocalizedControl localizedControl))
548 - {
549 - if (CompilerConstants.IntegerNotSet != localizedControl.X)
550 - {
551 - row.HCentering = localizedControl.X;
552 - }
553 -
554 - if (CompilerConstants.IntegerNotSet != localizedControl.Y)
555 - {
556 - row.VCentering = localizedControl.Y;
557 - }
558 -
559 - if (CompilerConstants.IntegerNotSet != localizedControl.Width)
560 - {
561 - row.Width = localizedControl.Width;
562 - }
563 -
564 - if (CompilerConstants.IntegerNotSet != localizedControl.Height)
565 - {
566 - row.Height = localizedControl.Height;
567 - }
568 -
569 - row.Attributes = row.Attributes | localizedControl.Attributes;
570 -
571 - if (!String.IsNullOrEmpty(localizedControl.Text))
572 - {
573 - row.Title = localizedControl.Text;
574 - }
575 - }
576 - }
577 -
578 -
579 - foreach (var row in section.Tuples.OfType<ControlTuple>())
580 - {
581 - string dialog = row.Dialog_;
582 - string control = row.Control;
583 -
584 - if (this.WixVariableResolver.TryGetLocalizedControl(dialog, control, out LocalizedControl localizedControl))
585 - {
586 - if (CompilerConstants.IntegerNotSet != localizedControl.X)
587 - {
588 - row.X = localizedControl.X;
589 - }
590 -
591 - if (CompilerConstants.IntegerNotSet != localizedControl.Y)
592 - {
593 - row.Y = localizedControl.Y;
594 - }
595 -
596 - if (CompilerConstants.IntegerNotSet != localizedControl.Width)
597 - {
598 - row.Width = localizedControl.Width;
599 - }
600 -
601 - if (CompilerConstants.IntegerNotSet != localizedControl.Height)
602 - {
603 - row.Height = localizedControl.Height;
604 - }
605 -
606 - row.Attributes = row.Attributes | localizedControl.Attributes;
607 -
608 - if (!String.IsNullOrEmpty(localizedControl.Text))
609 - {
610 - row.Text = localizedControl.Text;
611 - }
612 - }
613 - }
614 - }
615 -
530 #if TODO_FINISH_PATCH
531 /// <summary>
532 /// Copy file data between transform substorages and the patch output object
@@ -984,7 +898,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
898 private void GenerateDatabase(Output output, string databaseFile, bool keepAddedColumns, bool useSubdirectory)
899 {
900 var command = new GenerateDatabaseCommand();
987 - command.Extensions = this.Extensions;
901 + command.Extensions = this.FileSystemExtensions;
902 command.Output = output;
903 command.OutputPath = databaseFile;
904 command.KeepAddedColumns = keepAddedColumns;
src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
+1 -1
@@ -15,7 +15,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
15
16 internal class BindTransformCommand
17 {
18 - public IEnumerable<IBinderExtension> Extensions { private get; set; }
18 + public IEnumerable<IFileSystemExtension> Extensions { private get; set; }
19
20 public TableDefinitionCollection TableDefinitions { private get; set; }
21
src/WixToolset.Core.WindowsInstaller/Bind/CalculateComponentGuids.cs
+1 -2
@@ -6,7 +6,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Linq;
9 - using WixToolset.Bind;
9 using WixToolset.Core.Native;
10 using WixToolset.Data;
11 using WixToolset.Data.Tuples;
@@ -135,7 +134,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
134 if (fileRow.File == componentRow.KeyPath)
135 {
136 // calculate the key file's canonical target path
138 - string directoryPath = Binder.GetDirectoryPath(targetPathsByDirectoryId, componentIdGenSeeds, componentRow.Directory_, true);
137 + string directoryPath = PathResolver.GetDirectoryPath(targetPathsByDirectoryId, componentIdGenSeeds, componentRow.Directory_, true);
138 string fileName = Common.GetName(fileRow.LongFileName, false, true).ToLowerInvariant();
139 path = Path.Combine(directoryPath, fileName);
140
src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs
+1 -1
@@ -18,7 +18,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
18 {
19 public bool CopyOutFileRows { private get; set; }
20
21 - public IEnumerable<IBinderExtension> Extensions { private get; set; }
21 + public IEnumerable<IFileSystemExtension> Extensions { private get; set; }
22
23 public IMessaging Messaging { private get; set; }
24
src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
+1 -1
@@ -19,7 +19,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
19 {
20 public int Codepage { private get; set; }
21
22 - public IEnumerable<IBinderExtension> Extensions { private get; set; }
22 + public IEnumerable<IFileSystemExtension> Extensions { private get; set; }
23
24 /// <summary>
25 /// Whether to keep columns added in a transform.
src/WixToolset.Core.WindowsInstaller/Bind/PathResolver.cs new
+105
@@ -0,0 +1,105 @@
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.Core.WindowsInstaller.Bind
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using System.IO;
8 + using WixToolset.Data;
9 +
10 + internal static class PathResolver
11 + {
12 + /// <summary>
13 + /// Get the source path of a directory.
14 + /// </summary>
15 + /// <param name="directories">All cached directories.</param>
16 + /// <param name="componentIdGenSeeds">Hash table of Component GUID generation seeds indexed by directory id.</param>
17 + /// <param name="directory">Directory identifier.</param>
18 + /// <param name="canonicalize">Canonicalize the path for standard directories.</param>
19 + /// <returns>Source path of a directory.</returns>
20 + public static string GetDirectoryPath(Dictionary<string, ResolvedDirectory> directories, Dictionary<string, string> componentIdGenSeeds, string directory, bool canonicalize)
21 + {
22 + if (!directories.TryGetValue(directory, out var resolvedDirectory))
23 + {
24 + throw new WixException(ErrorMessages.ExpectedDirectory(directory));
25 + }
26 +
27 + if (null == resolvedDirectory.Path)
28 + {
29 + if (null != componentIdGenSeeds && componentIdGenSeeds.ContainsKey(directory))
30 + {
31 + resolvedDirectory.Path = (string)componentIdGenSeeds[directory];
32 + }
33 + else if (canonicalize && WindowsInstallerStandard.IsStandardDirectory(directory))
34 + {
35 + // when canonicalization is on, standard directories are treated equally
36 + resolvedDirectory.Path = directory;
37 + }
38 + else
39 + {
40 + string name = resolvedDirectory.Name;
41 +
42 + if (canonicalize)
43 + {
44 + name = name?.ToLowerInvariant();
45 + }
46 +
47 + if (String.IsNullOrEmpty(resolvedDirectory.DirectoryParent))
48 + {
49 + resolvedDirectory.Path = name;
50 + }
51 + else
52 + {
53 + string parentPath = GetDirectoryPath(directories, componentIdGenSeeds, resolvedDirectory.DirectoryParent, canonicalize);
54 +
55 + if (null != resolvedDirectory.Name)
56 + {
57 + resolvedDirectory.Path = Path.Combine(parentPath, name);
58 + }
59 + else
60 + {
61 + resolvedDirectory.Path = parentPath;
62 + }
63 + }
64 + }
65 + }
66 +
67 + return resolvedDirectory.Path;
68 + }
69 +
70 + /// <summary>
71 + /// Gets the source path of a file.
72 + /// </summary>
73 + /// <param name="directories">All cached directories in <see cref="ResolvedDirectory"/>.</param>
74 + /// <param name="directoryId">Parent directory identifier.</param>
75 + /// <param name="fileName">File name (in long|source format).</param>
76 + /// <param name="compressed">Specifies the package is compressed.</param>
77 + /// <param name="useLongName">Specifies the package uses long file names.</param>
78 + /// <returns>Source path of file relative to package directory.</returns>
79 + public static string GetFileSourcePath(Dictionary<string, ResolvedDirectory> directories, string directoryId, string fileName, bool compressed, bool useLongName)
80 + {
81 + string fileSourcePath = Common.GetName(fileName, true, useLongName);
82 +
83 + if (compressed)
84 + {
85 + // Use just the file name of the file since all uncompressed files must appear
86 + // in the root of the image in a compressed package.
87 + }
88 + else
89 + {
90 + // Get the relative path of where we want the file to be layed out as specified
91 + // in the Directory table.
92 + string directoryPath = PathResolver.GetDirectoryPath(directories, null, directoryId, false);
93 + fileSourcePath = Path.Combine(directoryPath, fileSourcePath);
94 + }
95 +
96 + // Strip off "SourceDir" if it's still on there.
97 + if (fileSourcePath.StartsWith("SourceDir\\", StringComparison.Ordinal))
98 + {
99 + fileSourcePath = fileSourcePath.Substring(10);
100 + }
101 +
102 + return fileSourcePath;
103 + }
104 + }
105 +}
src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs
+5 -6
@@ -5,14 +5,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 - using WixToolset.Data;
9 - using WixToolset.Msi;
10 - using WixToolset.Core.Native;
11 - using WixToolset.Bind;
8 + using System.Linq;
9 using WixToolset.Core.Bind;
10 + using WixToolset.Core.Native;
11 + using WixToolset.Data;
12 using WixToolset.Data.Bind;
13 using WixToolset.Data.Tuples;
15 - using System.Linq;
14 + using WixToolset.Msi;
15
16 /// <summary>
17 /// Defines the file transfers necessary to layout the uncompressed files.
@@ -100,7 +99,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
99 throw new WixException(ErrorMessages.FileIdentifierNotFound(facade.File.SourceLineNumbers, facade.File.File));
100 }
101
103 - relativeFileLayoutPath = Binder.GetFileSourcePath(directories, fileRecord[1], fileRecord[2], this.Compressed, this.LongNamesInImage);
102 + relativeFileLayoutPath = PathResolver.GetFileSourcePath(directories, fileRecord[1], fileRecord[2], this.Compressed, this.LongNamesInImage);
103 }
104
105 // finally put together the base media layout path and the relative file layout path
src/WixToolset.Core.WindowsInstaller/Bind/ResolvedDirectory.cs renamed
+2 -2
@@ -1,11 +1,11 @@
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.Bind
3 +namespace WixToolset.Core.WindowsInstaller.Bind
4 {
5 /// <summary>
6 /// Structure used for resolved directory information.
7 /// </summary>
8 - public struct ResolvedDirectory
8 + internal struct ResolvedDirectory
9 {
10 /// <summary>
11 /// Constructor for ResolvedDirectory.
src/WixToolset.Core.WindowsInstaller/MsiBackend.cs
+5 -3
@@ -12,9 +12,11 @@ namespace WixToolset.Core.WindowsInstaller
12
13 internal class MsiBackend : IBackend
14 {
15 - public BindResult Bind(IBindContext context)
15 + public BindResult Bind(WixToolset.Extensibility.IBindContext context)
16 {
17 - var backendExtensions = context.ExtensionManager.Create<IWindowsInstallerBackendExtension>();
17 + var extensionManager = context.ServiceProvider.GetService<IExtensionManager>();
18 +
19 + var backendExtensions = extensionManager.Create<IWindowsInstallerBackendExtension>();
20
21 foreach (var extension in backendExtensions)
22 {
@@ -26,7 +28,7 @@ namespace WixToolset.Core.WindowsInstaller
28 var command = new BindDatabaseCommand(context, backendExtensions, validator);
29 command.Execute();
30
29 - var result = new BindResult(command.FileTransfers, command.ContentFilePaths);
31 + var result = new BindResult { FileTransfers = command.FileTransfers, ContentFilePaths = command.ContentFilePaths };
32
33 foreach (var extension in backendExtensions)
34 {
src/WixToolset.Core.WindowsInstaller/MsmBackend.cs
+5 -3
@@ -11,9 +11,11 @@ namespace WixToolset.Core.WindowsInstaller
11
12 internal class MsmBackend : IBackend
13 {
14 - public BindResult Bind(IBindContext context)
14 + public BindResult Bind(WixToolset.Extensibility.IBindContext context)
15 {
16 - var backendExtensions = context.ExtensionManager.Create<IWindowsInstallerBackendExtension>();
16 + var extensionManager = context.ServiceProvider.GetService<IExtensionManager>();
17 +
18 + var backendExtensions = extensionManager.Create<IWindowsInstallerBackendExtension>();
19
20 foreach (var extension in backendExtensions)
21 {
@@ -25,7 +27,7 @@ namespace WixToolset.Core.WindowsInstaller
27 var command = new BindDatabaseCommand(context, backendExtensions, validator);
28 command.Execute();
29
28 - var result = new BindResult(command.FileTransfers, command.ContentFilePaths);
30 + var result = new BindResult { FileTransfers = command.FileTransfers, ContentFilePaths = command.ContentFilePaths };
31
32 foreach (var extension in backendExtensions)
33 {
src/WixToolset.Core.WindowsInstaller/MspBackend.cs
+1 -1
@@ -16,7 +16,7 @@ namespace WixToolset.Core.WindowsInstaller
16
17 internal class MspBackend : IBackend
18 {
19 - public BindResult Bind(IBindContext context)
19 + public BindResult Bind(WixToolset.Extensibility.IBindContext context)
20 {
21 throw new NotImplementedException();
22 }
src/WixToolset.Core.WindowsInstaller/MstBackend.cs
-1
@@ -7,7 +7,6 @@ namespace WixToolset.Core.WindowsInstaller
7 using WixToolset.Data;
8 using WixToolset.Data.Bind;
9 using WixToolset.Extensibility;
10 - using WixToolset.Extensibility.Services;
10
11 internal class MstBackend : IBackend
12 {
src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs
+1 -1
@@ -244,7 +244,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
244 private void GenerateDatabase(Output output, string databaseFile)
245 {
246 var command = new GenerateDatabaseCommand();
247 - command.Extensions = Array.Empty<IBinderExtension>();
247 + command.Extensions = Array.Empty<IFileSystemExtension>();
248 command.Output = output;
249 command.OutputPath = databaseFile;
250 command.KeepAddedColumns = true;
src/WixToolset.Core.WindowsInstaller/Validator.cs
+1 -1
@@ -324,7 +324,7 @@ namespace WixToolset.Core.WindowsInstaller
324 }
325 }
326
327 - public static Validator CreateFromContext(IBindContext context, string cubeFilename)
327 + public static Validator CreateFromContext(WixToolset.Extensibility.IBindContext context, string cubeFilename)
328 {
329 Validator validator = null;
330
src/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs
-1
@@ -5,7 +5,6 @@ namespace WixToolset.Core.WindowsInstaller
5 using System;
6 using System.IO;
7 using WixToolset.Extensibility;
8 - using WixToolset.Extensibility.Services;
8
9 internal class WindowsInstallerBackendFactory : IBackendFactory
10 {
src/WixToolset.Core/Bind/FileResolver.cs
+12 -80
@@ -6,7 +6,6 @@ namespace WixToolset.Core.Bind
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Linq;
9 - using System.Runtime.InteropServices;
9 using WixToolset.Data;
10 using WixToolset.Data.Bind;
11 using WixToolset.Extensibility;
@@ -22,9 +21,9 @@ namespace WixToolset.Core.Bind
21 this.RebaseUpdated = this.BindPaths[BindStage.Updated].Any();
22 }
23
25 - public FileResolver(IEnumerable<BindPath> bindPaths, IEnumerable<IBinderExtension> extensions) : this(bindPaths)
24 + public FileResolver(IEnumerable<BindPath> bindPaths, IEnumerable<IResolverExtension> extensions) : this(bindPaths)
25 {
27 - this.BinderExtensions = extensions ?? Array.Empty<IBinderExtension>();
26 + this.ResolverExtensions = extensions ?? Array.Empty<IResolverExtension>();
27 }
28
29 public FileResolver(IEnumerable<BindPath> bindPaths, IEnumerable<ILibrarianExtension> extensions) : this(bindPaths)
@@ -38,79 +37,15 @@ namespace WixToolset.Core.Bind
37
38 public bool RebaseUpdated { get; }
39
41 - private IEnumerable<IBinderExtension> BinderExtensions { get; }
40 + private IEnumerable<IResolverExtension> ResolverExtensions { get; }
41
42 private IEnumerable<ILibrarianExtension> LibrarianExtensions { get; }
43
45 - /// <summary>
46 - /// Copies a file.
47 - /// </summary>
48 - /// <param name="source">The file to copy.</param>
49 - /// <param name="destination">The destination file.</param>
50 - /// <param name="overwrite">true if the destination file can be overwritten; otherwise, false.</param>
51 - public bool CopyFile(string source, string destination, bool overwrite)
52 - {
53 - foreach (var extension in this.BinderExtensions)
54 - {
55 - if (extension.CopyFile(source, destination, overwrite))
56 - {
57 - return true;
58 - }
59 - }
60 -
61 - if (overwrite && File.Exists(destination))
62 - {
63 - File.Delete(destination);
64 - }
65 -
66 - if (!CreateHardLink(destination, source, IntPtr.Zero))
67 - {
68 -#if DEBUG
69 - int er = Marshal.GetLastWin32Error();
70 -#endif
71 -
72 - File.Copy(source, destination, overwrite);
73 - }
74 -
75 - return true;
76 - }
77 -
78 - /// <summary>
79 - /// Moves a file.
80 - /// </summary>
81 - /// <param name="source">The file to move.</param>
82 - /// <param name="destination">The destination file.</param>
83 - public bool MoveFile(string source, string destination, bool overwrite)
84 - {
85 - foreach (var extension in this.BinderExtensions)
86 - {
87 - if (extension.MoveFile(source, destination, overwrite))
88 - {
89 - return true;
90 - }
91 - }
92 -
93 - if (overwrite && File.Exists(destination))
94 - {
95 - File.Delete(destination);
96 - }
97 -
98 - var directory = Path.GetDirectoryName(destination);
99 - if (!String.IsNullOrEmpty(directory))
100 - {
101 - Directory.CreateDirectory(directory);
102 - }
103 -
104 - File.Move(source, destination);
105 -
106 - return true;
107 - }
108 -
109 - public string Resolve(SourceLineNumber sourceLineNumbers, string table, string path)
44 + public string Resolve(SourceLineNumber sourceLineNumbers, IntermediateTupleDefinition tupleDefinition, string source)
45 {
46 foreach (var extension in this.LibrarianExtensions)
47 {
113 - var resolved = extension.Resolve(sourceLineNumbers, table, path);
48 + var resolved = extension.Resolve(sourceLineNumbers, tupleDefinition, source);
49
50 if (null != resolved)
51 {
@@ -118,7 +53,7 @@ namespace WixToolset.Core.Bind
53 }
54 }
55
121 - return this.ResolveUsingBindPaths(path, table, sourceLineNumbers, BindStage.Normal);
56 + return this.ResolveUsingBindPaths(source, tupleDefinition, sourceLineNumbers, BindStage.Normal);
57 }
58
59 /// <summary>
@@ -129,11 +64,11 @@ namespace WixToolset.Core.Bind
64 /// <param name="sourceLineNumbers">Optional source line of source file being resolved.</param>
65 /// <param name="bindStage">The binding stage used to determine what collection of bind paths will be used</param>
66 /// <returns>Should return a valid path for the stream to be imported.</returns>
132 - public string ResolveFile(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage)
67 + public string ResolveFile(string source, IntermediateTupleDefinition tupleDefinition, SourceLineNumber sourceLineNumbers, BindStage bindStage)
68 {
134 - foreach (var extension in this.BinderExtensions)
69 + foreach (var extension in this.ResolverExtensions)
70 {
136 - var resolved = extension.ResolveFile(source, type, sourceLineNumbers, bindStage);
71 + var resolved = extension.ResolveFile(source, tupleDefinition, sourceLineNumbers, bindStage);
72
73 if (null != resolved)
74 {
@@ -141,10 +76,10 @@ namespace WixToolset.Core.Bind
76 }
77 }
78
144 - return this.ResolveUsingBindPaths(source, type, sourceLineNumbers, bindStage);
79 + return this.ResolveUsingBindPaths(source, tupleDefinition, sourceLineNumbers, bindStage);
80 }
81
147 - private string ResolveUsingBindPaths(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage)
82 + private string ResolveUsingBindPaths(string source, IntermediateTupleDefinition tupleDefinition, SourceLineNumber sourceLineNumbers, BindStage bindStage)
83 {
84 string resolved = null;
85
@@ -206,7 +141,7 @@ namespace WixToolset.Core.Bind
141
142 if (null == resolved)
143 {
209 - throw new WixFileNotFoundException(sourceLineNumbers, source, type);
144 + throw new WixFileNotFoundException(sourceLineNumbers, source, tupleDefinition.Name);
145 }
146
147 // Didn't find the file.
@@ -224,8 +159,5 @@ namespace WixToolset.Core.Bind
159 throw new WixException(ErrorMessages.IllegalCharactersInPath(path));
160 }
161 }
227 -
228 - [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
229 - private static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes);
162 }
163 }
src/WixToolset.Core/Bind/FileSystem.cs new
+87
@@ -0,0 +1,87 @@
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.Core.Bind
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using System.IO;
8 + using System.Runtime.InteropServices;
9 + using WixToolset.Extensibility;
10 +
11 + internal class FileSystem
12 + {
13 + public FileSystem(IEnumerable<IFileSystemExtension> extensions)
14 + {
15 + this.Extensions = extensions ?? Array.Empty<IFileSystemExtension>();
16 + }
17 +
18 + private IEnumerable<IFileSystemExtension> Extensions { get; }
19 +
20 + /// <summary>
21 + /// Copies a file.
22 + /// </summary>
23 + /// <param name="source">The file to copy.</param>
24 + /// <param name="destination">The destination file.</param>
25 + /// <param name="overwrite">true if the destination file can be overwritten; otherwise, false.</param>
26 + public bool CopyFile(string source, string destination, bool overwrite)
27 + {
28 + foreach (var extension in this.Extensions)
29 + {
30 + if (extension.CopyFile(source, destination, overwrite))
31 + {
32 + return true;
33 + }
34 + }
35 +
36 + if (overwrite && File.Exists(destination))
37 + {
38 + File.Delete(destination);
39 + }
40 +
41 + if (!CreateHardLink(destination, source, IntPtr.Zero))
42 + {
43 +#if DEBUG
44 + int er = Marshal.GetLastWin32Error();
45 +#endif
46 +
47 + File.Copy(source, destination, overwrite);
48 + }
49 +
50 + return true;
51 + }
52 +
53 + /// <summary>
54 + /// Moves a file.
55 + /// </summary>
56 + /// <param name="source">The file to move.</param>
57 + /// <param name="destination">The destination file.</param>
58 + public bool MoveFile(string source, string destination, bool overwrite)
59 + {
60 + foreach (var extension in this.Extensions)
61 + {
62 + if (extension.MoveFile(source, destination, overwrite))
63 + {
64 + return true;
65 + }
66 + }
67 +
68 + if (overwrite && File.Exists(destination))
69 + {
70 + File.Delete(destination);
71 + }
72 +
73 + var directory = Path.GetDirectoryName(destination);
74 + if (!String.IsNullOrEmpty(directory))
75 + {
76 + Directory.CreateDirectory(directory);
77 + }
78 +
79 + File.Move(source, destination);
80 +
81 + return true;
82 + }
83 +
84 + [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
85 + private static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes);
86 + }
87 +}
src/WixToolset.Core/Bind/ResolveFieldsCommand.cs
+9 -8
@@ -22,7 +22,7 @@ namespace WixToolset.Core.Bind
22
23 public IEnumerable<BindPath> BindPaths { private get; set; }
24
25 - public IEnumerable<IBinderExtension> Extensions { private get; set; }
25 + public IEnumerable<IResolverExtension> Extensions { private get; set; }
26
27 public ExtractEmbeddedFiles FilesWithEmbeddedFiles { private get; set; }
28
@@ -52,7 +52,6 @@ namespace WixToolset.Core.Bind
52 }
53
54 var isDefault = true;
55 - var delayedResolve = false;
55
56 // Check to make sure we're in a scenario where we can handle variable resolution.
57 if (null != delayedFields)
@@ -63,16 +62,18 @@ namespace WixToolset.Core.Bind
62 var original = field.AsString();
63 if (!String.IsNullOrEmpty(original))
64 {
66 - var value = this.BindVariableResolver.ResolveVariables(row.SourceLineNumbers, original, false, out isDefault, out delayedResolve);
67 - if (original != value)
65 + var resolution = this.BindVariableResolver.ResolveVariables(row.SourceLineNumbers, original, false);
66 + if (resolution.UpdatedValue)
67 {
69 - field.Set(value);
68 + field.Set(resolution.Value);
69 }
70
72 - if (delayedResolve)
71 + if (resolution.DelayedResolve)
72 {
73 delayedFields.Add(new DelayedField(row, field));
74 }
75 +
76 + isDefault = resolution.IsDefault;
77 }
78 }
79 }
@@ -119,13 +120,13 @@ namespace WixToolset.Core.Bind
120 #endif
121
122 // resolve the path to the file
122 - var value = fileResolver.ResolveFile(objectField.Path, row.Definition.Name, row.SourceLineNumbers, BindStage.Normal);
123 + var value = fileResolver.ResolveFile(objectField.Path, row.Definition, row.SourceLineNumbers, BindStage.Normal);
124 field.Set(value);
125 }
126 else if (!fileResolver.RebaseTarget && !fileResolver.RebaseUpdated) // Normal binding for Patch Scenario (normal patch, no re-basing logic)
127 {
128 // resolve the path to the file
128 - var value = fileResolver.ResolveFile(objectField.Path, row.Definition.Name, row.SourceLineNumbers, BindStage.Normal);
129 + var value = fileResolver.ResolveFile(objectField.Path, row.Definition, row.SourceLineNumbers, BindStage.Normal);
130 field.Set(value);
131 }
132 #if REVISIT_FOR_PATCHING
src/WixToolset.Core/Bind/ResolveResult.cs deleted
-14
@@ -1,14 +0,0 @@
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.Core.Bind
4 -{
5 - using System.Collections.Generic;
6 - using WixToolset.Extensibility;
7 -
8 - public class ResolveResult
9 - {
10 - public IEnumerable<IExpectedExtractFile> ExpectedEmbeddedFiles { get; set; }
11 -
12 - public IEnumerable<IDelayedField> DelayedFields { get; set; }
13 - }
14 -}
\ No newline at end of file
src/WixToolset.Core/Bind/TransferFilesCommand.cs
+10 -12
@@ -13,15 +13,15 @@ namespace WixToolset.Core.Bind
13
14 internal class TransferFilesCommand
15 {
16 - public TransferFilesCommand(IMessaging messaging, IEnumerable<BindPath> bindPaths, IEnumerable<IBinderExtension> extensions, IEnumerable<FileTransfer> fileTransfers, bool suppressAclReset)
16 + public TransferFilesCommand(IMessaging messaging, IEnumerable<IFileSystemExtension> extensions, IEnumerable<FileTransfer> fileTransfers, bool suppressAclReset)
17 {
18 - this.FileResolver = new FileResolver(bindPaths, extensions);
18 + this.FileSystem = new FileSystem(extensions);
19 this.Messaging = messaging;
20 this.FileTransfers = fileTransfers;
21 this.SuppressAclReset = suppressAclReset;
22 }
23
24 - private FileResolver FileResolver { get; }
24 + private FileSystem FileSystem { get; }
25
26 private IMessaging Messaging { get; }
27
@@ -35,10 +35,8 @@ namespace WixToolset.Core.Bind
35
36 foreach (var fileTransfer in this.FileTransfers)
37 {
38 - string fileSource = this.FileResolver.ResolveFile(fileTransfer.Source, fileTransfer.Type, fileTransfer.SourceLineNumbers, BindStage.Normal);
39 -
38 // If the source and destination are identical, then there's nothing to do here
41 - if (0 == String.Compare(fileSource, fileTransfer.Destination, StringComparison.OrdinalIgnoreCase))
39 + if (0 == String.Compare(fileTransfer.Source, fileTransfer.Destination, StringComparison.OrdinalIgnoreCase))
40 {
41 fileTransfer.Redundant = true;
42 continue;
@@ -51,13 +49,13 @@ namespace WixToolset.Core.Bind
49 {
50 if (fileTransfer.Move)
51 {
54 - this.Messaging.Write(VerboseMessages.MoveFile(fileSource, fileTransfer.Destination));
55 - this.TransferFile(true, fileSource, fileTransfer.Destination);
52 + this.Messaging.Write(VerboseMessages.MoveFile(fileTransfer.Source, fileTransfer.Destination));
53 + this.TransferFile(true, fileTransfer.Source, fileTransfer.Destination);
54 }
55 else
56 {
59 - this.Messaging.Write(VerboseMessages.CopyFile(fileSource, fileTransfer.Destination));
60 - this.TransferFile(false, fileSource, fileTransfer.Destination);
57 + this.Messaging.Write(VerboseMessages.CopyFile(fileTransfer.Source, fileTransfer.Destination));
58 + this.TransferFile(false, fileTransfer.Source, fileTransfer.Destination);
59 }
60
61 retry = false;
@@ -183,11 +181,11 @@ namespace WixToolset.Core.Bind
181
182 if (move)
183 {
186 - complete = this.FileResolver.MoveFile(source, destination, true);
184 + complete = this.FileSystem.MoveFile(source, destination, true);
185 }
186 else
187 {
190 - complete = this.FileResolver.CopyFile(source, destination, true);
188 + complete = this.FileSystem.CopyFile(source, destination, true);
189 }
190
191 if (!complete)
src/WixToolset.Core/BindContext.cs
+1 -3
@@ -35,7 +35,7 @@ namespace WixToolset.Core
35
36 public IExtensionManager ExtensionManager { get; set; }
37
38 - public IEnumerable<IBinderExtension> Extensions { get; set; }
38 + public IEnumerable<IFileSystemExtension> FileSystemExtensions { get; set; }
39
40 public IEnumerable<string> Ices { get; set; }
41
@@ -47,8 +47,6 @@ namespace WixToolset.Core
47
48 public string OutputPdbPath { get; set; }
49
50 - public bool SuppressAclReset { get; set; }
51 -
50 public IEnumerable<string> SuppressIces { get; set; }
51
52 public bool SuppressValidation { get; set; }
src/WixToolset.Core/Binder.cs
+55 -396
@@ -3,14 +3,11 @@
3 namespace WixToolset.Core
4 {
5 using System;
6 - using System.Collections;
6 using System.Collections.Generic;
7 using System.Diagnostics;
9 - using System.Globalization;
8 using System.IO;
9 using System.Linq;
10 using System.Reflection;
13 - using WixToolset.Bind;
11 using WixToolset.Core.Bind;
12 using WixToolset.Data;
13 using WixToolset.Data.Bind;
@@ -148,130 +145,72 @@ namespace WixToolset.Core
145 /// <value>The Wix variable resolver.</value>
146 internal WixVariableResolver WixVariableResolver { get; set; }
147
151 - /// <summary>
152 - /// Add a binder extension.
153 - /// </summary>
154 - /// <param name="extension">New extension.</param>
155 - //public void AddExtension(IBinderExtension extension)
156 - //{
157 - // this.extensions.Add(extension);
158 - //}
159 -
160 - /// <summary>
161 - /// Add a file manager extension.
162 - /// </summary>
163 - /// <param name="extension">New file manager.</param>
164 - //public void AddExtension(IBinderFileManager extension)
165 - //{
166 - // this.fileManagers.Add(extension);
167 - //}
168 -
169 - public bool Bind(IBindContext context)
148 + public BindResult Bind(IBindContext context)
149 {
150 this.Context = context;
151
173 - //if (!String.IsNullOrEmpty(this.Context.FileManagerCore.CabCachePath))
174 - //{
175 - // Directory.CreateDirectory(this.Context.FileManagerCore.CabCachePath);
176 - //}
177 -
178 - //this.core = new BinderCore();
179 - //this.core.FileManagerCore = this.Context.FileManagerCore;
180 -
152 this.WriteBuildInfoTable(this.Context.IntermediateRepresentation, this.Context.OutputPath);
153
183 - // Prebind.
184 - //
185 - this.Context.Extensions = this.Context.ExtensionManager.Create<IBinderExtension>();
186 -
187 - foreach (IBinderExtension extension in this.Context.Extensions)
188 - {
189 - extension.PreBind(this.Context);
190 - }
191 -
192 - // Resolve.
193 - //
194 - var resolveResult = this.Resolve();
195 -
196 - this.Context.DelayedFields = resolveResult.DelayedFields;
197 -
198 - this.Context.ExpectedEmbeddedFiles = resolveResult.ExpectedEmbeddedFiles;
199 -
200 - // Backend.
201 - //
154 var bindResult = this.BackendBind();
203 -
204 - if (bindResult != null)
205 - {
206 - // Postbind.
207 - //
208 - foreach (IBinderExtension extension in this.Context.Extensions)
209 - {
210 - extension.PostBind(bindResult);
211 - }
212 -
213 - // Layout.
214 - //
215 - this.Layout(bindResult);
216 - }
217 -
218 - return this.Context.Messaging.EncounteredError;
155 + return bindResult;
156 }
157
221 - private ResolveResult Resolve()
222 - {
223 - var buildingPatch = this.Context.IntermediateRepresentation.Sections.Any(s => s.Type == SectionType.Patch);
224 -
225 - var filesWithEmbeddedFiles = new ExtractEmbeddedFiles();
226 -
227 - IEnumerable<DelayedField> delayedFields;
228 - {
229 - var command = new ResolveFieldsCommand();
230 - command.Messaging = this.Context.Messaging;
231 - command.BuildingPatch = buildingPatch;
232 - command.BindVariableResolver = this.Context.WixVariableResolver;
233 - command.BindPaths = this.Context.BindPaths;
234 - command.Extensions = this.Context.Extensions;
235 - command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
236 - command.IntermediateFolder = this.Context.IntermediateFolder;
237 - command.Intermediate = this.Context.IntermediateRepresentation;
238 - command.SupportDelayedResolution = true;
239 - command.Execute();
240 -
241 - delayedFields = command.DelayedFields;
242 - }
243 -
244 -#if REVISIT_FOR_PATCHING
245 - if (this.Context.IntermediateRepresentation.SubStorages != null)
246 - {
247 - foreach (SubStorage transform in this.Context.IntermediateRepresentation.SubStorages)
248 - {
249 - var command = new ResolveFieldsCommand();
250 - command.BuildingPatch = buildingPatch;
251 - command.BindVariableResolver = this.Context.WixVariableResolver;
252 - command.BindPaths = this.Context.BindPaths;
253 - command.Extensions = this.Context.Extensions;
254 - command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
255 - command.IntermediateFolder = this.Context.IntermediateFolder;
256 - command.Intermediate = this.Context.IntermediateRepresentation;
257 - command.SupportDelayedResolution = false;
258 - command.Execute();
259 - }
260 - }
261 -#endif
262 -
263 - var expectedEmbeddedFiles = filesWithEmbeddedFiles.GetExpectedEmbeddedFiles();
264 -
265 - return new ResolveResult
266 - {
267 - ExpectedEmbeddedFiles = expectedEmbeddedFiles,
268 - DelayedFields = delayedFields,
269 - };
270 - }
158 +//// private ResolveResult Resolve()
159 +//// {
160 +//// var buildingPatch = this.Context.IntermediateRepresentation.Sections.Any(s => s.Type == SectionType.Patch);
161 +
162 +//// var filesWithEmbeddedFiles = new ExtractEmbeddedFiles();
163 +
164 +//// IEnumerable<DelayedField> delayedFields;
165 +//// {
166 +//// var command = new ResolveFieldsCommand();
167 +//// command.Messaging = this.Context.Messaging;
168 +//// command.BuildingPatch = buildingPatch;
169 +//// command.BindVariableResolver = this.Context.WixVariableResolver;
170 +//// command.BindPaths = this.Context.BindPaths;
171 +//// command.Extensions = this.Context.Extensions;
172 +//// command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
173 +//// command.IntermediateFolder = this.Context.IntermediateFolder;
174 +//// command.Intermediate = this.Context.IntermediateRepresentation;
175 +//// command.SupportDelayedResolution = true;
176 +//// command.Execute();
177 +
178 +//// delayedFields = command.DelayedFields;
179 +//// }
180 +
181 +////#if REVISIT_FOR_PATCHING
182 +//// if (this.Context.IntermediateRepresentation.SubStorages != null)
183 +//// {
184 +//// foreach (SubStorage transform in this.Context.IntermediateRepresentation.SubStorages)
185 +//// {
186 +//// var command = new ResolveFieldsCommand();
187 +//// command.BuildingPatch = buildingPatch;
188 +//// command.BindVariableResolver = this.Context.WixVariableResolver;
189 +//// command.BindPaths = this.Context.BindPaths;
190 +//// command.Extensions = this.Context.Extensions;
191 +//// command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
192 +//// command.IntermediateFolder = this.Context.IntermediateFolder;
193 +//// command.Intermediate = this.Context.IntermediateRepresentation;
194 +//// command.SupportDelayedResolution = false;
195 +//// command.Execute();
196 +//// }
197 +//// }
198 +////#endif
199 +
200 +//// var expectedEmbeddedFiles = filesWithEmbeddedFiles.GetExpectedEmbeddedFiles();
201 +
202 +//// return new ResolveResult
203 +//// {
204 +//// ExpectedEmbeddedFiles = expectedEmbeddedFiles,
205 +//// DelayedFields = delayedFields,
206 +//// };
207 +//// }
208
209 private BindResult BackendBind()
210 {
274 - var backendFactories = this.Context.ExtensionManager.Create<IBackendFactory>();
211 + var extensionManager = this.Context.ServiceProvider.GetService<IExtensionManager>();
212 +
213 + var backendFactories = extensionManager.Create<IBackendFactory>();
214
215 var entrySection = this.Context.IntermediateRepresentation.Sections[0];
216
@@ -289,31 +228,6 @@ namespace WixToolset.Core
228 return null;
229 }
230
292 - private void Layout(BindResult result)
293 - {
294 - try
295 - {
296 - this.LayoutMedia(result.FileTransfers);
297 - }
298 - finally
299 - {
300 - if (!String.IsNullOrEmpty(this.Context.ContentsFile) && result.ContentFilePaths != null)
301 - {
302 - this.CreateContentsFile(this.Context.ContentsFile, result.ContentFilePaths);
303 - }
304 -
305 - if (!String.IsNullOrEmpty(this.Context.OutputsFile) && result.FileTransfers != null)
306 - {
307 - this.CreateOutputsFile(this.Context.OutputsFile, result.FileTransfers, this.Context.OutputPdbPath);
308 - }
309 -
310 - if (!String.IsNullOrEmpty(this.Context.BuiltOutputsFile) && result.FileTransfers != null)
311 - {
312 - this.CreateBuiltOutputsFile(this.Context.BuiltOutputsFile, result.FileTransfers, this.Context.OutputPdbPath);
313 - }
314 - }
315 - }
316 -
231 /// <summary>
232 /// Binds an output.
233 /// </summary>
@@ -428,35 +342,6 @@ namespace WixToolset.Core
342 }
343 #endif
344
431 - /// <summary>
432 - /// Does any housekeeping after Bind.
433 - /// </summary>
434 - /// <param name="tidy">Whether or not any actual tidying should be done.</param>
435 - public void Cleanup(bool tidy)
436 - {
437 - if (tidy)
438 - {
439 - if (!this.DeleteTempFiles())
440 - {
441 - this.Context.Messaging.Write(WarningMessages.FailedToDeleteTempDir(this.TempFilesLocation));
442 - }
443 - }
444 - else
445 - {
446 - this.Context.Messaging.Write(VerboseMessages.BinderTempDirLocatedAt(this.TempFilesLocation));
447 - }
448 - }
449 -
450 - /// <summary>
451 - /// Cleans up the temp files used by the Binder.
452 - /// </summary>
453 - /// <returns>True if all files were deleted, false otherwise.</returns>
454 - private bool DeleteTempFiles()
455 - {
456 - bool deleted = Common.DeleteTempFiles(this.TempFilesLocation, this.Context.Messaging);
457 - return deleted;
458 - }
459 -
345 /// <summary>
346 /// Populates the WixBuildInfo table in an output.
347 /// </summary>
@@ -585,231 +470,5 @@ namespace WixToolset.Core
470 command.Execute();
471 }
472 #endif
588 -
589 - /// <summary>
590 - /// Final step in binding that transfers (moves/copies) all files generated into the appropriate
591 - /// location in the source image
592 - /// </summary>
593 - /// <param name="fileTransfers">List of files to transfer.</param>
594 - private void LayoutMedia(IEnumerable<FileTransfer> transfers)
595 - {
596 - if (null != transfers && transfers.Any())
597 - {
598 - this.Context.Messaging.Write(VerboseMessages.LayingOutMedia());
599 -
600 - var command = new TransferFilesCommand(this.Context.Messaging, this.Context.BindPaths, this.Context.Extensions, transfers, this.Context.SuppressAclReset);
601 - command.Execute();
602 - }
603 - }
604 -
605 - /// <summary>
606 - /// Get the source path of a directory.
607 - /// </summary>
608 - /// <param name="directories">All cached directories.</param>
609 - /// <param name="componentIdGenSeeds">Hash table of Component GUID generation seeds indexed by directory id.</param>
610 - /// <param name="directory">Directory identifier.</param>
611 - /// <param name="canonicalize">Canonicalize the path for standard directories.</param>
612 - /// <returns>Source path of a directory.</returns>
613 - public static string GetDirectoryPath(Dictionary<string, ResolvedDirectory> directories, Dictionary<string, string> componentIdGenSeeds, string directory, bool canonicalize)
614 - {
615 - if (!directories.ContainsKey(directory))
616 - {
617 - throw new WixException(ErrorMessages.ExpectedDirectory(directory));
618 - }
619 -
620 - ResolvedDirectory resolvedDirectory = (ResolvedDirectory)directories[directory];
621 -
622 - if (null == resolvedDirectory.Path)
623 - {
624 - if (null != componentIdGenSeeds && componentIdGenSeeds.ContainsKey(directory))
625 - {
626 - resolvedDirectory.Path = (string)componentIdGenSeeds[directory];
627 - }
628 - else if (canonicalize && WindowsInstallerStandard.IsStandardDirectory(directory))
629 - {
630 - // when canonicalization is on, standard directories are treated equally
631 - resolvedDirectory.Path = directory;
632 - }
633 - else
634 - {
635 - string name = resolvedDirectory.Name;
636 -
637 - if (canonicalize && null != name)
638 - {
639 - name = name.ToLower(CultureInfo.InvariantCulture);
640 - }
641 -
642 - if (String.IsNullOrEmpty(resolvedDirectory.DirectoryParent))
643 - {
644 - resolvedDirectory.Path = name;
645 - }
646 - else
647 - {
648 - string parentPath = GetDirectoryPath(directories, componentIdGenSeeds, resolvedDirectory.DirectoryParent, canonicalize);
649 -
650 - if (null != resolvedDirectory.Name)
651 - {
652 - resolvedDirectory.Path = Path.Combine(parentPath, name);
653 - }
654 - else
655 - {
656 - resolvedDirectory.Path = parentPath;
657 - }
658 - }
659 - }
660 - }
661 -
662 - return resolvedDirectory.Path;
663 - }
664 -
665 - /// <summary>
666 - /// Gets the source path of a file.
667 - /// </summary>
668 - /// <param name="directories">All cached directories in <see cref="ResolvedDirectory"/>.</param>
669 - /// <param name="directoryId">Parent directory identifier.</param>
670 - /// <param name="fileName">File name (in long|source format).</param>
671 - /// <param name="compressed">Specifies the package is compressed.</param>
672 - /// <param name="useLongName">Specifies the package uses long file names.</param>
673 - /// <returns>Source path of file relative to package directory.</returns>
674 - public static string GetFileSourcePath(Dictionary<string, ResolvedDirectory> directories, string directoryId, string fileName, bool compressed, bool useLongName)
675 - {
676 - string fileSourcePath = Common.GetName(fileName, true, useLongName);
677 -
678 - if (compressed)
679 - {
680 - // Use just the file name of the file since all uncompressed files must appear
681 - // in the root of the image in a compressed package.
682 - }
683 - else
684 - {
685 - // Get the relative path of where we want the file to be layed out as specified
686 - // in the Directory table.
687 - string directoryPath = Binder.GetDirectoryPath(directories, null, directoryId, false);
688 - fileSourcePath = Path.Combine(directoryPath, fileSourcePath);
689 - }
690 -
691 - // Strip off "SourceDir" if it's still on there.
692 - if (fileSourcePath.StartsWith("SourceDir\\", StringComparison.Ordinal))
693 - {
694 - fileSourcePath = fileSourcePath.Substring(10);
695 - }
696 -
697 - return fileSourcePath;
698 - }
699 -
700 - /// <summary>
701 - /// Writes the paths to the content files included in the package to a text file.
702 - /// </summary>
703 - /// <param name="path">Path to write file.</param>
704 - /// <param name="contentFilePaths">Collection of paths to content files that will be written to file.</param>
705 - private void CreateContentsFile(string path, IEnumerable<string> contentFilePaths)
706 - {
707 - string directory = Path.GetDirectoryName(path);
708 - if (!Directory.Exists(directory))
709 - {
710 - Directory.CreateDirectory(directory);
711 - }
712 -
713 - using (StreamWriter contents = new StreamWriter(path, false))
714 - {
715 - foreach (string contentPath in contentFilePaths)
716 - {
717 - contents.WriteLine(contentPath);
718 - }
719 - }
720 - }
721 -
722 - /// <summary>
723 - /// Writes the paths to the content files included in the bundle to a text file.
724 - /// </summary>
725 - /// <param name="path">Path to write file.</param>
726 - /// <param name="payloads">Collection of payloads whose source will be written to file.</param>
727 - private void CreateContentsFile(string path, IEnumerable<WixBundlePayloadTuple> payloads)
728 - {
729 - string directory = Path.GetDirectoryName(path);
730 - if (!Directory.Exists(directory))
731 - {
732 - Directory.CreateDirectory(directory);
733 - }
734 -
735 - using (StreamWriter contents = new StreamWriter(path, false))
736 - {
737 - foreach (var payload in payloads)
738 - {
739 - if (payload.ContentFile)
740 - {
741 - var fullPath = Path.GetFullPath(payload.SourceFile);
742 - contents.WriteLine(fullPath);
743 - }
744 - }
745 - }
746 - }
747 -
748 - /// <summary>
749 - /// Writes the paths to the output files to a text file.
750 - /// </summary>
751 - /// <param name="path">Path to write file.</param>
752 - /// <param name="fileTransfers">Collection of files that were transferred to the output directory.</param>
753 - /// <param name="pdbPath">Optional path to created .wixpdb.</param>
754 - private void CreateOutputsFile(string path, IEnumerable<FileTransfer> fileTransfers, string pdbPath)
755 - {
756 - string directory = Path.GetDirectoryName(path);
757 - if (!Directory.Exists(directory))
758 - {
759 - Directory.CreateDirectory(directory);
760 - }
761 -
762 - using (StreamWriter outputs = new StreamWriter(path, false))
763 - {
764 - foreach (FileTransfer fileTransfer in fileTransfers)
765 - {
766 - // Don't list files where the source is the same as the destination since
767 - // that might be the only place the file exists. The outputs file is often
768 - // used to delete stuff and losing the original source would be bad.
769 - if (!fileTransfer.Redundant)
770 - {
771 - outputs.WriteLine(fileTransfer.Destination);
772 - }
773 - }
774 -
775 - if (!String.IsNullOrEmpty(pdbPath))
776 - {
777 - outputs.WriteLine(Path.GetFullPath(pdbPath));
778 - }
779 - }
780 - }
781 -
782 - /// <summary>
783 - /// Writes the paths to the built output files to a text file.
784 - /// </summary>
785 - /// <param name="path">Path to write file.</param>
786 - /// <param name="fileTransfers">Collection of files that were transferred to the output directory.</param>
787 - /// <param name="pdbPath">Optional path to created .wixpdb.</param>
788 - private void CreateBuiltOutputsFile(string path, IEnumerable<FileTransfer> fileTransfers, string pdbPath)
789 - {
790 - string directory = Path.GetDirectoryName(path);
791 - if (!Directory.Exists(directory))
792 - {
793 - Directory.CreateDirectory(directory);
794 - }
795 -
796 - using (StreamWriter outputs = new StreamWriter(path, false))
797 - {
798 - foreach (FileTransfer fileTransfer in fileTransfers)
799 - {
800 - // Only write the built file transfers. Also, skip redundant
801 - // files for the same reason spelled out in this.CreateOutputsFile().
802 - if (fileTransfer.Built && !fileTransfer.Redundant)
803 - {
804 - outputs.WriteLine(fileTransfer.Destination);
805 - }
806 - }
807 -
808 - if (!String.IsNullOrEmpty(pdbPath))
809 - {
810 - outputs.WriteLine(Path.GetFullPath(pdbPath));
811 - }
812 - }
813 - }
473 }
474 }
src/WixToolset.Core/BinderFileManager.cs
+1 -1
@@ -12,7 +12,7 @@ namespace WixToolset
12 using WixToolset.Data.Rows;
13 using WixToolset.Extensibility;
14
15 -#if false
15 +#if DEAD_CODE
16 /// <summary>
17 /// Base class for creating a binder file manager.
18 /// </summary>
src/WixToolset.Core/BinderFileManagerCore.cs
+2
@@ -9,6 +9,7 @@ namespace WixToolset
9 using WixToolset.Data.Bind;
10 using WixToolset.Extensibility;
11
12 +#if DEAD_CODE
13 public class BinderFileManagerCore : IBinderFileManagerCore
14 {
15 private Dictionary<string, List<string>>[] bindPaths;
@@ -97,4 +98,5 @@ namespace WixToolset
98 return Enumerable.Empty<string>();
99 }
100 }
101 +#endif
102 }
src/WixToolset.Core/CommandLine/BuildCommand.cs
+69 -53
@@ -7,7 +7,7 @@ namespace WixToolset.Core
7 using System.IO;
8 using System.Linq;
9 using WixToolset.Data;
10 - using WixToolset.Data.Tuples;
10 + using WixToolset.Data.Bind;
11 using WixToolset.Extensibility;
12 using WixToolset.Extensibility.Services;
13
@@ -90,6 +90,12 @@ namespace WixToolset.Core
90
91 library?.Save(this.OutputPath);
92 }
93 + else if (this.OutputType == OutputType.Wixout)
94 + {
95 + var output = this.LinkPhase(intermediates);
96 +
97 + output?.Save(this.OutputPath);
98 + }
99 else
100 {
101 var output = this.LinkPhase(intermediates);
@@ -147,22 +153,20 @@ namespace WixToolset.Core
153 {
154 var localizations = this.LoadLocalizationFiles().ToList();
155
150 - // If there was an error adding localization files, then bail.
156 + // If there was an error loading localization files, then bail.
157 if (this.Messaging.EncounteredError)
158 {
159 return null;
160 }
161
156 - var resolver = CreateWixResolverWithVariables(null, null);
157 -
162 var context = new LibraryContext();
163 + context.Messaging = this.Messaging;
164 context.BindFiles = this.BindFiles;
165 context.BindPaths = this.BindPaths;
166 context.Extensions = this.ExtensionManager.Create<ILibrarianExtension>();
167 context.Localizations = localizations;
168 context.LibraryId = Guid.NewGuid().ToString("N");
169 context.Intermediates = intermediates;
165 - context.WixVariableResolver = resolver;
170
171 var librarian = new Librarian();
172 return librarian.Combine(context);
@@ -174,6 +178,11 @@ namespace WixToolset.Core
178
179 var libraries = this.LoadLibraries(creator);
180
181 + if (this.Messaging.EncounteredError)
182 + {
183 + return null;
184 + }
185 +
186 var context = this.ServiceProvider.GetService<ILinkContext>();
187 context.Messaging = this.Messaging;
188 context.Extensions = this.ExtensionManager.Create<ILinkerExtension>();
@@ -183,48 +192,72 @@ namespace WixToolset.Core
192 context.TupleDefinitionCreator = creator;
193
194 var linker = new Linker();
186 - var output = linker.Link(context);
187 - return output;
195 + return linker.Link(context);
196 }
197
198 private void BindPhase(Intermediate output)
199 {
200 var localizations = this.LoadLocalizationFiles().ToList();
201
194 - var localizer = new Localizer(this.Messaging, localizations);
202 + // If there was an error loading localization files, then bail.
203 + if (this.Messaging.EncounteredError)
204 + {
205 + return;
206 + }
207
196 - var resolver = CreateWixResolverWithVariables(localizer, output);
208 + ResolveResult resolveResult;
209 + {
210 + var resolver = new Resolver(this.ServiceProvider, this.BindPaths, output, this.IntermediateFolder, localizations);
211 + resolveResult = resolver.Execute();
212 + }
213
198 - var intermediateFolder = this.IntermediateFolder;
199 - if (String.IsNullOrEmpty(intermediateFolder))
214 + if (this.Messaging.EncounteredError)
215 {
201 - intermediateFolder = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
216 + return;
217 }
218
204 - var context = this.ServiceProvider.GetService<IBindContext>();
205 - context.Messaging = this.Messaging;
206 - context.ExtensionManager = this.ExtensionManager;
207 - context.BindPaths = this.BindPaths ?? Array.Empty<BindPath>();
208 - //context.CabbingThreadCount = this.CabbingThreadCount;
209 - context.CabCachePath = this.CabCachePath;
210 - context.Codepage = localizer.Codepage;
211 - //context.DefaultCompressionLevel = this.DefaultCompressionLevel;
212 - //context.Ices = this.Ices;
213 - context.IntermediateFolder = intermediateFolder;
214 - context.IntermediateRepresentation = output;
215 - context.OutputPath = this.OutputPath;
216 - context.OutputPdbPath = Path.ChangeExtension(this.OutputPath, ".wixpdb");
217 - //context.SuppressIces = this.SuppressIces;
218 - context.SuppressValidation = true;
219 - //context.SuppressValidation = this.SuppressValidation;
220 - context.WixVariableResolver = resolver;
221 - context.ContentsFile = this.ContentsFile;
222 - context.OutputsFile = this.OutputsFile;
223 - context.BuiltOutputsFile = this.BuiltOutputsFile;
224 - context.WixprojectFile = this.WixProjectFile;
225 -
226 - var binder = new Binder();
227 - binder.Bind(context);
219 + BindResult bindResult;
220 + {
221 + var intermediateFolder = this.IntermediateFolder;
222 + if (String.IsNullOrEmpty(intermediateFolder))
223 + {
224 + intermediateFolder = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
225 + }
226 +
227 + var context = this.ServiceProvider.GetService<IBindContext>();
228 + context.Messaging = this.Messaging;
229 + //context.CabbingThreadCount = this.CabbingThreadCount;
230 + context.CabCachePath = this.CabCachePath;
231 + context.Codepage = resolveResult.Codepage;
232 + //context.DefaultCompressionLevel = this.DefaultCompressionLevel;
233 + context.DelayedFields = resolveResult.DelayedFields;
234 + context.ExpectedEmbeddedFiles = resolveResult.ExpectedEmbeddedFiles;
235 + //context.Ices = this.Ices;
236 + context.IntermediateFolder = intermediateFolder;
237 + context.IntermediateRepresentation = resolveResult.IntermediateRepresentation;
238 + context.OutputPath = this.OutputPath;
239 + context.OutputPdbPath = Path.ChangeExtension(this.OutputPath, ".wixpdb");
240 + //context.SuppressIces = this.SuppressIces;
241 + context.SuppressValidation = true; // TODO: set this correctly
242 + context.ContentsFile = this.ContentsFile;
243 + context.OutputsFile = this.OutputsFile;
244 + context.BuiltOutputsFile = this.BuiltOutputsFile;
245 + context.WixprojectFile = this.WixProjectFile;
246 +
247 + var binder = new Binder();
248 + bindResult = binder.Bind(context);
249 + }
250 +
251 + if (this.Messaging.EncounteredError)
252 + {
253 + return;
254 + }
255 +
256 + {
257 + // TODO: correctly set SuppressAclReset bool at the end.
258 + var layout = new Layout(this.ServiceProvider, bindResult.FileTransfers, bindResult.ContentFilePaths, this.ContentsFile, this.OutputsFile, this.BuiltOutputsFile, false);
259 + layout.Execute();
260 + }
261 }
262
263 private IEnumerable<Intermediate> LoadLibraries(ITupleDefinitionCreator creator)
@@ -264,22 +297,5 @@ namespace WixToolset.Core
297 yield return localization;
298 }
299 }
267 -
268 - private WixVariableResolver CreateWixResolverWithVariables(Localizer localizer, Intermediate output)
269 - {
270 - var resolver = new WixVariableResolver(this.Messaging, localizer);
271 -
272 - // Gather all the wix variables.
273 - var wixVariables = output?.Sections.SelectMany(s => s.Tuples).OfType<WixVariableTuple>();
274 - if (wixVariables != null)
275 - {
276 - foreach (var wixVariableRow in wixVariables)
277 - {
278 - resolver.AddVariable(wixVariableRow);
279 - }
280 - }
281 -
282 - return resolver;
283 - }
300 }
301 }
src/WixToolset.Core/CommandLine/CommandLine.cs
+4
@@ -260,6 +260,10 @@ namespace WixToolset.Core
260 case "transform":
261 case ".mst":
262 return OutputType.Transform;
263 +
264 + case "wixout":
265 + case ".wixout":
266 + return OutputType.Wixout;
267 }
268
269 return OutputType.Unknown;
src/WixToolset.Core/Compiler.cs
+29 -24
@@ -154,24 +154,40 @@ namespace WixToolset.Core
154 }
155
156 // Resolve any Component Id placeholders compiled into the intermediate.
157 - if (0 < this.componentIdPlaceholdersResolver.VariableCount)
157 + this.ResolveComponentIdPlaceholders(target);
158 + }
159 + finally
160 + {
161 + foreach (var extension in context.Extensions)
162 {
159 - foreach (var section in target.Sections)
163 + extension.PostCompile(target);
164 + }
165 +
166 + this.Core = null;
167 + }
168 +
169 + return this.Context.Messaging.EncounteredError ? null : target;
170 + }
171 +
172 + private void ResolveComponentIdPlaceholders(Intermediate target)
173 + {
174 + if (0 < this.componentIdPlaceholdersResolver.VariableCount)
175 + {
176 + foreach (var section in target.Sections)
177 + {
178 + foreach (var tuple in section.Tuples)
179 {
161 - foreach (var tuple in section.Tuples)
180 + foreach (var field in tuple.Fields)
181 {
163 - foreach (var field in tuple.Fields)
182 + if (field?.Type == IntermediateFieldType.String)
183 {
165 - if (field != null && field.Type == IntermediateFieldType.String)
184 + var data = field.AsString();
185 + if (!String.IsNullOrEmpty(data))
186 {
167 - var data = field.AsString();
168 - if (!String.IsNullOrEmpty(data))
187 + var resolved = this.componentIdPlaceholdersResolver.ResolveVariables(tuple.SourceLineNumbers, data, false, false);
188 + if (resolved.UpdatedValue)
189 {
170 - var resolved = this.componentIdPlaceholdersResolver.ResolveVariables(tuple.SourceLineNumbers, data, false, false, out var defaultIgnored, out var delayedIgnored);
171 - if (data != resolved)
172 - {
173 - field.Set(resolved);
174 - }
190 + field.Set(resolved.Value);
191 }
192 }
193 }
@@ -179,17 +195,6 @@ namespace WixToolset.Core
195 }
196 }
197 }
182 - finally
183 - {
184 - foreach (var extension in context.Extensions)
185 - {
186 - extension.PostCompile(target);
187 - }
188 -
189 - this.Core = null;
190 - }
191 -
192 - return this.Context.Messaging.EncounteredError ? null : target;
198 }
199
200 /// <summary>
@@ -2422,7 +2427,7 @@ namespace WixToolset.Core
2427 {
2428 if (isGeneratableGuidOk || keyFound && !String.IsNullOrEmpty(keyPath))
2429 {
2425 - this.componentIdPlaceholdersResolver.AddVariable(componentIdPlaceholder, keyPath);
2430 + this.componentIdPlaceholdersResolver.AddVariable(componentIdPlaceholder, keyPath, false);
2431
2432 id = new Identifier(keyPath, AccessModifier.Private);
2433 }
src/WixToolset.Core/Layout.cs new
+188
@@ -0,0 +1,188 @@
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.Core
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using System.IO;
8 + using System.Linq;
9 + using WixToolset.Core.Bind;
10 + using WixToolset.Data;
11 + using WixToolset.Data.Bind;
12 + using WixToolset.Data.Tuples;
13 + using WixToolset.Extensibility;
14 + using WixToolset.Extensibility.Services;
15 +
16 + /// <summary>
17 + /// Layout for the WiX toolset.
18 + /// </summary>
19 + public sealed class Layout
20 + {
21 + public Layout(IServiceProvider serviceProvider, IEnumerable<FileTransfer> fileTransfers, IEnumerable<string> contentFilePaths, string contentsFile, string outputsFile, string builtOutputsFile, bool suppressAclReset)
22 + {
23 + this.ServiceProvider = serviceProvider;
24 + this.FileTransfers = fileTransfers;
25 + this.ContentFilePaths = contentFilePaths;
26 + this.ContentsFile = contentsFile;
27 + this.OutputsFile = outputsFile;
28 + this.BuiltOutputsFile = builtOutputsFile;
29 + this.SuppressAclReset = suppressAclReset;
30 + this.Messaging = this.ServiceProvider.GetService<IMessaging>();
31 + }
32 +
33 + private IServiceProvider ServiceProvider { get; }
34 +
35 + private IEnumerable<FileTransfer> FileTransfers { get; }
36 +
37 + private IEnumerable<string> ContentFilePaths { get; }
38 +
39 + private string ContentsFile { get; }
40 +
41 + private string OutputsFile { get; }
42 +
43 + private string BuiltOutputsFile { get; }
44 +
45 + private bool SuppressAclReset { get; }
46 +
47 + private IMessaging Messaging { get; }
48 +
49 + public void Execute()
50 + {
51 + var extensionManager = this.ServiceProvider.GetService<IExtensionManager>();
52 +
53 + var context = this.ServiceProvider.GetService<ILayoutContext>();
54 + context.Messaging = this.Messaging;
55 + context.Extensions = extensionManager.Create<ILayoutExtension>();
56 + context.FileSystemExtensions = extensionManager.Create<IFileSystemExtension>();
57 + context.FileTransfers = this.FileTransfers;
58 + context.ContentFilePaths = this.ContentFilePaths;
59 + context.ContentsFile = this.ContentsFile;
60 + context.OutputPdbPath = this.OutputsFile;
61 + context.BuiltOutputsFile = this.BuiltOutputsFile;
62 + context.SuppressAclReset = this.SuppressAclReset;
63 +
64 + // Pre-layout.
65 + //
66 + foreach (var extension in context.Extensions)
67 + {
68 + extension.PreLayout(context);
69 + }
70 +
71 + try
72 + {
73 + // Final step in binding that transfers (moves/copies) all files generated into the appropriate
74 + // location in the source image.
75 + if (context.FileTransfers?.Any() == true)
76 + {
77 + this.Messaging.Write(VerboseMessages.LayingOutMedia());
78 +
79 + var command = new TransferFilesCommand(context.Messaging, context.FileSystemExtensions, context.FileTransfers, context.SuppressAclReset);
80 + command.Execute();
81 + }
82 + }
83 + finally
84 + {
85 + if (!String.IsNullOrEmpty(context.ContentsFile) && context.ContentFilePaths != null)
86 + {
87 + this.CreateContentsFile(context.ContentsFile, context.ContentFilePaths);
88 + }
89 +
90 + if (!String.IsNullOrEmpty(context.OutputsFile) && context.FileTransfers != null)
91 + {
92 + this.CreateOutputsFile(context.OutputsFile, context.FileTransfers, context.OutputPdbPath);
93 + }
94 +
95 + if (!String.IsNullOrEmpty(context.BuiltOutputsFile) && context.FileTransfers != null)
96 + {
97 + this.CreateBuiltOutputsFile(context.BuiltOutputsFile, context.FileTransfers, context.OutputPdbPath);
98 + }
99 + }
100 +
101 + // Post-layout.
102 + foreach (var extension in context.Extensions)
103 + {
104 + extension.PostLayout();
105 + }
106 + }
107 +
108 + /// <summary>
109 + /// Writes the paths to the content files to a text file.
110 + /// </summary>
111 + /// <param name="path">Path to write file.</param>
112 + /// <param name="contentFilePaths">Collection of paths to content files that will be written to file.</param>
113 + private void CreateContentsFile(string path, IEnumerable<string> contentFilePaths)
114 + {
115 + var directory = Path.GetDirectoryName(path);
116 + Directory.CreateDirectory(directory);
117 +
118 + using (var contents = new StreamWriter(path, false))
119 + {
120 + foreach (string contentPath in contentFilePaths)
121 + {
122 + contents.WriteLine(contentPath);
123 + }
124 + }
125 + }
126 +
127 + /// <summary>
128 + /// Writes the paths to the output files to a text file.
129 + /// </summary>
130 + /// <param name="path">Path to write file.</param>
131 + /// <param name="fileTransfers">Collection of files that were transferred to the output directory.</param>
132 + /// <param name="pdbPath">Optional path to created .wixpdb.</param>
133 + private void CreateOutputsFile(string path, IEnumerable<FileTransfer> fileTransfers, string pdbPath)
134 + {
135 + var directory = Path.GetDirectoryName(path);
136 + Directory.CreateDirectory(directory);
137 +
138 + using (var outputs = new StreamWriter(path, false))
139 + {
140 + foreach (FileTransfer fileTransfer in fileTransfers)
141 + {
142 + // Don't list files where the source is the same as the destination since
143 + // that might be the only place the file exists. The outputs file is often
144 + // used to delete stuff and losing the original source would be bad.
145 + if (!fileTransfer.Redundant)
146 + {
147 + outputs.WriteLine(fileTransfer.Destination);
148 + }
149 + }
150 +
151 + if (!String.IsNullOrEmpty(pdbPath))
152 + {
153 + outputs.WriteLine(Path.GetFullPath(pdbPath));
154 + }
155 + }
156 + }
157 +
158 + /// <summary>
159 + /// Writes the paths to the built output files to a text file.
160 + /// </summary>
161 + /// <param name="path">Path to write file.</param>
162 + /// <param name="fileTransfers">Collection of files that were transferred to the output directory.</param>
163 + /// <param name="pdbPath">Optional path to created .wixpdb.</param>
164 + private void CreateBuiltOutputsFile(string path, IEnumerable<FileTransfer> fileTransfers, string pdbPath)
165 + {
166 + var directory = Path.GetDirectoryName(path);
167 + Directory.CreateDirectory(directory);
168 +
169 + using (var outputs = new StreamWriter(path, false))
170 + {
171 + foreach (FileTransfer fileTransfer in fileTransfers)
172 + {
173 + // Only write the built file transfers. Also, skip redundant
174 + // files for the same reason spelled out in this.CreateOutputsFile().
175 + if (fileTransfer.Built && !fileTransfer.Redundant)
176 + {
177 + outputs.WriteLine(fileTransfer.Destination);
178 + }
179 + }
180 +
181 + if (!String.IsNullOrEmpty(pdbPath))
182 + {
183 + outputs.WriteLine(Path.GetFullPath(pdbPath));
184 + }
185 + }
186 + }
187 + }
188 +}
src/WixToolset.Core/LayoutContext.cs new
+40
@@ -0,0 +1,40 @@
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.Core
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using WixToolset.Data.Bind;
8 + using WixToolset.Extensibility;
9 + using WixToolset.Extensibility.Services;
10 +
11 + public class LayoutContext : ILayoutContext
12 + {
13 + internal LayoutContext(IServiceProvider serviceProvider)
14 + {
15 + this.ServiceProvider = serviceProvider;
16 + }
17 +
18 + public IServiceProvider ServiceProvider { get; }
19 +
20 + public IMessaging Messaging { get; set; }
21 +
22 + public IEnumerable<ILayoutExtension> Extensions { get; set; }
23 +
24 + public IEnumerable<IFileSystemExtension> FileSystemExtensions { get; set; }
25 +
26 + public IEnumerable<FileTransfer> FileTransfers { get; set; }
27 +
28 + public IEnumerable<string> ContentFilePaths { get; set; }
29 +
30 + public string OutputPdbPath { get; set; }
31 +
32 + public string ContentsFile { get; set; }
33 +
34 + public string OutputsFile { get; set; }
35 +
36 + public string BuiltOutputsFile { get; set; }
37 +
38 + public bool SuppressAclReset { get; set; }
39 + }
40 +}
src/WixToolset.Core/Librarian.cs
+22 -8
@@ -38,11 +38,14 @@ namespace WixToolset.Core
38
39 var sections = this.Context.Intermediates.SelectMany(i => i.Sections).ToList();
40
41 - var fileResolver = new FileResolver(this.Context.BindPaths, this.Context.Extensions);
41 + var embedFilePaths = this.ResolveFilePathsToEmbed(sections);
42
43 - var embedFilePaths = ResolveFilePathsToEmbed(sections, fileResolver);
43 + var localizationsByCulture = this.CollateLocalizations(this.Context.Localizations);
44
45 - var localizationsByCulture = CollateLocalizations(this.Context.Localizations);
45 + if (this.Context.Messaging.EncounteredError)
46 + {
47 + return null;
48 + }
49
50 foreach (var section in sections)
51 {
@@ -83,7 +86,7 @@ namespace WixToolset.Core
86 return (this.Context.Messaging.EncounteredError ? null : library);
87 }
88
86 - private static Dictionary<string, Localization> CollateLocalizations(IEnumerable<Localization> localizations)
89 + private Dictionary<string, Localization> CollateLocalizations(IEnumerable<Localization> localizations)
90 {
91 var localizationsByCulture = new Dictionary<string, Localization>(StringComparer.OrdinalIgnoreCase);
92
@@ -91,7 +94,14 @@ namespace WixToolset.Core
94 {
95 if (localizationsByCulture.TryGetValue(localization.Culture, out var existingCulture))
96 {
94 - existingCulture.Merge(localization);
97 + try
98 + {
99 + existingCulture.Merge(localization);
100 + }
101 + catch (WixException e)
102 + {
103 + this.Context.Messaging.Write(e.Error);
104 + }
105 }
106 else
107 {
@@ -102,13 +112,17 @@ namespace WixToolset.Core
112 return localizationsByCulture;
113 }
114
105 - private List<string> ResolveFilePathsToEmbed(IEnumerable<IntermediateSection> sections, FileResolver fileResolver)
115 + private List<string> ResolveFilePathsToEmbed(IEnumerable<IntermediateSection> sections)
116 {
117 var embedFilePaths = new List<string>();
118
119 // Resolve paths to files that are to be embedded in the library.
120 if (this.Context.BindFiles)
121 {
122 + var variableResolver = new WixVariableResolver(this.Context.Messaging);
123 +
124 + var fileResolver = new FileResolver(this.Context.BindPaths, this.Context.Extensions);
125 +
126 foreach (var tuple in sections.SelectMany(s => s.Tuples))
127 {
128 foreach (var field in tuple.Fields.Where(f => f.Type == IntermediateFieldType.Path))
@@ -117,9 +131,9 @@ namespace WixToolset.Core
131
132 if (pathField != null)
133 {
120 - var resolvedPath = this.Context.WixVariableResolver.ResolveVariables(tuple.SourceLineNumbers, pathField.Path, false);
134 + var resolution = variableResolver.ResolveVariables(tuple.SourceLineNumbers, pathField.Path, false);
135
122 - var file = fileResolver.Resolve(tuple.SourceLineNumbers, tuple.Definition.Name, resolvedPath);
136 + var file = fileResolver.Resolve(tuple.SourceLineNumbers, tuple.Definition, resolution.Value);
137
138 if (!String.IsNullOrEmpty(file))
139 {
src/WixToolset.Core/LibraryContext.cs
-2
@@ -25,7 +25,5 @@ namespace WixToolset.Core
25 public IEnumerable<Localization> Localizations { get; set; }
26
27 public IEnumerable<Intermediate> Intermediates { get; set; }
28 -
29 - public IBindVariableResolver WixVariableResolver { get; set; }
28 }
29 }
src/WixToolset.Core/Linker.cs
+3 -3
@@ -8,11 +8,11 @@ namespace WixToolset.Core
8 using System.Diagnostics;
9 using System.Globalization;
10 using System.Linq;
11 - using WixToolset.Data;
12 - using WixToolset.Link;
11 using WixToolset.Core.Link;
12 + using WixToolset.Data;
13 using WixToolset.Data.Tuples;
15 - using WixToolset.Extensibility.Services;
14 + using WixToolset.Extensibility;
15 + using WixToolset.Link;
16
17 /// <summary>
18 /// Linker core of the WiX toolset.
src/WixToolset.Core/PreprocessContext.cs
-3
@@ -8,9 +8,6 @@ namespace WixToolset.Core
8 using WixToolset.Extensibility;
9 using WixToolset.Extensibility.Services;
10
11 - /// <summary>
12 - /// The preprocessor core.
13 - /// </summary>
11 internal class PreprocessContext : IPreprocessContext
12 {
13 internal PreprocessContext(IServiceProvider serviceProvider)
src/WixToolset.Core/ResolveContext.cs new
+32
@@ -0,0 +1,32 @@
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.Core
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using WixToolset.Data;
8 + using WixToolset.Extensibility;
9 + using WixToolset.Extensibility.Services;
10 +
11 + public class ResolveContext : IResolveContext
12 + {
13 + internal ResolveContext(IServiceProvider serviceProvider)
14 + {
15 + this.ServiceProvider = serviceProvider;
16 + }
17 +
18 + public IServiceProvider ServiceProvider { get; }
19 +
20 + public IMessaging Messaging { get; set; }
21 +
22 + public IEnumerable<BindPath> BindPaths { get; set; }
23 +
24 + public IEnumerable<IResolverExtension> Extensions { get; set; }
25 +
26 + public string IntermediateFolder { get; set; }
27 +
28 + public Intermediate IntermediateRepresentation { get; set; }
29 +
30 + public IBindVariableResolver WixVariableResolver { get; set; }
31 + }
32 +}
src/WixToolset.Core/Resolver.cs new
+234
@@ -0,0 +1,234 @@
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.Core
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using System.Linq;
8 + using WixToolset.Core.Bind;
9 + using WixToolset.Data;
10 + using WixToolset.Data.Tuples;
11 + using WixToolset.Extensibility;
12 + using WixToolset.Extensibility.Services;
13 +
14 + /// <summary>
15 + /// Resolver for the WiX toolset.
16 + /// </summary>
17 + public sealed class Resolver
18 + {
19 + public Resolver(IServiceProvider serviceProvider, IEnumerable<BindPath> bindPaths, Intermediate intermediateRepresentation, string intermediateFolder, IEnumerable<Localization> localizations)
20 + {
21 + this.ServiceProvider = serviceProvider;
22 + this.BindPaths = bindPaths;
23 + this.IntermediateRepresentation = intermediateRepresentation;
24 + this.IntermediateFolder = intermediateFolder;
25 + this.Localizations = localizations;
26 +
27 + this.Messaging = this.ServiceProvider.GetService<IMessaging>();
28 + }
29 +
30 + private IServiceProvider ServiceProvider { get; }
31 +
32 + private IEnumerable<BindPath> BindPaths { get; }
33 +
34 + private Intermediate IntermediateRepresentation { get; }
35 +
36 + private string IntermediateFolder { get; }
37 +
38 + private IEnumerable<Localization> Localizations { get; }
39 +
40 + private IMessaging Messaging { get; }
41 +
42 + public ResolveResult Execute()
43 + {
44 + var localizer = new Localizer(this.Messaging, this.Localizations);
45 +
46 + var variableResolver = new WixVariableResolver(this.Messaging, localizer);
47 +
48 + var context = this.ServiceProvider.GetService<IResolveContext>();
49 + context.Messaging = this.Messaging;
50 + context.BindPaths = this.BindPaths;
51 + context.Extensions = this.ServiceProvider.GetService<IExtensionManager>().Create<IResolverExtension>();
52 + context.IntermediateFolder = this.IntermediateFolder;
53 + context.IntermediateRepresentation = this.IntermediateRepresentation;
54 + context.WixVariableResolver = this.PopulateVariableResolver(variableResolver);
55 +
56 + // Preresolve.
57 + //
58 + foreach (IResolverExtension extension in context.Extensions)
59 + {
60 + extension.PreResolve(context);
61 + }
62 +
63 + // Resolve.
64 + //
65 + this.LocalizeUI(context);
66 +
67 + var resolveResult = this.Resolve(localizer.Codepage, context);
68 +
69 + if (resolveResult != null)
70 + {
71 + // Postresolve.
72 + //
73 + foreach (IResolverExtension extension in context.Extensions)
74 + {
75 + extension.PostResolve(resolveResult);
76 + }
77 + }
78 +
79 + return resolveResult;
80 + }
81 +
82 + private ResolveResult Resolve(int codepage, IResolveContext context)
83 + {
84 + var buildingPatch = context.IntermediateRepresentation.Sections.Any(s => s.Type == SectionType.Patch);
85 +
86 + var filesWithEmbeddedFiles = new ExtractEmbeddedFiles();
87 +
88 + IEnumerable<DelayedField> delayedFields;
89 + {
90 + var command = new ResolveFieldsCommand();
91 + command.Messaging = context.Messaging;
92 + command.BuildingPatch = buildingPatch;
93 + command.BindVariableResolver = context.WixVariableResolver;
94 + command.BindPaths = context.BindPaths;
95 + command.Extensions = context.Extensions;
96 + command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
97 + command.IntermediateFolder = context.IntermediateFolder;
98 + command.Intermediate = context.IntermediateRepresentation;
99 + command.SupportDelayedResolution = true;
100 + command.Execute();
101 +
102 + delayedFields = command.DelayedFields;
103 + }
104 +
105 +#if REVISIT_FOR_PATCHING
106 + if (context.IntermediateRepresentation.SubStorages != null)
107 + {
108 + foreach (SubStorage transform in context.IntermediateRepresentation.SubStorages)
109 + {
110 + var command = new ResolveFieldsCommand();
111 + command.BuildingPatch = buildingPatch;
112 + command.BindVariableResolver = context.WixVariableResolver;
113 + command.BindPaths = context.BindPaths;
114 + command.Extensions = context.Extensions;
115 + command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
116 + command.IntermediateFolder = context.IntermediateFolder;
117 + command.Intermediate = context.IntermediateRepresentation;
118 + command.SupportDelayedResolution = false;
119 + command.Execute();
120 + }
121 + }
122 +#endif
123 +
124 + var expectedEmbeddedFiles = filesWithEmbeddedFiles.GetExpectedEmbeddedFiles();
125 +
126 + return new ResolveResult
127 + {
128 + Codepage = codepage,
129 + ExpectedEmbeddedFiles = expectedEmbeddedFiles,
130 + DelayedFields = delayedFields,
131 + IntermediateRepresentation = context.IntermediateRepresentation
132 + };
133 + }
134 +
135 + /// <summary>
136 + /// Localize dialogs and controls.
137 + /// </summary>
138 + private void LocalizeUI(IResolveContext context)
139 + {
140 + foreach (var section in context.IntermediateRepresentation.Sections)
141 + {
142 + foreach (var row in section.Tuples.OfType<DialogTuple>())
143 + {
144 + string dialog = row.Dialog;
145 +
146 + if (context.WixVariableResolver.TryGetLocalizedControl(dialog, null, out LocalizedControl localizedControl))
147 + {
148 + if (CompilerConstants.IntegerNotSet != localizedControl.X)
149 + {
150 + row.HCentering = localizedControl.X;
151 + }
152 +
153 + if (CompilerConstants.IntegerNotSet != localizedControl.Y)
154 + {
155 + row.VCentering = localizedControl.Y;
156 + }
157 +
158 + if (CompilerConstants.IntegerNotSet != localizedControl.Width)
159 + {
160 + row.Width = localizedControl.Width;
161 + }
162 +
163 + if (CompilerConstants.IntegerNotSet != localizedControl.Height)
164 + {
165 + row.Height = localizedControl.Height;
166 + }
167 +
168 + row.Attributes = row.Attributes | localizedControl.Attributes;
169 +
170 + if (!String.IsNullOrEmpty(localizedControl.Text))
171 + {
172 + row.Title = localizedControl.Text;
173 + }
174 + }
175 + }
176 +
177 + foreach (var row in section.Tuples.OfType<ControlTuple>())
178 + {
179 + string dialog = row.Dialog_;
180 + string control = row.Control;
181 +
182 + if (context.WixVariableResolver.TryGetLocalizedControl(dialog, control, out LocalizedControl localizedControl))
183 + {
184 + if (CompilerConstants.IntegerNotSet != localizedControl.X)
185 + {
186 + row.X = localizedControl.X;
187 + }
188 +
189 + if (CompilerConstants.IntegerNotSet != localizedControl.Y)
190 + {
191 + row.Y = localizedControl.Y;
192 + }
193 +
194 + if (CompilerConstants.IntegerNotSet != localizedControl.Width)
195 + {
196 + row.Width = localizedControl.Width;
197 + }
198 +
199 + if (CompilerConstants.IntegerNotSet != localizedControl.Height)
200 + {
201 + row.Height = localizedControl.Height;
202 + }
203 +
204 + row.Attributes = row.Attributes | localizedControl.Attributes;
205 +
206 + if (!String.IsNullOrEmpty(localizedControl.Text))
207 + {
208 + row.Text = localizedControl.Text;
209 + }
210 + }
211 + }
212 + }
213 + }
214 +
215 + private WixVariableResolver PopulateVariableResolver(WixVariableResolver resolver)
216 + {
217 + // Gather all the wix variables.
218 + var wixVariableTuples = this.IntermediateRepresentation.Sections.SelectMany(s => s.Tuples).OfType<WixVariableTuple>();
219 + foreach (var tuple in wixVariableTuples)
220 + {
221 + try
222 + {
223 + resolver.AddVariable(tuple.WixVariable, tuple.Value, tuple.Overridable);
224 + }
225 + catch (ArgumentException)
226 + {
227 + this.Messaging.Write(ErrorMessages.WixVariableCollision(tuple.SourceLineNumbers, tuple.WixVariable));
228 + }
229 + }
230 +
231 + return resolver;
232 + }
233 + }
234 +}
src/WixToolset.Core/WixToolsetServiceProvider.cs
+10
@@ -42,6 +42,16 @@ namespace WixToolset.Core
42 return new BindContext(this);
43 }
44
45 + if (serviceType == typeof(ILayoutContext))
46 + {
47 + return new LayoutContext(this);
48 + }
49 +
50 + if (serviceType == typeof(IResolveContext))
51 + {
52 + return new ResolveContext(this);
53 + }
54 +
55 if (serviceType == typeof(IInscribeContext))
56 {
57 return new InscribeContext(this);
src/WixToolset.Core/WixVariableResolver.cs
+17 -60
@@ -9,8 +9,6 @@ namespace WixToolset.Core
9 using System.Text;
10 using System.Text.RegularExpressions;
11 using WixToolset.Data;
12 - using WixToolset.Data.Tuples;
13 - using WixToolset.Extensibility;
12 using WixToolset.Extensibility.Services;
13
14 /// <summary>
@@ -37,14 +35,15 @@ namespace WixToolset.Core
35 /// <summary>
36 /// Gets the count of variables added to the resolver.
37 /// </summary>
40 - public int VariableCount => this.wixVariables.Count;
38 + public int VariableCount => this.wixVariables.Count;
39
40 /// <summary>
41 /// Add a variable.
42 /// </summary>
43 /// <param name="name">The name of the variable.</param>
44 /// <param name="value">The value of the variable.</param>
47 - public void AddVariable(string name, string value)
45 + /// <param name="overridable">Indicates whether the variable can be overridden by an existing variable.</param>
46 + public void AddVariable(string name, string value, bool overridable)
47 {
48 try
49 {
@@ -52,25 +51,9 @@ namespace WixToolset.Core
51 }
52 catch (ArgumentException)
53 {
55 - this.Messaging.Write(ErrorMessages.WixVariableCollision(null, name));
56 - }
57 - }
58 -
59 - /// <summary>
60 - /// Add a variable.
61 - /// </summary>
62 - /// <param name="wixVariableRow">The WixVariableRow to add.</param>
63 - public void AddVariable(WixVariableTuple wixVariableRow)
64 - {
65 - try
66 - {
67 - this.wixVariables.Add(wixVariableRow.WixVariable, wixVariableRow.Value);
68 - }
69 - catch (ArgumentException)
70 - {
71 - if (!wixVariableRow.Overridable) // collision
54 + if (!overridable)
55 {
73 - this.Messaging.Write(ErrorMessages.WixVariableCollision(wixVariableRow.SourceLineNumbers, wixVariableRow.WixVariable));
56 + throw;
57 }
58 }
59 }
@@ -82,37 +65,9 @@ namespace WixToolset.Core
65 /// <param name="value">The value to resolve.</param>
66 /// <param name="localizationOnly">true to only resolve localization variables; false otherwise.</param>
67 /// <returns>The resolved value.</returns>
85 - public string ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly)
68 + public BindVariableResolution ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly)
69 {
87 - return this.ResolveVariables(sourceLineNumbers, value, localizationOnly, out var defaultIgnored, out var delayedIgnored);
88 - }
89 -
90 - /// <summary>
91 - /// Resolve the wix variables in a value.
92 - /// </summary>
93 - /// <param name="sourceLineNumbers">The source line information for the value.</param>
94 - /// <param name="value">The value to resolve.</param>
95 - /// <param name="localizationOnly">true to only resolve localization variables; false otherwise.</param>
96 - /// <param name="isDefault">true if the resolved value was the default.</param>
97 - /// <returns>The resolved value.</returns>
98 - public string ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly, out bool isDefault)
99 - {
100 - return this.ResolveVariables(sourceLineNumbers, value, localizationOnly, out isDefault, out var ignored);
101 - }
102 -
103 - /// <summary>
104 - /// Resolve the wix variables in a value.
105 - /// </summary>
106 - /// <param name="sourceLineNumbers">The source line information for the value.</param>
107 - /// <param name="value">The value to resolve.</param>
108 - /// <param name="localizationOnly">true to only resolve localization variables; false otherwise.</param>
109 - /// <param name="errorOnUnknown">true if unknown variables should throw errors.</param>
110 - /// <param name="isDefault">true if the resolved value was the default.</param>
111 - /// <param name="delayedResolve">true if the value has variables that cannot yet be resolved.</param>
112 - /// <returns>The resolved value.</returns>
113 - public string ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly, out bool isDefault, out bool delayedResolve)
114 - {
115 - return this.ResolveVariables(sourceLineNumbers, value, localizationOnly, true, out isDefault, out delayedResolve);
70 + return this.ResolveVariables(sourceLineNumbers, value, localizationOnly, true);
71 }
72
73 /// <summary>
@@ -125,13 +80,12 @@ namespace WixToolset.Core
80 /// <param name="isDefault">true if the resolved value was the default.</param>
81 /// <param name="delayedResolve">true if the value has variables that cannot yet be resolved.</param>
82 /// <returns>The resolved value.</returns>
128 - public string ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly, bool errorOnUnknown, out bool isDefault, out bool delayedResolve)
83 + internal BindVariableResolution ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly, bool errorOnUnknown)
84 {
85 MatchCollection matches = Common.WixVariableRegex.Matches(value);
86
87 // the value is the default unless its substituted further down
133 - isDefault = true;
134 - delayedResolve = false;
88 + var result = new BindVariableResolution { IsDefault = true, Value = value };
89
90 if (0 < matches.Count)
91 {
@@ -172,6 +126,8 @@ namespace WixToolset.Core
126 if (!localizationOnly)
127 {
128 sb.Remove(matches[i].Index - 1, 1);
129 +
130 + result.UpdatedValue = true;
131 }
132 }
133 else
@@ -200,7 +156,7 @@ namespace WixToolset.Core
156 if (this.wixVariables.TryGetValue(variableId, out resolvedValue))
157 {
158 resolvedValue = resolvedValue ?? String.Empty;
203 - isDefault = false;
159 + result.IsDefault = false;
160 }
161 else if (null != variableDefaultValue) // default the resolved value to the inline value if one was specified
162 {
@@ -212,16 +168,17 @@ namespace WixToolset.Core
168 if ("bind" == variableNamespace)
169 {
170 // can't resolve these yet, but keep track of where we find them so they can be resolved later with less effort
215 - delayedResolve = true;
171 + result.DelayedResolve = true;
172 }
173 else
174 {
219 -
175 // insert the resolved value if it was found or display an error
176 if (null != resolvedValue)
177 {
178 sb.Remove(matches[i].Index, matches[i].Length);
179 sb.Insert(matches[i].Index, resolvedValue);
180 +
181 + result.UpdatedValue = true;
182 }
183 else if ("loc" == variableNamespace && errorOnUnknown) // unresolved loc variable
184 {
@@ -235,10 +192,10 @@ namespace WixToolset.Core
192 }
193 }
194
238 - value = sb.ToString();
195 + result.Value = sb.ToString();
196 }
197
241 - return value;
198 + return result;
199 }
200
201 /// <summary>
src/test/WixToolsetTest.CoreIntegration/ProgramFixture.cs
+39 -6
@@ -20,7 +20,8 @@ namespace WixToolsetTest.CoreIntegration
20
21 using (var fs = new DisposableFileSystem())
22 {
23 - var intermediateFolder = fs.GetFolder();
23 + var baseFolder = fs.GetFolder();
24 + var intermediateFolder = Path.Combine(baseFolder, "obj");
25
26 var program = new Program();
27 var result = program.Run(new WixToolsetServiceProvider(), null, new[]
@@ -31,16 +32,16 @@ namespace WixToolsetTest.CoreIntegration
32 "-loc", Path.Combine(folder, "Package.en-us.wxl"),
33 "-bindpath", Path.Combine(folder, "data"),
34 "-intermediateFolder", intermediateFolder,
34 - "-o", Path.Combine(intermediateFolder, @"bin\test.msi")
35 + "-o", Path.Combine(baseFolder, @"bin\test.msi")
36 });
37
38 Assert.Equal(0, result);
39
39 - Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.msi")));
40 - Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.wixpdb")));
41 - Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\MsiPackage\test.txt")));
40 + Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\test.msi")));
41 + Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\test.wixpdb")));
42 + Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\MsiPackage\test.txt")));
43
43 - var intermediate = Intermediate.Load(Path.Combine(intermediateFolder, @"bin\test.wir"));
44 + var intermediate = Intermediate.Load(Path.Combine(baseFolder, @"bin\test.wir"));
45 var section = intermediate.Sections.Single();
46
47 var wixFile = section.Tuples.OfType<WixFileTuple>().Single();
@@ -245,6 +246,38 @@ namespace WixToolsetTest.CoreIntegration
246 }
247 }
248
249 + [Fact]
250 + public void CanBuildWixout()
251 + {
252 + var folder = TestData.Get(@"TestData\SingleFile");
253 +
254 + using (var fs = new DisposableFileSystem())
255 + {
256 + var baseFolder = fs.GetFolder();
257 + var intermediateFolder = Path.Combine(baseFolder, "obj");
258 +
259 + var program = new Program();
260 + var result = program.Run(new WixToolsetServiceProvider(), null, new[]
261 + {
262 + "build",
263 + Path.Combine(folder, "Package.wxs"),
264 + Path.Combine(folder, "PackageComponents.wxs"),
265 + "-loc", Path.Combine(folder, "Package.en-us.wxl"),
266 + "-bindpath", Path.Combine(folder, "data"),
267 + "-intermediateFolder", intermediateFolder,
268 + "-o", Path.Combine(baseFolder, @"bin\test.wixout")
269 + });
270 +
271 + Assert.Equal(0, result);
272 +
273 + var builtFiles = Directory.GetFiles(Path.Combine(baseFolder, @"bin"));
274 +
275 + Assert.Equal(new[]{
276 + "test.wixout"
277 + }, builtFiles.Select(Path.GetFileName).ToArray());
278 + }
279 + }
280 +
281 [Fact(Skip = "Not implemented yet.")]
282 public void CanBuildInstanceTransform()
283 {