/// <summary> /// Finds the package matching the given atom. /// </summary> /// <param name="atom">an atom</param> /// <returns>the matching package, or NULL if no match is found</returns> protected virtual IPackage ResolvePackage(Atom atom) { ReadOnlyCollection<IPackage> pkglst = this.Packages; IPackage pkg = null; if (atom.IsFullName) { pkg = pkglst .Where(i => i.FullName == atom.PackageName) .SingleOrDefault(); } else { IPackage[] pkgarr = pkglst .Where(i => i.Name == atom.PackageName) .ToArray(); if (pkgarr.Length == 1) /* one match found */ pkg = pkgarr[0]; else if (pkgarr.Length > 1) /* multiple matches found */ throw new AmbiguousMatchException(atom.PackageName); } return pkg; }
/// <summary> /// Determines if the given atom matches the constraints of this atom. /// </summary> /// <param name="left">the atom to compare to</param> /// <returns>true on match, false otherwise</returns> /// <remarks> /// Evaluation uses the input as the left side of the equation and this /// instance as the right side. /// /// Example: /// "this" is >=foo/bar-123 /// "left" is foo/bar-124 /// /// foo/bar-124 >= foo/bar-123 /// /// In this case the match will succeed. If "this" doesn't have a /// comparison operator set, then the package names are compared /// for equality and version is ignored. The comparison operator for /// "left" is always ignored. /// /// If either atom doesn't have a version then version matching is /// skipped. Slots are only compared if both sides have slots but /// no versions. /// </remarks> public bool Match(Atom left) { if (this.IsFullName && left.IsFullName && left.PackageName != _pkg) return false; else if (left.PackagePart != this.PackagePart) return false; bool lnoverslot = (!left.HasVersion && left.Slot == 0); bool rnoverslot = (!this.HasVersion && _slot == 0); if (lnoverslot || rnoverslot || _oper == null) return true; else if (!left.HasVersion || !this.HasVersion) { return left.Slot == _slot; } else if (_oper == "=" && left.Version == _ver) return true; else if (_oper == "<" && left.Version < _ver) return true; else if (_oper == "<=" && left.Version <= _ver) return true; else if (_oper == ">" && left.Version > _ver) return true; else if (_oper == ">=" && left.Version >= _ver) return true; else if (_oper == "!" && left.Version != _ver) return true; return false; }
/// <summary> /// Gets all package distributions referenced by the given atom. /// </summary> /// <param name="atom">the package atom to lookup</param> /// <returns>the matching distributions</returns> public virtual IDistribution[] LookupAll(Atom atom) { IPackage pkg = this.ResolvePackage(atom); List<IDistribution> results = new List<IDistribution>(); if (pkg == null) throw new PackageNotFoundException(atom.PackageName); if (atom.HasVersion || atom.Slot > 0) { /* find a specific version */ IDistribution[] matcharr = pkg.Distributions .Where(i => atom.Match(i.Atom)) .OrderBy(i => i.Version) .ToArray(); if (matcharr.Length == 0) throw new DistributionNotFoundException(atom); if (atom.Comparison == "=") results.Add(matcharr.SingleOrDefault()); else { IDistribution[] unmasked = matcharr .Where(i => !i.PortsTree.IsMasked(i)) .ToArray(); results.AddRange(unmasked.Length > 0 ? unmasked : matcharr); } } else { /* find the best version */ if (pkg.LatestUnmasked != null) results.Add(pkg.LatestUnmasked); else if (pkg.LatestAvailable != null) results.Add(pkg.LatestAvailable); } if (results.Count == 0) throw new DistributionNotFoundException(atom); return results.ToArray(); }
/// <summary> /// Gets the package distribution referenced by the given atom. /// If multiple distributions match, then the latest version is /// returned. /// </summary> /// <param name="atom">the package atom to lookup</param> /// <returns>the matching distribution</returns> public virtual IDistribution Lookup(Atom atom) { return this.LookupAll(atom) .OrderBy(i => i.Version) .Last(); }
/// <summary> /// Determines if files in the image directory would collide with existing /// files owned by other packages. /// </summary> /// <param name="imgdir">image directory</param> /// <param name="atom">package atom to test</param> /// <returns>true if collisions are detected, false otherwise</returns> private bool DetectCollisions(DirectoryInfo imgdir, Atom atom) { int striplev = imgdir.FullName.TrimEnd('\\').Count(i => i == '\\') + 1; string[] files = imgdir .GetFileSystemInfos("*", SearchOption.AllDirectories) .Where(i => (i.Attributes & FileAttributes.Directory) != FileAttributes.Directory) .Select(i => String.Join("\\", i.FullName.Split('\\').Skip(striplev))) .Select(i => _cfg.RootDir.FullName.TrimEnd('\\') + @"\" + i) .ToArray(); string[] collisions = _pkgmgr.CheckFilesOwner(files, atom); bool protect = false; foreach (string file in collisions) _log.ErrorFormat("File '{0}' is already owned by a package", file); foreach (string file in files) { if (this.IsProtected(file)) { _log.ErrorFormat("File '{0}' is protected by the system profile", file); protect = true; } } return (collisions.Length > 0 || protect); }
/// <summary> /// Unmerges the packages matching the given atoms. /// </summary> /// <param name="atomarr">package atoms for unmerging</param> public void Unmerge(Atom[] atomarr) { foreach (Atom atom in atomarr) { UnmergeEventArgs uae = new UnmergeEventArgs(); uae.Package = atom; if (_pkgmgr.IsProtected(atom)) throw new ProtectedPackageException(atom.ToString()); if (this.OnUnmerge != null) this.OnUnmerge.Invoke(this, uae); IInstallProject installer = _pkgmgr.GetPackageInstaller(atom); if (installer != null && installer.HasPkgPreRmTarget) { _log.Info("Executing pre-removal tasks..."); installer.PkgPreRm(); } FileTuple[] files = _pkgmgr.QueryPackageFiles(atom) .OrderByDescending(i => i.Item1) .ToArray(); IntPtr wow64oldval = IntPtr.Zero; if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess) { if (!Wow64DisableWow64FsRedirection(out wow64oldval)) throw new InstallException("Failed to disable Wow64 file system redirection."); } foreach (FileTuple ft in files) { try { if (ft.Item2 == FileType.Directory && this.IsProtected(ft.Item1)) { _log.DebugFormat("Skipping protected directory '{0}'", ft.Item1); continue; } if (ft.Item2 == FileType.Directory) { if (Directory.GetFiles(ft.Item1).Length > 0) throw new IOException(); Directory.Delete(ft.Item1); _log.InfoFormat("Deleted directory '{0}'", ft.Item1); } else { File.Delete(ft.Item1); _log.InfoFormat("Deleted file '{0}'", ft.Item1); } } catch (DirectoryNotFoundException) { _log.DebugFormat("Skipping non-existant directory '{0}'", ft.Item1); } catch (FileNotFoundException) { _log.DebugFormat("Skipping non-existant file '{0}'", ft.Item1); } catch { TrashWorker.AddFile(ft.Item1, _pkgmgr); _log.WarnFormat("Marked '{0}' for future removal", ft.Item1); } } if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess) Wow64RevertWow64FsRedirection(wow64oldval); if (installer != null && installer.HasPkgPostRmTarget) { _log.Info("Executing post-removal tasks..."); installer.PkgPostRm(); } _pkgmgr.DeletePackage(atom); Atom worldatom = Atom.Parse(atom.PackageName, AtomParseOptions.WithoutVersion); if (_pkgmgr.FindPackages(worldatom).Length == 0) _pkgmgr.DeselectPackage(worldatom); } if (this.OnAutoClean != null) this.OnAutoClean.Invoke(this, new EventArgs()); TrashWorker.Purge(_pkgmgr); }
/// <summary> /// Determines if the given distribution satisfies dependency requirements for /// the current graph. /// </summary> /// <param name="dist">distribution atom to check</param> /// <returns>true if satisfies, false otherwise</returns> public bool CheckSatisfies(Atom atom) { if (!_depmap.ContainsKey(atom.PackageName)) throw new KeyNotFoundException("Package '" + atom.PackageName + "' is not a dependency."); /* the given dist satisfies the requirement if it appears in all matches found */ foreach (Dependency d in _depmap[atom.PackageName]) { if (d.Matches.Where(i => atom.Match(d.Atom)).Count() == 0) return false; } return true; }