-
Notifications
You must be signed in to change notification settings - Fork 69
/
VSPackageManager.cs
408 lines (351 loc) · 16.5 KB
/
VSPackageManager.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using EnvDTE;
#if VS14
using Microsoft.VisualStudio.ProjectSystem.Interop;
#endif
using Microsoft.VisualStudio.Shell.Interop;
using NuGet.VisualStudio.Resources;
namespace NuGet.VisualStudio
{
public class VsPackageManager : PackageManager, IVsPackageManager
{
private readonly ISharedPackageRepository _sharedRepository;
private readonly IDictionary<string, IProjectManager> _projects;
private readonly ISolutionManager _solutionManager;
protected readonly IFileSystemProvider _fileSystemProvider;
private readonly IDeleteOnRestartManager _deleteOnRestartManager;
private readonly VsPackageInstallerEvents _packageEvents;
private readonly IVsFrameworkMultiTargeting _frameworkMultiTargeting;
private bool _repositoryOperationPending;
public VsPackageManager(ISolutionManager solutionManager,
IPackageRepository sourceRepository,
IFileSystemProvider fileSystemProvider,
IFileSystem fileSystem,
ISharedPackageRepository sharedRepository,
IDeleteOnRestartManager deleteOnRestartManager,
VsPackageInstallerEvents packageEvents,
IVsFrameworkMultiTargeting frameworkMultiTargeting = null)
: base(sourceRepository, new DefaultPackagePathResolver(fileSystem), fileSystem, sharedRepository)
{
_solutionManager = solutionManager;
_sharedRepository = sharedRepository;
_packageEvents = packageEvents;
_fileSystemProvider = fileSystemProvider;
_deleteOnRestartManager = deleteOnRestartManager;
_frameworkMultiTargeting = frameworkMultiTargeting;
_projects = new Dictionary<string, IProjectManager>(StringComparer.OrdinalIgnoreCase);
}
public ISolutionManager SolutionManager
{
get
{
return _solutionManager;
}
}
internal void EnsureCached(Project project)
{
string projectUniqueName = project.GetUniqueName();
if (_projects.ContainsKey(projectUniqueName))
{
return;
}
_projects[projectUniqueName] = CreateProjectManager(project);
}
public VsPackageInstallerEvents PackageEvents
{
get
{
return _packageEvents;
}
}
public virtual IProjectManager GetProjectManager(Project project)
{
EnsureCached(project);
IProjectManager projectManager;
bool projectExists = _projects.TryGetValue(project.GetUniqueName(), out projectManager);
Debug.Assert(projectExists, "Unknown project");
return projectManager;
}
private IProjectManager CreateProjectManager(Project project)
{
// Create the project system
IProjectSystem projectSystem = VsProjectSystemFactory.CreateProjectSystem(project, _fileSystemProvider);
#if VS14
if (projectSystem is INuGetPackageManager)
{
var nugetAwareRepo = new NuGetAwareProjectPackageRepository((INuGetPackageManager)projectSystem, _sharedRepository);
return new ProjectManager(this, PathResolver, projectSystem, nugetAwareRepo);
}
#endif
PackageReferenceRepository repository = new PackageReferenceRepository(projectSystem, project.GetProperName(), _sharedRepository);
// Ensure the logger is null while registering the repository
FileSystem.Logger = null;
Logger = null;
// Ensure that this repository is registered with the shared repository if it needs to be
if (repository != null)
{
repository.RegisterIfNecessary();
}
var projectManager = new VsProjectManager(this, PathResolver, projectSystem, repository);
// The package reference repository also provides constraints for packages (via the allowedVersions attribute)
projectManager.ConstraintProvider = repository;
return projectManager;
}
protected override void ExecuteUninstall(IPackage package)
{
// Check if the package is in use before removing it
if (!_sharedRepository.IsReferenced(package.Id, package.Version))
{
base.ExecuteUninstall(package);
}
}
public IPackage FindLocalPackage(IProjectManager projectManager,
string packageId,
SemanticVersion version,
Func<IProjectManager, IList<IPackage>, Exception> getAmbiguousMatchException)
{
IPackage package = null;
bool existsInProject = false;
bool appliesToProject = false;
if (projectManager != null)
{
// Try the project repository first
package = projectManager.LocalRepository.FindPackage(packageId, version);
existsInProject = package != null;
}
// Fallback to the solution repository (it might be a solution only package)
if (package == null)
{
if (version != null)
{
// Get the exact package
package = LocalRepository.FindPackage(packageId, version);
}
else
{
// Get all packages by this name to see if we find an ambiguous match
var packages = LocalRepository.FindPackagesById(packageId).ToList();
if (packages.Count > 1)
{
throw getAmbiguousMatchException(projectManager, packages);
}
// Pick the only one of default if none match
package = packages.SingleOrDefault();
}
}
// Can't find the package in the solution or in the project then fail
if (package == null)
{
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
VsResources.UnknownPackage, packageId));
}
#if VS14
bool isNuGetAwareProjectSystem = (projectManager != null && projectManager.Project is NuGetAwareProjectSystem);
#else
bool isNuGetAwareProjectSystem = false;
#endif
appliesToProject = isNuGetAwareProjectSystem || IsProjectLevel(package);
if (appliesToProject)
{
if (!existsInProject)
{
if (_sharedRepository.IsReferenced(package.Id, package.Version))
{
// If the package doesn't exist in the project and is referenced by other projects
// then fail.
if (projectManager != null)
{
if (version == null)
{
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
VsResources.UnknownPackageInProject,
package.Id,
projectManager.Project.ProjectName));
}
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
VsResources.UnknownPackageInProject,
package.GetFullName(),
projectManager.Project.ProjectName));
}
}
else
{
// The operation applies to solution level since it's not installed in the current project
// but it is installed in some other project
appliesToProject = false;
}
}
}
// Can't have a project level operation if no project was specified
if (appliesToProject && projectManager == null)
{
throw new InvalidOperationException(VsResources.ProjectNotSpecified);
}
return package;
}
public IPackage FindLocalPackage(string packageId, out bool appliesToProject)
{
// It doesn't matter if there are multiple versions of the package installed at solution level,
// we just want to know that one exists.
var packages = LocalRepository.FindPackagesById(packageId).OrderByDescending(p => p.Version).ToList();
// Can't find the package in the solution.
if (!packages.Any())
{
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
VsResources.UnknownPackage, packageId));
}
foreach (IPackage package in packages)
{
appliesToProject = IsProjectLevel(package);
if (!appliesToProject)
{
if (packages.Count > 1)
{
throw CreateAmbiguousUpdateException(projectManager: null, packages: packages);
}
}
else if (!_sharedRepository.IsReferenced(package.Id, package.Version))
{
Logger.Log(MessageLevel.Warning, String.Format(CultureInfo.CurrentCulture,
VsResources.Warning_PackageNotReferencedByAnyProject, package.Id, package.Version));
// Try next package
continue;
}
// Found a package with package Id as 'packageId' which is installed in at least 1 project
return package;
}
// There are one or more packages with package Id as 'packageId'
// BUT, none of them is installed in a project
// it's probably a borked install.
throw new PackageNotInstalledException(
String.Format(CultureInfo.CurrentCulture,
VsResources.PackageNotInstalledInAnyProject, packageId));
}
public Exception CreateAmbiguousUpdateException(IProjectManager projectManager, IList<IPackage> packages)
{
if (projectManager != null && packages.Any(IsProjectLevel))
{
return new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
VsResources.UnknownPackageInProject,
packages[0].Id,
projectManager.Project.ProjectName));
}
return new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
VsResources.AmbiguousUpdate,
packages[0].Id));
}
public Exception CreateAmbiguousUninstallException(IProjectManager projectManager, IList<IPackage> packages)
{
if (projectManager != null && packages.Any(IsProjectLevel))
{
return new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
VsResources.AmbiguousProjectLevelUninstal,
packages[0].Id,
projectManager.Project.ProjectName));
}
return new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
VsResources.AmbiguousUninstall,
packages[0].Id));
}
public Project GetProject(IProjectManager projectManager)
{
// We only support project systems that implement IVsProjectSystem
var vsProjectSystem = projectManager.Project as IVsProjectSystem;
if (vsProjectSystem == null)
{
return null;
}
// Find the project by it's unique name
return _solutionManager.GetProject(vsProjectSystem.UniqueName);
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "If we failed to add binding redirects we don't want it to stop the install/update.")]
public override void AddBindingRedirects(IProjectManager projectManager)
{
// Find the project by it's unique name
Project project = GetProject(projectManager);
// If we can't find the project or it doesn't support binding redirects then don't add any redirects
if (project == null || !project.SupportsBindingRedirects())
{
return;
}
try
{
RuntimeHelpers.AddBindingRedirects(_solutionManager, project, _fileSystemProvider, _frameworkMultiTargeting);
}
catch (Exception e)
{
// If there was an error adding binding redirects then print a warning and continue
Logger.Log(MessageLevel.Warning, String.Format(CultureInfo.CurrentCulture, VsResources.Warning_FailedToAddBindingRedirects, projectManager.Project.ProjectName, e.Message));
}
}
protected override void OnUninstalled(PackageOperationEventArgs e)
{
base.OnUninstalled(e);
PackageEvents.NotifyUninstalled(e);
_deleteOnRestartManager.MarkPackageDirectoryForDeletion(e.Package);
}
private IDisposable StartInstallOperation(string packageId, string packageVersion)
{
return StartOperation(RepositoryOperationNames.Install, packageId, packageVersion);
}
private IDisposable StartUpdateOperation(string packageId, string packageVersion)
{
return StartOperation(RepositoryOperationNames.Update, packageId, packageVersion);
}
private IDisposable StartReinstallOperation(string packageId, string packageVersion)
{
return StartOperation(RepositoryOperationNames.Reinstall, packageId, packageVersion);
}
private IDisposable StartOperation(string operation, string packageId, string mainPackageVersion)
{
// If there's a pending operation, don't allow another one to start.
// This is for the Reinstall case. Because Reinstall just means
// uninstalling and installing, we don't want the child install operation
// to override Reinstall value.
if (_repositoryOperationPending)
{
return DisposableAction.NoOp;
}
_repositoryOperationPending = true;
return DisposableAction.All(
SourceRepository.StartOperation(operation, packageId, mainPackageVersion),
new DisposableAction(() => _repositoryOperationPending = false));
}
protected override void OnInstalling(PackageOperationEventArgs e)
{
PackageEvents.NotifyInstalling(e);
base.OnInstalling(e);
}
protected override void OnInstalled(PackageOperationEventArgs e)
{
base.OnInstalled(e);
PackageEvents.NotifyInstalled(e);
}
protected override void OnUninstalling(PackageOperationEventArgs e)
{
PackageEvents.NotifyUninstalling(e);
base.OnUninstalling(e);
}
public override IPackage LocatePackageToUninstall(IProjectManager projectManager, string id, SemanticVersion version)
{
return FindLocalPackage(
projectManager,
id,
version,
CreateAmbiguousUninstallException);
}
}
}