diff options
author | Rob Mensching <rob@firegiant.com> | 2022-03-19 22:53:06 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2022-03-20 09:00:57 -0700 |
commit | ba4bb7b2080d74918d6d856fba6f86caa410149b (patch) | |
tree | df15e170b01ad2cff63405e347bd8276229b5321 | |
parent | f481f3bfd0b6196dee0034801e4d8768f1fbea63 (diff) | |
download | wix-ba4bb7b2080d74918d6d856fba6f86caa410149b.tar.gz wix-ba4bb7b2080d74918d6d856fba6f86caa410149b.tar.bz2 wix-ba4bb7b2080d74918d6d856fba6f86caa410149b.zip |
Centralize cache locations in IExtensionManager
This removes the duplication of cache location definitions between
IExtensionManager and extension cache command. Also, adds an
extension cache test.
Fixes 6536
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) |