diff options
11 files changed, 235 insertions, 61 deletions
diff --git a/src/api/wix/WixToolset.Extensibility/Data/IExtensionCacheLocation.cs b/src/api/wix/WixToolset.Extensibility/Data/IExtensionCacheLocation.cs new file mode 100644 index 00000000..53158875 --- /dev/null +++ b/src/api/wix/WixToolset.Extensibility/Data/IExtensionCacheLocation.cs | |||
@@ -0,0 +1,41 @@ | |||
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.Extensibility.Data | ||
4 | { | ||
5 | /// <summary> | ||
6 | /// Extension cache location scope. | ||
7 | /// </summary> | ||
8 | public enum ExtensionCacheLocationScope | ||
9 | { | ||
10 | /// <summary> | ||
11 | /// Project extension cache location. | ||
12 | /// </summary> | ||
13 | Project, | ||
14 | |||
15 | /// <summary> | ||
16 | /// User extension cache location. | ||
17 | /// </summary> | ||
18 | User, | ||
19 | |||
20 | /// <summary> | ||
21 | /// Machine extension cache location. | ||
22 | /// </summary> | ||
23 | Machine, | ||
24 | } | ||
25 | |||
26 | /// <summary> | ||
27 | /// Location where extensions may be cached. | ||
28 | /// </summary> | ||
29 | public interface IExtensionCacheLocation | ||
30 | { | ||
31 | /// <summary> | ||
32 | /// Path for the extension cache location. | ||
33 | /// </summary> | ||
34 | string Path { get; } | ||
35 | |||
36 | /// <summary> | ||
37 | /// Scope for the extension cache location. | ||
38 | /// </summary> | ||
39 | ExtensionCacheLocationScope Scope { get; } | ||
40 | } | ||
41 | } | ||
diff --git a/src/api/wix/WixToolset.Extensibility/Services/IExtensionManager.cs b/src/api/wix/WixToolset.Extensibility/Services/IExtensionManager.cs index 8e49c38d..fe939a59 100644 --- a/src/api/wix/WixToolset.Extensibility/Services/IExtensionManager.cs +++ b/src/api/wix/WixToolset.Extensibility/Services/IExtensionManager.cs | |||
@@ -4,6 +4,7 @@ namespace WixToolset.Extensibility.Services | |||
4 | { | 4 | { |
5 | using System.Collections.Generic; | 5 | using System.Collections.Generic; |
6 | using System.Reflection; | 6 | using System.Reflection; |
7 | using WixToolset.Extensibility.Data; | ||
7 | 8 | ||
8 | /// <summary> | 9 | /// <summary> |
9 | /// Loads extensions and uses the extensions' factories to provide services. | 10 | /// Loads extensions and uses the extensions' factories to provide services. |
@@ -33,6 +34,12 @@ namespace WixToolset.Extensibility.Services | |||
33 | void Load(string extensionReference); | 34 | void Load(string extensionReference); |
34 | 35 | ||
35 | /// <summary> | 36 | /// <summary> |
37 | /// Gets extensions cache locations. | ||
38 | /// </summary> | ||
39 | /// <returns>List of cache locations where extensions may be found.</returns> | ||
40 | IReadOnlyCollection<IExtensionCacheLocation> GetCacheLocations(); | ||
41 | |||
42 | /// <summary> | ||
36 | /// Gets extensions of specified type from factories loaded into the extension manager. | 43 | /// Gets extensions of specified type from factories loaded into the extension manager. |
37 | /// </summary> | 44 | /// </summary> |
38 | /// <typeparam name="T">Type of extension to get.</typeparam> | 45 | /// <typeparam name="T">Type of extension to get.</typeparam> |
diff --git a/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheManager.cs b/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheManager.cs index 256eeb0b..b50ad6e8 100644 --- a/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheManager.cs +++ b/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheManager.cs | |||
@@ -15,22 +15,26 @@ namespace WixToolset.Core.ExtensionCache | |||
15 | using NuGet.Protocol; | 15 | using NuGet.Protocol; |
16 | using NuGet.Protocol.Core.Types; | 16 | using NuGet.Protocol.Core.Types; |
17 | using NuGet.Versioning; | 17 | using NuGet.Versioning; |
18 | using WixToolset.Extensibility.Data; | ||
19 | using WixToolset.Extensibility.Services; | ||
18 | 20 | ||
19 | /// <summary> | 21 | /// <summary> |
20 | /// Extension cache manager. | 22 | /// Extension cache manager. |
21 | /// </summary> | 23 | /// </summary> |
22 | internal class ExtensionCacheManager | 24 | internal class ExtensionCacheManager |
23 | { | 25 | { |
24 | public string CacheFolder(bool global) => global ? this.GlobalCacheFolder() : this.LocalCacheFolder(); | 26 | private IReadOnlyCollection<IExtensionCacheLocation> cacheLocations; |
25 | 27 | ||
26 | public string LocalCacheFolder() => Path.Combine(Environment.CurrentDirectory, ".wix", "extensions"); | 28 | public ExtensionCacheManager(IMessaging messaging, IExtensionManager extensionManager) |
27 | |||
28 | public string GlobalCacheFolder() | ||
29 | { | 29 | { |
30 | var baseFolder = Environment.GetEnvironmentVariable("WIX_EXTENSIONS") ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); | 30 | this.Messaging = messaging; |
31 | return Path.Combine(baseFolder, ".wix", "extensions"); | 31 | this.ExtensionManager = extensionManager; |
32 | } | 32 | } |
33 | 33 | ||
34 | private IMessaging Messaging { get; } | ||
35 | |||
36 | private IExtensionManager ExtensionManager { get; } | ||
37 | |||
34 | public async Task<bool> AddAsync(bool global, string extension, CancellationToken cancellationToken) | 38 | public async Task<bool> AddAsync(bool global, string extension, CancellationToken cancellationToken) |
35 | { | 39 | { |
36 | if (String.IsNullOrEmpty(extension)) | 40 | if (String.IsNullOrEmpty(extension)) |
@@ -54,11 +58,11 @@ namespace WixToolset.Core.ExtensionCache | |||
54 | 58 | ||
55 | (var extensionId, var extensionVersion) = ParseExtensionReference(extension); | 59 | (var extensionId, var extensionVersion) = ParseExtensionReference(extension); |
56 | 60 | ||
57 | var cacheFolder = this.CacheFolder(global); | 61 | var cacheFolder = this.GetCacheFolder(global); |
58 | 62 | ||
59 | cacheFolder = Path.Combine(cacheFolder, extensionId, extensionVersion); | 63 | var extensionFolder = Path.Combine(cacheFolder, extensionId, extensionVersion); |
60 | 64 | ||
61 | if (Directory.Exists(cacheFolder)) | 65 | if (Directory.Exists(extensionFolder)) |
62 | { | 66 | { |
63 | cancellationToken.ThrowIfCancellationRequested(); | 67 | cancellationToken.ThrowIfCancellationRequested(); |
64 | 68 | ||
@@ -75,7 +79,7 @@ namespace WixToolset.Core.ExtensionCache | |||
75 | 79 | ||
76 | (var extensionId, var extensionVersion) = ParseExtensionReference(extension); | 80 | (var extensionId, var extensionVersion) = ParseExtensionReference(extension); |
77 | 81 | ||
78 | var cacheFolder = this.CacheFolder(global); | 82 | var cacheFolder = this.GetCacheFolder(global); |
79 | 83 | ||
80 | var searchFolder = Path.Combine(cacheFolder, extensionId, extensionVersion); | 84 | var searchFolder = Path.Combine(cacheFolder, extensionId, extensionVersion); |
81 | 85 | ||
@@ -127,6 +131,20 @@ namespace WixToolset.Core.ExtensionCache | |||
127 | return Task.FromResult((IEnumerable<CachedExtension>)found); | 131 | return Task.FromResult((IEnumerable<CachedExtension>)found); |
128 | } | 132 | } |
129 | 133 | ||
134 | private string GetCacheFolder(bool global) | ||
135 | { | ||
136 | if (this.cacheLocations == null) | ||
137 | { | ||
138 | this.cacheLocations = this.ExtensionManager.GetCacheLocations(); | ||
139 | } | ||
140 | |||
141 | var requestedScope = global ? ExtensionCacheLocationScope.User : ExtensionCacheLocationScope.Project; | ||
142 | |||
143 | var cacheLocation = this.cacheLocations.First(l => l.Scope == requestedScope); | ||
144 | |||
145 | return cacheLocation.Path; | ||
146 | } | ||
147 | |||
130 | private async Task<bool> DownloadAndExtractAsync(bool global, string id, string version, CancellationToken cancellationToken) | 148 | private async Task<bool> DownloadAndExtractAsync(bool global, string id, string version, CancellationToken cancellationToken) |
131 | { | 149 | { |
132 | var logger = NullLogger.Instance; | 150 | var logger = NullLogger.Instance; |
@@ -149,15 +167,22 @@ namespace WixToolset.Core.ExtensionCache | |||
149 | var repository = Repository.Factory.GetCoreV3(source.Source); | 167 | var repository = Repository.Factory.GetCoreV3(source.Source); |
150 | var resource = await repository.GetResourceAsync<FindPackageByIdResource>(); | 168 | var resource = await repository.GetResourceAsync<FindPackageByIdResource>(); |
151 | 169 | ||
152 | var availableVersions = await resource.GetAllVersionsAsync(id, cache, logger, cancellationToken); | 170 | try |
153 | foreach (var availableVersion in availableVersions) | ||
154 | { | 171 | { |
155 | if (nugetVersion is null || nugetVersion < availableVersion) | 172 | var availableVersions = await resource.GetAllVersionsAsync(id, cache, logger, cancellationToken); |
173 | foreach (var availableVersion in availableVersions) | ||
156 | { | 174 | { |
157 | nugetVersion = availableVersion; | 175 | if (nugetVersion is null || nugetVersion < availableVersion) |
158 | versionSource = source; | 176 | { |
177 | nugetVersion = availableVersion; | ||
178 | versionSource = source; | ||
179 | } | ||
159 | } | 180 | } |
160 | } | 181 | } |
182 | catch (FatalProtocolException e) | ||
183 | { | ||
184 | this.Messaging.Write(ExtensionCacheWarnings.NugetException(id, e.Message)); | ||
185 | } | ||
161 | } | 186 | } |
162 | 187 | ||
163 | if (nugetVersion is null) | 188 | if (nugetVersion is null) |
@@ -168,7 +193,9 @@ namespace WixToolset.Core.ExtensionCache | |||
168 | 193 | ||
169 | var searchSources = versionSource is null ? sources : new[] { versionSource }; | 194 | var searchSources = versionSource is null ? sources : new[] { versionSource }; |
170 | 195 | ||
171 | var extensionFolder = Path.Combine(this.CacheFolder(global), id, nugetVersion.ToString()); | 196 | var cacheFolder = this.GetCacheFolder(global); |
197 | |||
198 | var extensionFolder = Path.Combine(cacheFolder, id, nugetVersion.ToString()); | ||
172 | 199 | ||
173 | foreach (var source in searchSources) | 200 | foreach (var source in searchSources) |
174 | { | 201 | { |
@@ -183,6 +210,8 @@ namespace WixToolset.Core.ExtensionCache | |||
183 | { | 210 | { |
184 | stream.Position = 0; | 211 | stream.Position = 0; |
185 | 212 | ||
213 | Directory.CreateDirectory(extensionFolder); | ||
214 | |||
186 | using (var archive = new PackageArchiveReader(stream)) | 215 | using (var archive = new PackageArchiveReader(stream)) |
187 | { | 216 | { |
188 | var files = PackagingConstants.Folders.Known.SelectMany(folder => archive.GetFiles(folder)).Distinct(StringComparer.OrdinalIgnoreCase); | 217 | var files = PackagingConstants.Folders.Known.SelectMany(folder => archive.GetFiles(folder)).Distinct(StringComparer.OrdinalIgnoreCase); |
@@ -198,7 +227,10 @@ namespace WixToolset.Core.ExtensionCache | |||
198 | return false; | 227 | return false; |
199 | } | 228 | } |
200 | 229 | ||
201 | private string ExtractProgress(string sourceFile, string targetPath, Stream fileStream) => fileStream.CopyToFile(targetPath); | 230 | private string ExtractProgress(string sourceFile, string targetPath, Stream fileStream) |
231 | { | ||
232 | return fileStream.CopyToFile(targetPath); | ||
233 | } | ||
202 | 234 | ||
203 | private static (string extensionId, string extensionVersion) ParseExtensionReference(string extensionReference) | 235 | private static (string extensionId, string extensionVersion) ParseExtensionReference(string extensionReference) |
204 | { | 236 | { |
diff --git a/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheManagerCommand.cs b/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheManagerCommand.cs index d37ee341..008fcebd 100644 --- a/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheManagerCommand.cs +++ b/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheManagerCommand.cs | |||
@@ -25,17 +25,20 @@ namespace WixToolset.Core.ExtensionCache | |||
25 | public ExtensionCacheManagerCommand(IServiceProvider serviceProvider) | 25 | public ExtensionCacheManagerCommand(IServiceProvider serviceProvider) |
26 | { | 26 | { |
27 | this.Messaging = serviceProvider.GetService<IMessaging>(); | 27 | this.Messaging = serviceProvider.GetService<IMessaging>(); |
28 | this.ExtensionManager = serviceProvider.GetService<IExtensionManager>(); | ||
28 | this.ExtensionReferences = new List<string>(); | 29 | this.ExtensionReferences = new List<string>(); |
29 | } | 30 | } |
30 | 31 | ||
31 | private IMessaging Messaging { get; } | ||
32 | |||
33 | public bool ShowHelp { get; set; } | 32 | public bool ShowHelp { get; set; } |
34 | 33 | ||
35 | public bool ShowLogo { get; set; } | 34 | public bool ShowLogo { get; set; } |
36 | 35 | ||
37 | public bool StopParsing { get; set; } | 36 | public bool StopParsing { get; set; } |
38 | 37 | ||
38 | private IMessaging Messaging { get; } | ||
39 | |||
40 | private IExtensionManager ExtensionManager { get; } | ||
41 | |||
39 | private bool Global { get; set; } | 42 | private bool Global { get; set; } |
40 | 43 | ||
41 | private CacheSubcommand? Subcommand { get; set; } | 44 | private CacheSubcommand? Subcommand { get; set; } |
@@ -51,7 +54,7 @@ namespace WixToolset.Core.ExtensionCache | |||
51 | } | 54 | } |
52 | 55 | ||
53 | var success = false; | 56 | var success = false; |
54 | var cacheManager = new ExtensionCacheManager(); | 57 | var cacheManager = new ExtensionCacheManager(this.Messaging, this.ExtensionManager); |
55 | 58 | ||
56 | switch (this.Subcommand) | 59 | switch (this.Subcommand) |
57 | { | 60 | { |
diff --git a/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheWarnings.cs b/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheWarnings.cs new file mode 100644 index 00000000..ddcbfdea --- /dev/null +++ b/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheWarnings.cs | |||
@@ -0,0 +1,24 @@ | |||
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.ExtensionCache | ||
4 | { | ||
5 | using WixToolset.Data; | ||
6 | |||
7 | internal static class ExtensionCacheWarnings | ||
8 | { | ||
9 | public static Message NugetException(string extensionId, string exceptionMessage) | ||
10 | { | ||
11 | return Message(new SourceLineNumber(extensionId), Ids.NugetException, "{0}", exceptionMessage); | ||
12 | } | ||
13 | |||
14 | private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) | ||
15 | { | ||
16 | return new Message(sourceLineNumber, MessageLevel.Warning, (int)id, format, args); | ||
17 | } | ||
18 | |||
19 | public enum Ids | ||
20 | { | ||
21 | NugetException = 6100, | ||
22 | } // last available is 6499. 6500 is ExtensionCacheErrors. | ||
23 | } | ||
24 | } | ||
diff --git a/src/wix/WixToolset.Core.ExtensionCache/WixToolsetCoreServiceProviderExtensions.cs b/src/wix/WixToolset.Core.ExtensionCache/WixToolsetCoreServiceProviderExtensions.cs index 424fc469..b591661e 100644 --- a/src/wix/WixToolset.Core.ExtensionCache/WixToolsetCoreServiceProviderExtensions.cs +++ b/src/wix/WixToolset.Core.ExtensionCache/WixToolsetCoreServiceProviderExtensions.cs | |||
@@ -27,7 +27,10 @@ namespace WixToolset.Core.ExtensionCache | |||
27 | 27 | ||
28 | private static ExtensionCacheManager CreateExtensionCacheManager(IWixToolsetCoreServiceProvider coreProvider, Dictionary<Type, object> singletons) | 28 | private static ExtensionCacheManager CreateExtensionCacheManager(IWixToolsetCoreServiceProvider coreProvider, Dictionary<Type, object> singletons) |
29 | { | 29 | { |
30 | var extensionCacheManager = new ExtensionCacheManager(); | 30 | var messaging = coreProvider.GetService<IMessaging>(); |
31 | var extensionManager = coreProvider.GetService<IExtensionManager>(); | ||
32 | |||
33 | var extensionCacheManager = new ExtensionCacheManager(messaging, extensionManager); | ||
31 | singletons.Add(typeof(ExtensionCacheManager), extensionCacheManager); | 34 | singletons.Add(typeof(ExtensionCacheManager), extensionCacheManager); |
32 | 35 | ||
33 | return extensionCacheManager; | 36 | return extensionCacheManager; |
diff --git a/src/wix/WixToolset.Core.TestPackage/WixRunner.cs b/src/wix/WixToolset.Core.TestPackage/WixRunner.cs index ed7c49b8..8ad3af9a 100644 --- a/src/wix/WixToolset.Core.TestPackage/WixRunner.cs +++ b/src/wix/WixToolset.Core.TestPackage/WixRunner.cs | |||
@@ -7,6 +7,7 @@ namespace WixToolset.Core.TestPackage | |||
7 | using System.Threading; | 7 | using System.Threading; |
8 | using System.Threading.Tasks; | 8 | using System.Threading.Tasks; |
9 | using WixToolset.Core.Burn; | 9 | using WixToolset.Core.Burn; |
10 | using WixToolset.Core.ExtensionCache; | ||
10 | using WixToolset.Core.WindowsInstaller; | 11 | using WixToolset.Core.WindowsInstaller; |
11 | using WixToolset.Data; | 12 | using WixToolset.Data; |
12 | using WixToolset.Extensibility.Services; | 13 | using WixToolset.Extensibility.Services; |
@@ -65,7 +66,8 @@ namespace WixToolset.Core.TestPackage | |||
65 | public static Task<int> Execute(string[] args, IWixToolsetCoreServiceProvider coreProvider, out List<Message> messages, bool warningsAsErrors = true) | 66 | public static Task<int> Execute(string[] args, IWixToolsetCoreServiceProvider coreProvider, out List<Message> messages, bool warningsAsErrors = true) |
66 | { | 67 | { |
67 | coreProvider.AddWindowsInstallerBackend() | 68 | coreProvider.AddWindowsInstallerBackend() |
68 | .AddBundleBackend(); | 69 | .AddBundleBackend() |
70 | .AddExtensionCacheManager(); | ||
69 | 71 | ||
70 | var listener = new TestMessageListener(); | 72 | var listener = new TestMessageListener(); |
71 | 73 | ||
diff --git a/src/wix/WixToolset.Core.TestPackage/WixToolset.Core.TestPackage.csproj b/src/wix/WixToolset.Core.TestPackage/WixToolset.Core.TestPackage.csproj index bda54cd1..0c78cc32 100644 --- a/src/wix/WixToolset.Core.TestPackage/WixToolset.Core.TestPackage.csproj +++ b/src/wix/WixToolset.Core.TestPackage/WixToolset.Core.TestPackage.csproj | |||
@@ -16,6 +16,7 @@ | |||
16 | <ProjectReference Include="..\WixToolset.Core.Native\WixToolset.Core.Native.csproj" PrivateAssets="true" /> | 16 | <ProjectReference Include="..\WixToolset.Core.Native\WixToolset.Core.Native.csproj" PrivateAssets="true" /> |
17 | <ProjectReference Include="..\WixToolset.Core\WixToolset.Core.csproj" PrivateAssets="true" /> | 17 | <ProjectReference Include="..\WixToolset.Core\WixToolset.Core.csproj" PrivateAssets="true" /> |
18 | <ProjectReference Include="..\WixToolset.Core.Burn\WixToolset.Core.Burn.csproj" PrivateAssets="true" /> | 18 | <ProjectReference Include="..\WixToolset.Core.Burn\WixToolset.Core.Burn.csproj" PrivateAssets="true" /> |
19 | <ProjectReference Include="..\WixToolset.Core.ExtensionCache\WixToolset.Core.ExtensionCache.csproj" PrivateAssets="true" /> | ||
19 | <ProjectReference Include="..\WixToolset.Core.WindowsInstaller\WixToolset.Core.WindowsInstaller.csproj" PrivateAssets="true" /> | 20 | <ProjectReference Include="..\WixToolset.Core.WindowsInstaller\WixToolset.Core.WindowsInstaller.csproj" PrivateAssets="true" /> |
20 | </ItemGroup> | 21 | </ItemGroup> |
21 | 22 | ||
diff --git a/src/wix/WixToolset.Core/ExtensibilityServices/ExtensionCacheLocation.cs b/src/wix/WixToolset.Core/ExtensibilityServices/ExtensionCacheLocation.cs new file mode 100644 index 00000000..b45cd315 --- /dev/null +++ b/src/wix/WixToolset.Core/ExtensibilityServices/ExtensionCacheLocation.cs | |||
@@ -0,0 +1,19 @@ | |||
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.ExtensibilityServices | ||
4 | { | ||
5 | using WixToolset.Extensibility.Data; | ||
6 | |||
7 | internal class ExtensionCacheLocation : IExtensionCacheLocation | ||
8 | { | ||
9 | public ExtensionCacheLocation(string path, ExtensionCacheLocationScope scope) | ||
10 | { | ||
11 | this.Path = path; | ||
12 | this.Scope = scope; | ||
13 | } | ||
14 | |||
15 | public string Path { get; } | ||
16 | |||
17 | public ExtensionCacheLocationScope Scope { get; } | ||
18 | } | ||
19 | } | ||
diff --git a/src/wix/WixToolset.Core/ExtensibilityServices/ExtensionManager.cs b/src/wix/WixToolset.Core/ExtensibilityServices/ExtensionManager.cs index 2340ed9e..71334841 100644 --- a/src/wix/WixToolset.Core/ExtensibilityServices/ExtensionManager.cs +++ b/src/wix/WixToolset.Core/ExtensibilityServices/ExtensionManager.cs | |||
@@ -9,6 +9,7 @@ namespace WixToolset.Core.ExtensibilityServices | |||
9 | using System.Reflection; | 9 | using System.Reflection; |
10 | using WixToolset.Data; | 10 | using WixToolset.Data; |
11 | using WixToolset.Extensibility; | 11 | using WixToolset.Extensibility; |
12 | using WixToolset.Extensibility.Data; | ||
12 | using WixToolset.Extensibility.Services; | 13 | using WixToolset.Extensibility.Services; |
13 | 14 | ||
14 | internal class ExtensionManager : IExtensionManager | 15 | internal class ExtensionManager : IExtensionManager |
@@ -16,6 +17,7 @@ namespace WixToolset.Core.ExtensibilityServices | |||
16 | private const string UserWixFolderName = ".wix4"; | 17 | private const string UserWixFolderName = ".wix4"; |
17 | private const string MachineWixFolderName = "WixToolset4"; | 18 | private const string MachineWixFolderName = "WixToolset4"; |
18 | private const string ExtensionsFolderName = "extensions"; | 19 | private const string ExtensionsFolderName = "extensions"; |
20 | private const string UserEnvironmentName = "WIX_EXTENSIONS"; | ||
19 | 21 | ||
20 | private readonly List<IExtensionFactory> extensionFactories = new List<IExtensionFactory>(); | 22 | private readonly List<IExtensionFactory> extensionFactories = new List<IExtensionFactory>(); |
21 | private readonly Dictionary<Type, List<object>> loadedExtensionsByType = new Dictionary<Type, List<object>>(); | 23 | private readonly Dictionary<Type, List<object>> loadedExtensionsByType = new Dictionary<Type, List<object>>(); |
@@ -51,9 +53,14 @@ namespace WixToolset.Core.ExtensibilityServices | |||
51 | { | 53 | { |
52 | if (TryParseExtensionReference(extensionPath, out var extensionId, out var extensionVersion)) | 54 | if (TryParseExtensionReference(extensionPath, out var extensionId, out var extensionVersion)) |
53 | { | 55 | { |
54 | foreach (var cachePath in this.CacheLocations()) | 56 | foreach (var cacheLocation in this.GetCacheLocations()) |
55 | { | 57 | { |
56 | var extensionFolder = Path.Combine(cachePath, extensionId); | 58 | var extensionFolder = Path.Combine(cacheLocation.Path, extensionId); |
59 | |||
60 | if (!Directory.Exists(extensionFolder)) | ||
61 | { | ||
62 | continue; | ||
63 | } | ||
57 | 64 | ||
58 | var versionFolder = extensionVersion; | 65 | var versionFolder = extensionVersion; |
59 | if (String.IsNullOrEmpty(versionFolder) && !TryFindLatestVersionInFolder(extensionFolder, out versionFolder)) | 66 | if (String.IsNullOrEmpty(versionFolder) && !TryFindLatestVersionInFolder(extensionFolder, out versionFolder)) |
@@ -94,6 +101,32 @@ namespace WixToolset.Core.ExtensibilityServices | |||
94 | } | 101 | } |
95 | } | 102 | } |
96 | 103 | ||
104 | public IReadOnlyCollection<IExtensionCacheLocation> GetCacheLocations() | ||
105 | { | ||
106 | var locations = new List<IExtensionCacheLocation>(); | ||
107 | |||
108 | var path = Path.Combine(Environment.CurrentDirectory, UserWixFolderName, ExtensionsFolderName); | ||
109 | locations.Add(new ExtensionCacheLocation(path, ExtensionCacheLocationScope.Project)); | ||
110 | |||
111 | path = Environment.GetEnvironmentVariable(UserEnvironmentName) ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); | ||
112 | path = Path.Combine(path, UserWixFolderName, ExtensionsFolderName); | ||
113 | locations.Add(new ExtensionCacheLocation(path, ExtensionCacheLocationScope.User)); | ||
114 | |||
115 | if (Environment.Is64BitOperatingSystem) | ||
116 | { | ||
117 | path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFiles), MachineWixFolderName, ExtensionsFolderName); | ||
118 | locations.Add(new ExtensionCacheLocation(path, ExtensionCacheLocationScope.Machine)); | ||
119 | } | ||
120 | |||
121 | path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFilesX86), MachineWixFolderName, ExtensionsFolderName); | ||
122 | locations.Add(new ExtensionCacheLocation(path, ExtensionCacheLocationScope.Machine)); | ||
123 | |||
124 | path = Path.Combine(Path.GetDirectoryName(new Uri(Assembly.GetCallingAssembly().CodeBase).LocalPath), ExtensionsFolderName); | ||
125 | locations.Add(new ExtensionCacheLocation(path, ExtensionCacheLocationScope.Machine)); | ||
126 | |||
127 | return locations; | ||
128 | } | ||
129 | |||
97 | public IReadOnlyCollection<T> GetServices<T>() where T : class | 130 | public IReadOnlyCollection<T> GetServices<T>() where T : class |
98 | { | 131 | { |
99 | if (!this.loadedExtensionsByType.TryGetValue(typeof(T), out var extensions)) | 132 | if (!this.loadedExtensionsByType.TryGetValue(typeof(T), out var extensions)) |
@@ -125,43 +158,6 @@ namespace WixToolset.Core.ExtensibilityServices | |||
125 | return (IExtensionFactory)Activator.CreateInstance(type); | 158 | return (IExtensionFactory)Activator.CreateInstance(type); |
126 | } | 159 | } |
127 | 160 | ||
128 | private IEnumerable<string> CacheLocations() | ||
129 | { | ||
130 | var path = Path.Combine(Environment.CurrentDirectory, UserWixFolderName, ExtensionsFolderName); | ||
131 | if (Directory.Exists(path)) | ||
132 | { | ||
133 | yield return path; | ||
134 | } | ||
135 | |||
136 | path = Environment.GetEnvironmentVariable("WIX_EXTENSIONS") ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); | ||
137 | path = Path.Combine(path, UserWixFolderName, ExtensionsFolderName); | ||
138 | if (Directory.Exists(path)) | ||
139 | { | ||
140 | yield return path; | ||
141 | } | ||
142 | |||
143 | if (Environment.Is64BitOperatingSystem) | ||
144 | { | ||
145 | path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFiles), MachineWixFolderName, ExtensionsFolderName); | ||
146 | if (Directory.Exists(path)) | ||
147 | { | ||
148 | yield return path; | ||
149 | } | ||
150 | } | ||
151 | |||
152 | path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFilesX86), MachineWixFolderName, ExtensionsFolderName); | ||
153 | if (Directory.Exists(path)) | ||
154 | { | ||
155 | yield return path; | ||
156 | } | ||
157 | |||
158 | path = Path.Combine(Path.GetDirectoryName(new Uri(Assembly.GetCallingAssembly().CodeBase).LocalPath), ExtensionsFolderName); | ||
159 | if (Directory.Exists(path)) | ||
160 | { | ||
161 | yield return path; | ||
162 | } | ||
163 | } | ||
164 | |||
165 | private static bool TryParseExtensionReference(string extensionReference, out string extensionId, out string extensionVersion) | 161 | private static bool TryParseExtensionReference(string extensionReference, out string extensionId, out string extensionVersion) |
166 | { | 162 | { |
167 | extensionId = extensionReference ?? String.Empty; | 163 | extensionId = extensionReference ?? String.Empty; |
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/ExtensionFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/ExtensionFixture.cs index 225355bf..d814000c 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/ExtensionFixture.cs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/ExtensionFixture.cs | |||
@@ -144,6 +144,52 @@ namespace WixToolsetTest.CoreIntegration | |||
144 | } | 144 | } |
145 | } | 145 | } |
146 | 146 | ||
147 | [Fact] | ||
148 | public void CanManipulateExtensionCache() | ||
149 | { | ||
150 | var currentFolder = Environment.CurrentDirectory; | ||
151 | |||
152 | try | ||
153 | { | ||
154 | using (var fs = new DisposableFileSystem()) | ||
155 | { | ||
156 | var folder = fs.GetFolder(true); | ||
157 | Environment.CurrentDirectory = folder; | ||
158 | |||
159 | var result = WixRunner.Execute(new[] | ||
160 | { | ||
161 | "extension", "add", "WixToolset.UI.wixext" | ||
162 | }); | ||
163 | |||
164 | result.AssertSuccess(); | ||
165 | |||
166 | var cacheFolder = Path.Combine(folder, ".wix4", "extensions", "WixToolset.UI.wixext"); | ||
167 | Assert.True(Directory.Exists(cacheFolder), $"Expected folder '{cacheFolder}' to exist"); | ||
168 | |||
169 | result = WixRunner.Execute(new[] | ||
170 | { | ||
171 | "extension", "list" | ||
172 | }); | ||
173 | |||
174 | result.AssertSuccess(); | ||
175 | var output = result.Messages.Select(m => m.ToString()).Single(); | ||
176 | Assert.StartsWith("WixToolset.UI.wixext 4.", output); | ||
177 | |||
178 | result = WixRunner.Execute(new[] | ||
179 | { | ||
180 | "extension", "remove", "WixToolset.UI.wixext" | ||
181 | }); | ||
182 | |||
183 | result.AssertSuccess(); | ||
184 | Assert.False(Directory.Exists(cacheFolder), $"Expected folder '{cacheFolder}' to NOT exist"); | ||
185 | } | ||
186 | } | ||
187 | finally | ||
188 | { | ||
189 | Environment.CurrentDirectory = currentFolder; | ||
190 | } | ||
191 | } | ||
192 | |||
147 | private static void Build(string[] args) | 193 | private static void Build(string[] args) |
148 | { | 194 | { |
149 | var result = WixRunner.Execute(args) | 195 | var result = WixRunner.Execute(args) |