diff options
Diffstat (limited to 'src')
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) |
