protected override int LockedExecute(CancellationToken cancellationToken) { if (Packages == null) { throw new Exception("No packages specified."); } if (Force == false && Packages.Any(p => p == "OpenTAP") && Target == ExecutorClient.ExeDir) { log.Error("Aborting request to uninstall the OpenTAP package that is currently executing as that would brick this installation. Use --force to uninstall anyway."); return(-4); } Installer installer = new Installer(Target, cancellationToken) { DoSleep = false }; installer.ProgressUpdate += RaiseProgressUpdate; installer.Error += RaiseError; var installedPackages = new Installation(Target).GetPackages(); bool anyUnrecognizedPlugins = false; foreach (string pack in Packages) { PackageDef package = installedPackages.FirstOrDefault(p => p.Name == pack); if (package != null && package.PackageSource is InstalledPackageDefSource source) { installer.PackagePaths.Add(source.PackageDefFilePath); } else if (!IgnoreMissing) { log.Error("Package '{0}' is not installed", pack); anyUnrecognizedPlugins = true; } } if (anyUnrecognizedPlugins) { return(-2); } if (!Force) { if (!CheckPackageAndDependencies(installedPackages, installer.PackagePaths)) { return(-3); } } return(installer.RunCommand("uninstall", Force, true) ? 0 : -1); }
public override int Execute(CancellationToken cancellationToken) { if (Packages == null) { throw new Exception("No packages specified."); } var target = LockingPackageAction.GetLocalInstallationDir(); Installer installer = new Installer(target, cancellationToken) { DoSleep = false }; installer.ProgressUpdate += RaiseProgressUpdate; installer.Error += RaiseError; var installedPackages = new Installation(target).GetPackages(); bool anyUnrecognizedPlugins = false; foreach (string pack in Packages) { PackageDef package = installedPackages.FirstOrDefault(p => p.Name == pack); if (package != null && package.PackageSource is InstalledPackageDefSource source) { installer.PackagePaths.Add(source.PackageDefFilePath); } else if (!IgnoreMissing) { log.Error("Package '{0}' is not installed", pack); anyUnrecognizedPlugins = true; } } if (anyUnrecognizedPlugins) { return(-2); } return(installer.RunCommand("test", false, false) ? 0 : -1); }
protected override int LockedExecute(CancellationToken cancellationToken) { if (OS == null) { switch (Environment.OSVersion.Platform) { case PlatformID.MacOSX: OS = "OSX"; break; case PlatformID.Unix: OS = "Linux"; break; default: OS = "Windows"; break; } } List <IPackageRepository> repositories = new List <IPackageRepository>(); if (Installed == false) { if (Repository == null) { repositories.AddRange(PackageManagerSettings.Current.Repositories.Where(p => p.IsEnabled).Select(s => s.Manager)); } else { repositories.AddRange(Repository.Select(s => PackageRepositoryHelpers.DetermineRepositoryType(s))); } } if (Target == null) { Target = FileSystemHelper.GetCurrentInstallationDirectory(); } HashSet <PackageDef> installed = new Installation(Target).GetPackages().ToHashSet(); VersionSpecifier versionSpec = VersionSpecifier.Parse("^"); if (!String.IsNullOrWhiteSpace(Version)) { versionSpec = VersionSpecifier.Parse(Version); } if (string.IsNullOrEmpty(Name)) { var packages = installed.ToList(); packages.AddRange(PackageRepositoryHelpers.GetPackageNameAndVersionFromAllRepos(repositories, new PackageSpecifier("", versionSpec, Architecture, OS))); if (Installed) { packages = packages.Where(p => installed.Any(i => i.Name == p.Name)).ToList(); } PrintReadable(packages, installed); } else { IPackageIdentifier package = installed.FirstOrDefault(p => p.Name == Name); if (Installed) { if (package is null) { log.Info($"{Name} is not installed"); return(-1); } log.Info(package.Version.ToString()); return(0); } List <PackageVersion> versions = null; if (All) { versions = PackageRepositoryHelpers.GetAllVersionsFromAllRepos(repositories, Name).Distinct().ToList(); var versionsCount = versions.Count; if (versionsCount == 0) // No versions { log.Info($"No versions of '{Name}'."); return(0); } if (Version != null) // Version is specified by user { versions = versions.Where(v => versionSpec.IsCompatible(v.Version)).ToList(); } if (versions.Any() == false && versionsCount > 0) { log.Info($"Package '{Name}' does not exists with version '{Version}'."); log.Info($"Package '{Name}' exists in {versionsCount} other versions, please specify a different version."); return(0); } } else { var opentap = new Installation(Target).GetOpenTapPackage(); versions = PackageRepositoryHelpers.GetAllVersionsFromAllRepos(repositories, Name, opentap).Distinct().ToList(); versions = versions.Where(s => s.IsPlatformCompatible(Architecture, OS)).ToList(); if (versions.Any() == false) // No compatible versions { versions = PackageRepositoryHelpers.GetAllVersionsFromAllRepos(repositories, Name).ToList(); if (versions.Any()) { log.Warning($"There are no compatible versions of '{Name}'."); log.Info($"There are {versions.Count} incompatible versions available. Use '--all' to show these."); } else { log.Warning($"Package '{Name}' could not be found in any repository."); } return(0); } versions = versions.Where(v => versionSpec.IsCompatible(v.Version)).ToList(); if (versions.Any() == false) // No versions that are compatible { if (string.IsNullOrEmpty(Version)) { log.Warning($"There are no released versions of '{Name}'."); } else { log.Warning($"Package '{Name}' does not exists with version '{Version}'."); } var anyPrereleaseSpecifier = new VersionSpecifier(versionSpec.Major, versionSpec.Minor, versionSpec.Patch, versionSpec.PreRelease, versionSpec.BuildMetadata, VersionMatchBehavior.AnyPrerelease | versionSpec.MatchBehavior); versions = versions.Where(v => anyPrereleaseSpecifier.IsCompatible(v.Version)).ToList(); if (versions.Any()) { log.Info($"There are {versions.Count} pre-released versions available. Use '--version <pre-release>' (e.g. '--version rc') or '--all' to show these."); } return(0); } } PrintVersionsReadable(package, versions); } return(0); }
internal static void findDependencies(this PackageDef pkg, List <string> excludeAdd, List <AssemblyData> searchedFiles) { bool foundNew = false; var notFound = new HashSet <string>(); // First update the pre-entered dependencies var installed = new Installation(Directory.GetCurrentDirectory()).GetPackages().Where(p => p.Name != pkg.Name).ToList(); List <string> brokenPackageNames = new List <string>(); List <AssemblyData> getPackageAssemblues(PackageDef pkgDef) { List <AssemblyData> output = new List <AssemblyData>(); foreach (var f in pkgDef.Files) { var asms = searchedFiles.Where(sf => PathUtils.AreEqual(f.FileName, sf.Location)) .Where(sf => IsDotNetAssembly(sf.Location)).ToList(); if (asms.Count == 0 && (Path.GetExtension(f.FileName).Equals(".dll", StringComparison.OrdinalIgnoreCase) || Path.GetExtension(f.FileName).Equals(".exe", StringComparison.OrdinalIgnoreCase))) { if (File.Exists(f.FileName)) { // If the pluginSearcher found assemblies that are located somewhere not expected by the package definition, the package might appear broken. // But if the file found by the pluginSearcher is the same as the one expected by the package definition we should not count it as broken. // This could cause a package to not be added as a dependencies. // E.g. when debugging and the OpenTAP.Cli.dll is both in the root build dir and in "Packages/OpenTAP" var asmsIdenticalFilename = searchedFiles.Where(sf => Path.GetFileName(f.FileName) == Path.GetFileName(sf.Location)); var asmsIdentical = asmsIdenticalFilename.Where(sf => PathUtils.CompareFiles(f.FileName, sf.Location)); output.AddRange(asmsIdentical); continue; } if (!brokenPackageNames.Contains(pkgDef.Name) && IsDotNetAssembly(f.FileName)) { brokenPackageNames.Add(pkgDef.Name); log.Warning($"Package '{pkgDef.Name}' is not installed correctly? Referenced file '{f.FileName}' was not found."); } } output.AddRange(asms); } return(output); }; var packageAssemblies = new Memorizer <PackageDef, List <AssemblyData> >(getPackageAssemblues); // check versions of any hardcoded dependencies against what is currently installed foreach (PackageDependency dep in pkg.Dependencies) { var installedPackage = installed.FirstOrDefault(ip => ip.Name == dep.Name); if (installedPackage != null) { if (dep.Version == null) { dep.Version = new VersionSpecifier(installedPackage.Version, VersionMatchBehavior.Compatible); log.Info("A version was not specified for package dependency {0}. Using installed version ({1}).", dep.Name, dep.Version); } else { if (!dep.Version.IsCompatible(installedPackage.Version)) { throw new ExitCodeException((int)PackageCreateAction.ExitCodes.PackageDependencyError, $"Installed version of {dep.Name} ({installedPackage.Version}) is incompatible with dependency specified in package definition ({dep.Version})."); } } } else { throw new ExitCodeException((int)PackageCreateAction.ExitCodes.PackageDependencyError, $"Package dependency '{dep.Name}' specified in package definition is not installed. Please install a compatible version first."); } } // Find additional dependencies do { foundNew = false; // Find everything we already know about var offeredByDependencies = AssembliesOfferedBy(installed, pkg.Dependencies, false, packageAssemblies).ToList(); var offeredByThis = packageAssemblies[pkg] .Where(f => f != null) .ToList(); var anyOffered = offeredByDependencies.Concat(offeredByThis).ToList(); // Find our dependencies and subtract the above two lists var dependentAssemblyNames = pkg.Files .SelectMany(fs => fs.DependentAssemblies) .Where(r => r.Name != "mscorlib") // Special case. We should not bundle the framework assemblies. .Where(r => !anyOffered.Any(of => AssemblyRefUtils.IsCompatibleReference(of, r))) .Distinct().Where(x => !excludeAdd.Contains(x.Name)).ToList(); // If there's anything left we start resolving if (dependentAssemblyNames.Any()) { // First look in installed packages var packageCandidates = new Dictionary <PackageDef, int>(); foreach (var f in installed) { var candidateAsms = packageAssemblies[f].Where(asm => dependentAssemblyNames.Any(dep => (dep.Name == asm.Name))).ToList(); // Don't consider a package that only matches assemblies in the Dependencies subfolder candidateAsms.RemoveAll(asm => asm.Location.Contains("Dependencies")); // TODO: less lazy check for Dependencies subfolder would be good. if (candidateAsms.Count > 0) { packageCandidates[f] = candidateAsms.Count; } } // Look at the most promising candidate (i.e. the one containing most assemblies with the same names as things we need) PackageDef candidatePkg = packageCandidates.OrderByDescending(k => k.Value).FirstOrDefault().Key; if (candidatePkg != null) { foreach (AssemblyData candidateAsm in packageAssemblies[candidatePkg]) { var requiredAsm = dependentAssemblyNames.FirstOrDefault(dep => dep.Name == candidateAsm.Name); if (requiredAsm != null) { if (OpenTap.Utils.Compatible(candidateAsm.Version, requiredAsm.Version)) { log.Info($"Satisfying assembly reference to {requiredAsm.Name} by adding dependency on package {candidatePkg.Name}"); if (candidateAsm.Version != requiredAsm.Version) { log.Warning($"Version of {requiredAsm.Name} in {candidatePkg.Name} is different from the version referenced in this package ({requiredAsm.Version} vs {candidateAsm.Version})."); log.Warning($"Consider changing your version of {requiredAsm.Name} to {candidateAsm.Version} to match that in {candidatePkg.Name}."); } foundNew = true; } else { var depender = pkg.Files.FirstOrDefault(f => f.DependentAssemblies.Contains(requiredAsm)); if (depender == null) { log.Error($"This package require assembly {requiredAsm.Name} in version {requiredAsm.Version} while that assembly is already installed through package '{candidatePkg.Name}' in version {candidateAsm.Version}."); } else { log.Error($"{Path.GetFileName(depender.FileName)} in this package require assembly {requiredAsm.Name} in version {requiredAsm.Version} while that assembly is already installed through package '{candidatePkg.Name}' in version {candidateAsm.Version}."); } //log.Error($"Please align the version of {requiredAsm.Name} to ensure interoperability with package '{candidate.Key.Name}' or uninstall that package."); throw new ExitCodeException((int)PackageCreateAction.ExitCodes.AssemblyDependencyError, $"Please align the version of {requiredAsm.Name} ({candidateAsm.Version} vs {requiredAsm.Version}) to ensure interoperability with package '{candidatePkg.Name}' or uninstall that package."); } } } if (foundNew) { log.Info("Adding dependency on package '{0}' version {1}", candidatePkg.Name, candidatePkg.Version); PackageDependency pd = new PackageDependency(candidatePkg.Name, new VersionSpecifier(candidatePkg.Version, VersionMatchBehavior.Compatible)); pkg.Dependencies.Add(pd); } } else { // No installed package can offer any of the remaining referenced assemblies. // add them as payload in this package in the Dependencies subfolder foreach (var unknown in dependentAssemblyNames) { var foundAsms = searchedFiles.Where(asm => (asm.Name == unknown.Name) && OpenTap.Utils.Compatible(asm.Version, unknown.Version)).ToList(); var foundAsm = foundAsms.FirstOrDefault(); if (foundAsm != null) { AddFileDependencies(pkg, unknown, foundAsm); packageAssemblies.Invalidate(pkg); foundNew = true; } else if (!notFound.Contains(unknown.Name)) { log.Debug("'{0}' could not be found in any of {1} searched assemblies, or is already added.", unknown.Name, searchedFiles.Count); notFound.Add(unknown.Name); } } } } }while (foundNew); }
internal static void RunIsolated(string application = null, string target = null, IsolatedPackageAction isolatedAction = null) { using (var tpmClient = new ExecutorClient()) { var packages = new Installation(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)).GetPackages(); PackageDef findPackageWithFile(string file) { foreach (var package in packages) { foreach (var pkgfile in package.Files) { var filePath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), pkgfile.FileName); if (string.Equals(NormalizePath(filePath), NormalizePath(file), StringComparison.OrdinalIgnoreCase)) { return(package); } } } return(null); } var exec = application ?? Assembly.GetEntryAssembly().Location; PackageDef pkg = findPackageWithFile(Path.GetFullPath(exec)); if (pkg == null) { throw new InvalidOperationException($"{Path.GetFileName(exec)} was not installed through a package."); } var dependencies = pkg.Dependencies.ToList(); if (isolatedAction != null) { // If the executing IsolatedPackageAction does not origin from OpenTAP package, we need to include it when we copy and run isolated var actionAsm = isolatedAction.GetType().Assembly.Location; PackageDef isolatedActionPackage = findPackageWithFile(Path.GetFullPath(actionAsm)); if (isolatedActionPackage == null) { throw new InvalidOperationException($"{Path.GetFileName(actionAsm)} was not installed through a package."); } if (pkg.Name != isolatedActionPackage.Name) { if (!dependencies.Any(p => p.Name == isolatedActionPackage.Name)) { dependencies.Add(new PackageDependency(isolatedActionPackage.Name, new VersionSpecifier(isolatedActionPackage.Version, VersionMatchBehavior.Compatible))); } } } // when installing/uninstalling packages we might need to use custom package actions as well. var extraDependencies = PluginManager.GetPlugins <ICustomPackageAction>().Select(t => t.Assembly.Location).Distinct().ToList(); foreach (var exDep in extraDependencies) { var package = findPackageWithFile(exDep); if (package != null && !dependencies.Any(p => p.Name == package.Name)) { dependencies.Add(new PackageDependency(package.Name, new VersionSpecifier(package.Version, VersionMatchBehavior.Compatible))); } } var deps = OpenTap.Utils.FlattenHeirarchy(dependencies, dep => (IEnumerable <PackageDependency>)packages.FirstOrDefault(x => x.Name == dep.Name)?.Dependencies ?? Array.Empty <PackageDependency>(), distinct: true); if (false == deps.Any(x => x.Name == pkg.Name)) { deps.Add(new PackageDependency(pkg.Name, new VersionSpecifier(pkg.Version, VersionMatchBehavior.Compatible))); } bool force = isolatedAction?.Force ?? false; List <string> allFiles = new List <string>(); foreach (var d in deps) { var availPackages = packages.Where(p => p.Name == d.Name); var package = availPackages.FirstOrDefault(p => d.Version.IsCompatible(p.Version)); if (package == null) { package = availPackages.FirstOrDefault(); if (!force) { if (package != null) { throw new Exception($"Cannot find compatible dependency '{d.Name}' {d.Version}. Version {package.Version} is installed."); } throw new Exception($"Cannot find needed dependency '{d.Name}'."); } log.Warning("Unable to find compatible package, using {0} v{1} instead.", package.Name, package.Version); } var defPath = String.Join("/", PackageDef.PackageDefDirectory, package.Name, PackageDef.PackageDefFileName); if (File.Exists(defPath)) { allFiles.Add(defPath); } var fs = package.Files; var brokenPackages = new HashSet <string>(); foreach (var file in fs) { string loc = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), file.FileName); if (!File.Exists(loc)) { brokenPackages.Add(package.Name); log.Debug($"Could not find file '{loc}' part of package '{package.Name}'."); continue; } allFiles.Add(file.FileName); } foreach (var name in brokenPackages) { log.Warning($"Package '{name}' has missing files and is broken."); } } string tempFolder = Path.GetFullPath(FileSystemHelper.CreateTempDirectory()); foreach (var _loc in allFiles.Distinct()) { string loc = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), _loc); var newloc = Path.Combine(tempFolder, _loc); OpenTap.FileSystemHelper.EnsureDirectory(newloc); if (File.Exists(newloc)) { continue; } File.Copy(loc, newloc); } { // tell TPM Server to start new app. var loc = application ?? Assembly.GetEntryAssembly().Location; if (string.Equals(".dll", Path.GetExtension(loc), StringComparison.OrdinalIgnoreCase)) { //.netcore wierdness. loc = Path.ChangeExtension(loc, "exe"); if (File.Exists(loc) == false) { loc = loc.Substring(0, loc.Length - ".exe".Length); } } var newname = Path.Combine(tempFolder, Path.GetFileName(loc)); newname = $"\"{newname}\""; // there could be whitespace in the name. // now that we start from a different dir, we need to supply a --target argument if (target != null) { newname = $"{newname} --target \"{target}\""; } tpmClient.MessageServer("run " + newname); tpmClient.Dispose(); } } }