internal static List <PackageDef> GatherPackagesAndDependencyDefs(Installation installation, PackageSpecifier[] pkgRefs, string[] packageNames, string Version, CpuArchitecture arch, string OS, List <IPackageRepository> repositories, bool force, bool includeDependencies, bool askToIncludeDependencies, bool noDowngrade) { List <PackageDef> gatheredPackages = new List <PackageDef>(); List <PackageSpecifier> packages = new List <PackageSpecifier>(); if (pkgRefs != null) { packages = pkgRefs.ToList(); } else { if (packageNames == null) { throw new Exception("No packages specified."); } foreach (string packageName in packageNames) { var version = Version; if (Path.GetExtension(packageName).ToLower().EndsWith("tappackages")) { var tempDir = Path.GetTempPath(); var bundleFiles = PluginInstaller.UnpackPackage(packageName, tempDir); var packagesInBundle = bundleFiles.Select(PackageDef.FromPackage); // A packages file may contain the several variants of the same package, try to select one based on OS and Architecture foreach (IGrouping <string, PackageDef> grp in packagesInBundle.GroupBy(p => p.Name)) { var selected = grp.ToList(); if (selected.Count == 1) { gatheredPackages.Add(selected.First()); continue; } if (!string.IsNullOrEmpty(OS)) { selected = selected.Where(p => p.OS.ToLower().Split(',').Any(OS.ToLower().Contains)).ToList(); if (selected.Count == 1) { gatheredPackages.Add(selected.First()); log.Debug("TapPackages file contains packages for several operating systems. Picking only the one for {0}.", OS); continue; } } if (arch != CpuArchitecture.Unspecified) { selected = selected.Where(p => ArchitectureHelper.CompatibleWith(arch, p.Architecture)).ToList(); if (selected.Count == 1) { gatheredPackages.Add(selected.First()); log.Debug("TapPackages file contains packages for several CPU architectures. Picking only the one for {0}.", arch); continue; } } throw new Exception("TapPackages file contains multiple variants of the same package. Unable to autoselect a suitable one."); } } else if (string.IsNullOrWhiteSpace(packageName) == false) { packages.Add(new PackageSpecifier(packageName, VersionSpecifier.Parse(version ?? ""), arch, OS)); } } } foreach (var packageReference in packages) { var installedPackages = installation.GetPackages(); Stopwatch timer = Stopwatch.StartNew(); if (File.Exists(packageReference.Name)) { var package = PackageDef.FromPackage(packageReference.Name); if (noDowngrade) { var installedPackage = installedPackages.FirstOrDefault(p => p.Name == package.Name); if (installedPackage != null && installedPackage.Version.CompareTo(package.Version) >= 0) { log.Info($"The same or a newer version of package '{package.Name}' in already installed."); continue; } } gatheredPackages.Add(package); log.Debug(timer, "Found package {0} locally.", packageReference.Name); } else { PackageDef package = PackageActionHelpers.FindPackage(packageReference, force, installation, repositories); if (noDowngrade) { var installedPackage = installedPackages.FirstOrDefault(p => p.Name == package.Name); if (installedPackage != null && installedPackage.Version.CompareTo(package.Version) >= 0) { log.Info($"The same or a newer version of package '{package.Name}' in already installed."); continue; } } if (PackageCacheHelper.PackageIsFromCache(package)) { log.Debug(timer, "Found package {0} version {1} in local cache", package.Name, package.Version); } else { log.Debug(timer, "Found package {0} version {1}", package.Name, package.Version); } gatheredPackages.Add(package); } } if (gatheredPackages.All(p => p.IsBundle())) { // If we are just installing bundles, we can assume that dependencies should also be installed includeDependencies = true; } log.Debug("Resolving dependencies."); var resolver = new DependencyResolver(installation, gatheredPackages, repositories); if (resolver.UnknownDependencies.Any()) { foreach (var dep in resolver.UnknownDependencies) { string message = string.Format("A package dependency named '{0}' with a version compatible with {1} could not be found in any repository.", dep.Name, dep.Version); if (force) { log.Warning(message); log.Warning("Continuing without downloading dependencies. Plugins will likely not work as expected.", dep.Name); } else { log.Error(message); } } if (!force) { log.Info("To download package dependencies despite the conflicts, use the --force option."); return(null); } } else if (resolver.MissingDependencies.Any()) { string dependencyArgsHint = ""; if (!includeDependencies) { dependencyArgsHint = $" (use --dependencies to also get these)"; } if (resolver.MissingDependencies.Count > 1) { log.Info("{0} required dependencies are currently not installed{1}.", resolver.MissingDependencies.Count, dependencyArgsHint); } else { log.Info("A required dependency is currently not installed{0}.", dependencyArgsHint); } if (includeDependencies) { //log.Debug($"Currently set to download dependencies quietly."); foreach (var package in resolver.MissingDependencies) { log.Debug("Adding dependency {0} {1}", package.Name, package.Version); gatheredPackages.Insert(0, package); } } else if (askToIncludeDependencies) { var pkgs = new List <DepRequest>(); foreach (var package in resolver.MissingDependencies) { // Handle each package at a time. DepRequest req = null; pkgs.Add(req = new DepRequest { PackageName = package.Name, message = string.Format("Add dependency {0} {1} ?", package.Name, package.Version), Response = DepResponse.Add }); UserInput.Request(req, true); } foreach (var pkg in resolver.MissingDependencies) { var res = pkgs.FirstOrDefault(r => r.PackageName == pkg.Name); if ((res != null) && res.Response == DepResponse.Add) { gatheredPackages.Insert(0, pkg); } } } } return(gatheredPackages); }
internal void InstallThread() { if (cancellationToken.IsCancellationRequested) { return; } try { waitForPackageFilesFree(TapDir, PackagePaths); int progressPercent = 10; OnProgressUpdate(progressPercent, ""); foreach (string fileName in PackagePaths) { try { OnProgressUpdate(progressPercent, "Installing " + Path.GetFileNameWithoutExtension(fileName)); Stopwatch timer = Stopwatch.StartNew(); PackageDef pkg = PluginInstaller.InstallPluginPackage(TapDir, fileName); log.Info(timer, "Installed " + pkg.Name + " version " + pkg.Version); progressPercent += 80 / PackagePaths.Count(); if (pkg.Files.Any(s => s.Plugins.Any(p => p.BaseType == nameof(ICustomPackageData))) && PackagePaths.Last() != fileName) { log.Info(timer, $"Package '{pkg.Name}' contains possibly relevant plugins for next package installations. Searching for plugins.."); PluginManager.DirectoriesToSearch.Add(TapDir); PluginManager.SearchAsync(); } } catch { if (!ForceInstall) { if (PackagePaths.Last() != fileName) { log.Warning("Aborting installation of remaining packages (use --force to override this behavior)."); } throw; } else { if (PackagePaths.Last() != fileName) { log.Warning("Continuing installation of remaining packages (--force argument used)."); } } } } OnProgressUpdate(90, ""); if (DoSleep) { Thread.Sleep(100); } OnProgressUpdate(100, "Plugin installed."); Thread.Sleep(50); // Let Eventhandler get the last OnProgressUpdate } catch (Exception ex) { OnError(ex); return; } Installation installation = new Installation(TapDir); installation.AnnouncePackageChange(); }
private void waitForPackageFilesFree(string tapDir, List <string> PackagePaths) { // ignore tap.exe as it is not meant to be overwritten. bool exclude(string filename) => filename.ToLower() == "tap" || filename.ToLower() == "tap.exe"; List <FileInfo> filesInUse = new List <FileInfo>(); foreach (string packageFileName in PackagePaths) { foreach (string file in PluginInstaller.FilesInPackage(packageFileName)) { string fullPath = Path.Combine(tapDir, file); string filename = Path.GetFileName(file); if (exclude(filename)) { continue; } if (IsFileLocked(new FileInfo(fullPath))) { filesInUse.Add(new FileInfo(fullPath)); } } } // Check if the files that are in use are used by any other package var packages = PackagePaths.Select(p => p.EndsWith("TapPackage") ? PackageDef.FromPackage(p) : PackageDef.FromXml(p)); var remainingInstalledPlugins = new Installation(tapDir).GetPackages().Where(i => packages.Any(p => p.Name == i.Name) == false); var filesToRemain = remainingInstalledPlugins.SelectMany(p => p.Files).Select(f => f.RelativeDestinationPath).Distinct(StringComparer.InvariantCultureIgnoreCase); filesInUse = filesInUse.Where(f => filesToRemain.Contains(f.Name, StringComparer.InvariantCultureIgnoreCase) == false).ToList(); if (filesInUse.Count > 0) { log.Info("Following files cannot be modified because they are in use:"); foreach (var file in filesInUse) { log.Info("- " + file.FullName); var loaded_asm = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.IsDynamic == false && x.Location == file.FullName); if (loaded_asm != null) { throw new InvalidOperationException($"The file '{file.FullName}' is being used by this process."); } } var allProcesses = Process.GetProcesses().Where(p => p.ProcessName.ToLowerInvariant().Contains("opentap") && p.ProcessName.ToLowerInvariant().Contains("vshost") == false && p.ProcessName != Assembly.GetExecutingAssembly().GetName().Name).ToArray(); if (allProcesses.Any()) { // The file could be locked by someone other than OpenTAP processes. We should not assume it's OpenTAP holding the file. log.Warning(Environment.NewLine + "To continue, try closing applications that could be using the files."); foreach (var process in allProcesses) { log.Warning("- " + process.ProcessName); } } log.Warning(Environment.NewLine + "Waiting for files to become unlocked..."); while (isPackageFilesInUse(tapDir, PackagePaths, exclude)) { if (cancellationToken.IsCancellationRequested) { return; } if (!isTapRunning()) { OnError(new IOException("One or more plugin files are in use. View log for more information.")); } Thread.Sleep(300); } } }
internal bool RunCommand(string command, bool force, bool modifiesPackageFiles) { var verb = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(command.ToLower()) + "ed"; try { if (modifiesPackageFiles) { waitForPackageFilesFree(TapDir, PackagePaths); } if (cancellationToken.IsCancellationRequested) { log.Debug("Received abort while waiting for package files to be unlocked."); return(false); } double progressPercent = 10; OnProgressUpdate((int)progressPercent, ""); PluginInstaller pi = new PluginInstaller(); foreach (string fileName in PackagePaths) { PackageDef pkg = PackageDef.FromXml(fileName); OnProgressUpdate((int)progressPercent, string.Format("Running command '{0}' on '{1}'", command, pkg.Name)); Stopwatch timer = Stopwatch.StartNew(); var res = pi.ExecuteAction(pkg, command, force, TapDir); if (res == ActionResult.Error) { OnProgressUpdate(100, "Done"); return(false); } else if (res == ActionResult.NothingToDo) { log.Info(string.Format("Tried to {0} {1}, but there was nothing to do.", command, pkg.Name)); } else { log.Info(timer, string.Format("{1} {0} version {2}.", pkg.Name, verb, pkg.Version)); } progressPercent += (double)80 / PackagePaths.Count(); } OnProgressUpdate(90, ""); if (DoSleep) { Thread.Sleep(100); } OnProgressUpdate(100, "Done"); Thread.Sleep(50); // Let Eventhandler get the last OnProgressUpdate } catch (Exception ex) { OnError(ex); return(false); } new Installation(TapDir).AnnouncePackageChange(); return(true); }
private static ActionResult DoUninstall(PluginInstaller pluginInstaller, ActionExecuter action, PackageDef package, bool force, string target) { var result = ActionResult.Ok; var destination = package.IsSystemWide() ? PackageDef.SystemWideInstallationDirectory : target; var filesToRemain = new Installation(destination).GetPackages().Where(p => p.Name != package.Name).SelectMany(p => p.Files).Select(f => f.RelativeDestinationPath).Distinct(StringComparer.InvariantCultureIgnoreCase).ToHashSet(StringComparer.InvariantCultureIgnoreCase); try { CustomPackageActionHelper.RunCustomActions(package, PackageActionStage.Uninstall, new CustomPackageActionArgs(null, force)); } catch (Exception ex) { log.Error(ex); result = ActionResult.Error; } try { if (action.ExecutePackageActionSteps(package, force, target) == ActionResult.Error) { throw new Exception(); } } catch { log.Error($"Uninstall package action failed to execute for package '{package.Name}'."); result = ActionResult.Error; } foreach (var file in package.Files) { if (file.RelativeDestinationPath == "tap" || file.RelativeDestinationPath.ToLower() == "tap.exe") // ignore tap.exe as it is not meant to be overwritten. { continue; } string fullPath; if (package.IsSystemWide()) { fullPath = Path.Combine(PackageDef.SystemWideInstallationDirectory, file.RelativeDestinationPath); } else { fullPath = Path.Combine(destination, file.RelativeDestinationPath); } if (filesToRemain.Contains(file.RelativeDestinationPath)) { log.Debug("Skipping deletion of file '{0}' since it is required by another plugin package.", file.RelativeDestinationPath); continue; } try { log.Debug("Deleting file '{0}'.", file.RelativeDestinationPath); File.Delete(fullPath); } catch (Exception e) { log.Debug(e); result = ActionResult.Error; } DeleteEmptyDirectory(new FileInfo(fullPath).Directory); } var packageFile = PackageDef.GetDefaultPackageMetadataPath(package, target); if (!File.Exists(packageFile)) { // TAP 8.x support: packageFile = $"Package Definitions/{package.Name}.package.xml"; } if (File.Exists(packageFile)) { log.Debug("Deleting file '{0}'.", packageFile); File.Delete(packageFile); DeleteEmptyDirectory(new FileInfo(packageFile).Directory); } return(result); }
/// <summary> /// Uninstalls a package. /// </summary> internal static void Uninstall(PackageDef package, string target) { var pi = new PluginInstaller(); pi.ExecuteAction(package, "uninstall", true, target); }