/// <summary> /// Tries to install a plugin from 'path', throws an exception on error. /// </summary> internal static PackageDef InstallPluginPackage(string target, string path) { checkExtension(path); checkFileExists(path); var package = PackageDef.FromPackage(path); var destination = package.IsSystemWide() ? PackageDef.SystemWideInstallationDirectory : target; try { if (Path.GetExtension(path).ToLower().EndsWith("tappackages")) // This is a bundle { var tempDir = Path.GetTempPath(); var bundleFiles = UnpackPackage(path, tempDir); foreach (var file in bundleFiles) { UnpackPackage(file, destination); } } else { UnpackPackage(path, destination); } } catch (Exception e) { log.Error($"Install failed to execute for '{package.Name}'."); tryUninstall(path, package, target); throw new Exception($"Failed to install package '{path}'.", e); } var pi = new PluginInstaller(); if (pi.ExecuteAction(package, "install", false, target) == ActionResult.Error) { log.Error($"Install package action failed to execute for '{package.Name}'."); tryUninstall(path, package, target); throw new Exception($"Failed to install package '{path}'."); } CustomPackageActionHelper.RunCustomActions(package, PackageActionStage.Install, new CustomPackageActionArgs(null, false)); return(package); }
static Issue checkPackages(IEnumerable <PackageDef> installedPackages, string[] packages, LogEventType severity) { var packages_new = new List <PackageDef>(); foreach (string pkg in packages) { if (!File.Exists(pkg)) { log.Warning("Package does not exists."); return(Issue.MissingPackage); } packages_new.Add(PackageDef.FromPackage(pkg)); } var new_names = packages_new.Select(pkg => pkg.Name).ToArray(); var after_installation = packages_new.Concat(installedPackages.Where(pkg => new_names.Contains(pkg.Name) == false)).ToList(); return(CheckPackages(after_installation, packages_new, severity)); }
internal static List <string> DownloadPackages(string destinationDir, List <PackageDef> PackagesToDownload, List <string> filenames = null) { List <string> downloadedPackages = new List <string>(); for (int i = 0; i < PackagesToDownload.Count; i++) { Stopwatch timer = Stopwatch.StartNew(); var pkg = PackagesToDownload[i]; string filename = filenames?.ElementAtOrDefault(i) ?? Path.Combine(destinationDir, GetQualifiedFileName(pkg)); TapThread.ThrowIfAborted(); try { PackageDef existingPkg = null; try { // If the package we are installing is from a file, we should always use that file instead of a cached package. // During development a package might not change version but still have different content. if (pkg.PackageSource is FilePackageDefSource == false && File.Exists(filename)) { existingPkg = PackageDef.FromPackage(filename); } } catch (Exception e) { log.Warning("Could not open OpenTAP Package. Redownloading package.", e); File.Delete(filename); } if (existingPkg != null) { if (existingPkg.Version == pkg.Version && existingPkg.OS == pkg.OS && existingPkg.Architecture == pkg.Architecture) { if (!PackageCacheHelper.PackageIsFromCache(existingPkg)) { log.Info("Package '{0}' already exists in '{1}'.", pkg.Name, destinationDir); } else { log.Info("Package '{0}' already exists in cache '{1}'.", pkg.Name, destinationDir); } } else { throw new Exception($"A package already exists but it is not the same as the package that is being downloaded."); } } else { string source = (pkg.PackageSource as IRepositoryPackageDefSource)?.RepositoryUrl; if (source == null && pkg.PackageSource is FilePackageDefSource fileSource) { source = fileSource.PackageFilePath; } IPackageRepository rm = PackageRepositoryHelpers.DetermineRepositoryType(source); if (PackageCacheHelper.PackageIsFromCache(pkg)) { rm.DownloadPackage(pkg, filename); log.Info(timer, "Found package '{0}' in cache. Copied to '{1}'.", pkg.Name, Path.GetFullPath(filename)); } else { log.Debug("Downloading '{0}' version '{1}' from '{2}'", pkg.Name, pkg.Version, source); rm.DownloadPackage(pkg, filename); log.Info(timer, "Downloaded '{0}' to '{1}'.", pkg.Name, Path.GetFullPath(filename)); PackageCacheHelper.CachePackage(filename); } } } catch (Exception ex) { log.Error("Failed to download OpenTAP package."); throw ex; } downloadedPackages.Add(filename); } return(downloadedPackages); }
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); }
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); } } }
private PackageDef GetPackageDef(Installation targetInstallation) { // Try a number of methods to obtain the PackageDef in order of precedence var packageRef = new PackageSpecifier(Name, VersionSpecifier.Parse(Version ?? ""), Architecture, OS); PackageDef package; // a relative or absolute file path if (File.Exists(Name)) { package = PackageDef.FromPackage(Name); if (package != null) { Offline = true; return(package); } } // a currently installed package package = targetInstallation.GetPackages().FirstOrDefault(p => p.Name == Name && versionSpec.IsCompatible(p.Version)); if (package != null) { return(package); } if (Offline == false) { try { // a release from repositories package = repositories.SelectMany(x => x.GetPackages(packageRef)) .FindMax(p => p.Version); if (package != null) { return(package); } } catch (System.Net.WebException e) { // not connected to the internet log.Error(e.Message); log.Warning("Could not connect to repository. Showing results for local install"); DisableHttpRepositories(); package = repositories.SelectMany(x => x.GetPackages(packageRef)) .FindMax(p => p.Version); if (package != null) { return(package); } } } if (!string.IsNullOrWhiteSpace(Version)) { log.Warning($"{Name} version {Version} not found."); } if (Offline == false && string.IsNullOrWhiteSpace(Version)) { // a prerelease from repositories packageRef = new PackageSpecifier(Name, VersionSpecifier.Parse("any"), Architecture, OS); package = repositories.SelectMany(x => x.GetPackages(packageRef)) .FindMax(p => p.Version); } return(package); }