aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2020-06-08 16:26:59 -0700
committerRob Mensching <rob@firegiant.com>2020-06-08 16:37:14 -0700
commit02cdf55197d599d4d1fd611ad749d01f5c47a01f (patch)
tree76fcd573827fdb6b142197baa3237e6310a7bd4d /src/WixToolset.Core
parent04b8976ca565ce95cf32a58c8725843618724383 (diff)
downloadwix-02cdf55197d599d4d1fd611ad749d01f5c47a01f.tar.gz
wix-02cdf55197d599d4d1fd611ad749d01f5c47a01f.tar.bz2
wix-02cdf55197d599d4d1fd611ad749d01f5c47a01f.zip
Add "extension" command
Diffstat (limited to 'src/WixToolset.Core')
-rw-r--r--src/WixToolset.Core/ExtensibilityServices/ExtensionManager.cs182
-rw-r--r--src/WixToolset.Core/WixToolset.Core.csproj1
2 files changed, 146 insertions, 37 deletions
diff --git a/src/WixToolset.Core/ExtensibilityServices/ExtensionManager.cs b/src/WixToolset.Core/ExtensibilityServices/ExtensionManager.cs
index 97216479..f71c0fd1 100644
--- a/src/WixToolset.Core/ExtensibilityServices/ExtensionManager.cs
+++ b/src/WixToolset.Core/ExtensibilityServices/ExtensionManager.cs
@@ -28,59 +28,65 @@ namespace WixToolset.Core.ExtensibilityServices
28 var types = extensionAssembly.GetTypes().Where(t => !t.IsAbstract && !t.IsInterface && typeof(IExtensionFactory).IsAssignableFrom(t)); 28 var types = extensionAssembly.GetTypes().Where(t => !t.IsAbstract && !t.IsInterface && typeof(IExtensionFactory).IsAssignableFrom(t));
29 var factories = types.Select(this.CreateExtensionFactory).ToList(); 29 var factories = types.Select(this.CreateExtensionFactory).ToList();
30 30
31 this.extensionFactories.AddRange(factories); 31 if (!factories.Any())
32 }
33
34 private IExtensionFactory CreateExtensionFactory(Type type)
35 {
36 var constructor = type.GetConstructor(new[] { typeof(IWixToolsetCoreServiceProvider) });
37 if (constructor != null)
38 { 32 {
39 return (IExtensionFactory)constructor.Invoke(new[] { this.ServiceProvider }); 33 var path = Path.GetFullPath(new Uri(extensionAssembly.CodeBase).LocalPath);
34 throw new WixException(ErrorMessages.InvalidExtension(path, "The extension does not implement IExtensionFactory. All extensions must have at least one implementation of IExtensionFactory."));
40 } 35 }
41 36
42 return (IExtensionFactory)Activator.CreateInstance(type); 37 this.extensionFactories.AddRange(factories);
43 } 38 }
44 39
45 public void Load(string extensionPath) 40 public void Load(string extensionPath)
46 { 41 {
42 var checkPath = extensionPath;
43 var checkedPaths = new List<string> { checkPath };
47 try 44 try
48 { 45 {
49 Assembly assembly; 46 if (!TryLoadFromPath(checkPath, out var assembly) && !Path.IsPathRooted(extensionPath))
50
51 // Absolute path to an assembly which means only "load from" will work even though we'd prefer to
52 // use Assembly.Load (see the documentation for Assembly.LoadFrom why).
53 if (Path.IsPathRooted(extensionPath))
54 { 47 {
55 assembly = Assembly.LoadFrom(extensionPath); 48 if (TryParseExtensionReference(extensionPath, out var extensionId, out var extensionVersion))
56 } 49 {
57 else if (ExtensionManager.TryExtensionLoad(extensionPath, out assembly)) 50 foreach (var cachePath in this.CacheLocations())
58 { 51 {
59 // Loaded the assembly by name from the probing path. 52 var extensionFolder = Path.Combine(cachePath, extensionId);
60 } 53
61 else if (ExtensionManager.TryExtensionLoad(Path.GetFileNameWithoutExtension(extensionPath), out assembly)) 54 var versionFolder = extensionVersion;
62 { 55 if (String.IsNullOrEmpty(versionFolder) && !TryFindLatestVersionInFolder(extensionFolder, out versionFolder))
63 // Loaded the assembly by filename alone along the probing path. 56 {
57 checkedPaths.Add(extensionFolder);
58 continue;
59 }
60
61 checkPath = Path.Combine(extensionFolder, versionFolder, "tools", extensionId + ".dll");
62 checkedPaths.Add(checkPath);
63
64 if (TryLoadFromPath(checkPath, out assembly))
65 {
66 break;
67 }
68 }
69 }
64 } 70 }
65 else // relative path to an assembly 71
72 if (assembly == null)
66 { 73 {
67 // We want to use Assembly.Load when we can because it has some benefits over Assembly.LoadFrom 74 throw new WixException(ErrorMessages.CouldNotFindExtensionInPaths(extensionPath, checkedPaths));
68 // (see the documentation for Assembly.LoadFrom). However, it may fail when the path is a relative
69 // path, so we should try Assembly.LoadFrom one last time. We could have detected a directory
70 // separator character and used Assembly.LoadFrom directly, but dealing with path canonicalization
71 // issues is something we don't want to deal with if we don't have to.
72 assembly = Assembly.LoadFrom(extensionPath);
73 } 75 }
74 76
75 this.Add(assembly); 77 this.Add(assembly);
76 } 78 }
77 catch (ReflectionTypeLoadException rtle) 79 catch (ReflectionTypeLoadException rtle)
78 { 80 {
79 throw new WixException(ErrorMessages.InvalidExtension(extensionPath, String.Join(Environment.NewLine, rtle.LoaderExceptions.Select(le => le.ToString())))); 81 throw new WixException(ErrorMessages.InvalidExtension(checkPath, String.Join(Environment.NewLine, rtle.LoaderExceptions.Select(le => le.ToString()))));
82 }
83 catch (WixException)
84 {
85 throw;
80 } 86 }
81 catch (Exception e) 87 catch (Exception e)
82 { 88 {
83 throw new WixException(ErrorMessages.InvalidExtension(extensionPath, e.Message), e); 89 throw new WixException(ErrorMessages.InvalidExtension(checkPath, e.Message), e);
84 } 90 }
85 } 91 }
86 92
@@ -104,18 +110,120 @@ namespace WixToolset.Core.ExtensibilityServices
104 return extensions.Cast<T>().ToList(); 110 return extensions.Cast<T>().ToList();
105 } 111 }
106 112
107 private static bool TryExtensionLoad(string assemblyName, out Assembly assembly) 113 private IExtensionFactory CreateExtensionFactory(Type type)
114 {
115 var constructor = type.GetConstructor(new[] { typeof(IWixToolsetCoreServiceProvider) });
116 if (constructor != null)
117 {
118 return (IExtensionFactory)constructor.Invoke(new[] { this.ServiceProvider });
119 }
120
121 return (IExtensionFactory)Activator.CreateInstance(type);
122 }
123
124 private IEnumerable<string> CacheLocations()
125 {
126 var path = Path.Combine(Environment.CurrentDirectory, @".wix\extensions\");
127 if (Directory.Exists(path))
128 {
129 yield return path;
130 }
131
132 path = Environment.GetEnvironmentVariable("WIX_EXTENSIONS") ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
133 path = Path.Combine(path, @".wix\extensions\");
134 if (Directory.Exists(path))
135 {
136 yield return path;
137 }
138
139 if (Environment.Is64BitOperatingSystem)
140 {
141 path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFiles), @"WixToolset\extensions\");
142 if (Directory.Exists(path))
143 {
144 yield return path;
145 }
146 }
147
148 path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFilesX86), @"WixToolset\extensions\");
149 if (Directory.Exists(path))
150 {
151 yield return path;
152 }
153
154 path = Path.Combine(Path.GetDirectoryName(new Uri(Assembly.GetCallingAssembly().CodeBase).LocalPath), @"extensions\");
155 if (Directory.Exists(path))
156 {
157 yield return path;
158 }
159 }
160
161 private static bool TryParseExtensionReference(string extensionReference, out string extensionId, out string extensionVersion)
162 {
163 extensionId = extensionReference ?? String.Empty;
164 extensionVersion = String.Empty;
165
166 var index = extensionId.LastIndexOf('/');
167 if (index > 0)
168 {
169 extensionVersion = extensionReference.Substring(index + 1);
170 extensionId = extensionReference.Substring(0, index);
171
172 if (!NuGet.Versioning.NuGetVersion.TryParse(extensionVersion, out _))
173 {
174 return false;
175 }
176
177 if (String.IsNullOrEmpty(extensionId))
178 {
179 return false;
180 }
181 }
182
183 return true;
184 }
185
186 private static bool TryFindLatestVersionInFolder(string basePath, out string foundVersionFolder)
187 {
188 foundVersionFolder = null;
189
190 try
191 {
192 NuGet.Versioning.NuGetVersion version = null;
193 foreach (var versionPath in Directory.GetDirectories(basePath))
194 {
195 var versionFolder = Path.GetFileName(versionPath);
196 if (NuGet.Versioning.NuGetVersion.TryParse(versionFolder, out var checkVersion) &&
197 (version == null || version < checkVersion))
198 {
199 foundVersionFolder = versionFolder;
200 version = checkVersion;
201 }
202 }
203 }
204 catch (IOException)
205 {
206 }
207
208 return !String.IsNullOrEmpty(foundVersionFolder);
209 }
210
211 private static bool TryLoadFromPath(string extensionPath, out Assembly assembly)
108 { 212 {
109 try 213 try
110 { 214 {
111 assembly = Assembly.Load(assemblyName); 215 if (File.Exists(extensionPath))
112 return true; 216 {
217 assembly = Assembly.LoadFrom(extensionPath);
218 return true;
219 }
113 } 220 }
114 catch (IOException e) when (e is FileLoadException || e is FileNotFoundException) 221 catch (IOException e) when (e is FileLoadException || e is FileNotFoundException)
115 { 222 {
116 assembly = null;
117 return false;
118 } 223 }
224
225 assembly = null;
226 return false;
119 } 227 }
120 } 228 }
121} 229}
diff --git a/src/WixToolset.Core/WixToolset.Core.csproj b/src/WixToolset.Core/WixToolset.Core.csproj
index 3e7bea3b..41ab626e 100644
--- a/src/WixToolset.Core/WixToolset.Core.csproj
+++ b/src/WixToolset.Core/WixToolset.Core.csproj
@@ -22,6 +22,7 @@
22 <ItemGroup> 22 <ItemGroup>
23 <PackageReference Include="System.IO.FileSystem.AccessControl" Version="4.6.0" /> 23 <PackageReference Include="System.IO.FileSystem.AccessControl" Version="4.6.0" />
24 <PackageReference Include="System.Text.Encoding.CodePages" Version="4.6.0" /> 24 <PackageReference Include="System.Text.Encoding.CodePages" Version="4.6.0" />
25 <PackageReference Include="NuGet.Versioning" Version="5.6.0" />
25 </ItemGroup> 26 </ItemGroup>
26 27
27 <ItemGroup> 28 <ItemGroup>