Exemple #1
0
    def _check_conflict(self, p1, p2):
        # Does p1 conflict with p2
        for conflict in p1.conflicts:
            cq = Query(conflict)
            if cq.matches(p2) and not self._does_bridge_after(p1, p2):
                return (p1, p2)

        # Does p2 conflict with p1
        for conflict in p2.conflicts:
            cq = Query(conflict)
            if cq.matches(p1) and not self._does_bridge_after(p2, p1):
                return p2, p1
Exemple #2
0
 def _dependencies_filled_by_other(self, package):
     for dep_string in package.dependecies:
         dep_packages = self.local_repo.find_packages(Query(dep_string),
                                                      exclude=self.targets)
         if not dep_packages:
             return False
     return True
Exemple #3
0
    def _does_bridge(self, local_repo, targets, p1, p2):
        # Do we have a bridge already installed?
        installed_bridges = local_repo.find_bridges(p1, p2)
        if installed_bridges:
            return True

        # Do any of the packages we are about to install offer to bridge this?
        for package in targets:
            for bridge in package.bridges:
                b1 = Query(bridge[0])
                b2 = Query(bridge[1])
                if b1.matches(p1) and b2.matches(p2):
                    return True
                elif b2.matches(p1) and b1.matches(p2):
                    return True
        return False
Exemple #4
0
def visualize():
    import networkx as nx
    # How about we don't fire up a JVM just to start my script?
    from asciinet import graph_to_ascii

    G = nx.DiGraph()
    root = "Root"

    G.add_node(root)

    # @ENHANCEMENT
    # Right now we just visualize it as a stright up dependency graph. We might
    # want to show when we use provides instead in the future. This would
    # involve adding a fake node when we look for a provides and let that
    # depend on the actual implementors
    for package in local_repo.get_all_packages():
        G.add_node(package)
        if package.reason == InstallReason.REQ:
            G.add_edge(root, package)
        for dep_str in package.dependecies:
            q = Query(dep_str)
            dep = local_repo.find_package(q)
            if dep is None:
                print(package, q)
            G.add_edge(package, dep)

    print(graph_to_ascii(G))
Exemple #5
0
    def _expand_depedencies(self, local_repo, repo, targets, exclude=set()):
        missing = set()
        resolved = set(targets)
        queue = list(targets)
        seen = set()
        while queue:
            package = queue.pop()
            if package in seen:
                continue
            seen.add(package)

            # We don't want to expand dependencies for a package that is
            # already installed. The user might have intentionally ignored
            # dependencies when they installed the package, so we are just
            # going to assume they know what they are doing.
            if package.is_local:
                continue

            for dep_name in package.dependecies:
                q = Query(dep_name)
                dep_package = self._find_satisfier(local_repo,
                                                   repo,
                                                   q,
                                                   targets,
                                                   exclude=exclude)

                # We couldn't find a satisfier
                if dep_package is None:
                    missing.add((package, q))
                    continue

                resolved.add(dep_package)
                queue.append(dep_package)
        return (resolved, missing)
Exemple #6
0
 def _find_bridge_conflicts(self):
     conflicts = set()
     for package in self.removes:
         for (b1, b2) in package.bridges:
             p1 = self.local_repo.find_package(Query(b1))
             p2 = self.local_repo.find_package(Query(b2))
             if not p1 or not p2:
                 # One of the sides were not installed, which means it's
                 # fine
                 continue
             if p1 in self.removes or p2 in self.removes:
                 # We are about to remove one of the sides. It's fine
                 continue
             conflict = self._check_conflict(p1, p2)
             if conflict:
                 conflicts.add((package, conflict))
     return conflicts
Exemple #7
0
def details(package_q):
    q = Query(package_q)
    package = local_repo.find_package(q)
    if package is None:
        print("No package named {Style.BRIGHT}{}{Style.RESET_ALL}".format(
            q, Style=Style))
        exit(1)
    print_local_package(local_repo, package)
Exemple #8
0
 def _rate_satisfier(self, local_repo, package):
     if not package.dependecies:
         return 0
     cnt = 0
     for dep_str in package.dependecies:
         q = Query(dep_str)
         if self.local_repo.find_package(q) is not None:
             continue
         cnt += 1
     return cnt / len(package.dependecies)
Exemple #9
0
    def _find_package_upgrade(self, package):
        # Ignore any package that is already in the targets array, regardless
        # of version
        if package in self.targets:
            return

        # Just search for the name and do the version comparison manually
        q = Query(package.name)
        remote_package = self.repo.find_literal(q)
        if remote_package.version > package.version:
            self.targets.add(remote_package)
Exemple #10
0
 def _find_upgrade_required(self, targets):
     upgrade = set()
     for package in targets:
         assert (package.is_local)
         q = Query(package.name)
         remote_package = self.repo.find_literal(q)
         if remote_package is None:
             print("No remote package for {}".format(package))
             continue
         if remote_package.version > package.version:
             upgrade.add((package, remote_package))
     return upgrade
Exemple #11
0
    def _find_conflicts(self, targets, local_repo):
        # Find conflicts inside targets
        conflicts = set()
        for t in targets:
            for conflict in t.conflicts:
                cq = Query(conflict)
                for tc in targets:
                    # Packages don't conflict with themselves
                    if t == tc:
                        continue
                    if (cq.matches(tc) and
                            not self._does_bridge(local_repo, targets, t, tc)):
                        conflicts.add((t, tc))

        local_packages = local_repo.get_all_packages()
        # Find conflicts from targets to local_repo
        for t in targets:
            for conflict in t.conflicts:
                cq = Query(conflict)
                for lp in local_packages:
                    # Packages don't conflict with themselves
                    if t == lp:
                        continue
                    if (cq.matches(lp) and
                            not self._does_bridge(local_repo, targets, t, lp)):
                        conflicts.add((t, lp))

        # Find conflicts from local_repo to targets
        for lp in local_packages:
            for conflict in lp.conflicts:
                cq = Query(conflict)
                for tc in targets:
                    # Packages don't conflict with themselves
                    if lp == tc:
                        continue
                    if (cq.matches(tc) and not self._does_bridge(
                            local_repo, targets, lp, tc)):  # NOQA
                        conflicts.add((lp, tc))
        return conflicts
Exemple #12
0
def check(packages):
    init()

    if packages:
        qs = [Query(p) for p in packages]
        packages = []
        for q in qs:
            package = repo.find_package(q)
            if package is None:
                print("No package named "
                      "{Style.BRIGHT}{}{Style.RESET_ALL}".format(q,
                                                                 Style=Style))
                return
            packages.append(package)
    else:
        # A bit hacky, but this is a debug command, so i'm not too worried.
        packages = [repo._load_package(p) for p in repo._all_packages()]

    owners = {}
    fetches = set()
    for package in packages:
        print("Collecting sources from {}".format(package.name))
        for s in package.sources:
            # We need the owners to report who requested a download later
            if s.uri not in owners:
                owners[s.uri] = []
            owners[s.uri].append(package)

            fetches.add((s.uri))
    # Actually do the check, this should use some fast method in the individual
    # handlers
    checked = downloader.check(fetches)

    anyFail = False
    for (uri, result) in checked.items():
        # Only report if something went wrong
        if not result:
            print("Failed checking uri "
                  "{Style.BRIGHT}{Fore.RED}{}{Style.RESET_ALL} "
                  "requested by:".format(uri, Style=Style, Fore=Fore))
            # Print the packages that depend on the download, helps find the
            # culprit in large checks
            for package in owners[uri]:
                print(f"\t{package.name}")
            anyFail = True

    # If everything went well let's just print something to instill some
    # confidence
    if not anyFail:
        print("Everything was good")
Exemple #13
0
 def packages_to_graph(self, targets):
     G = nx.DiGraph()
     for package in targets:
         G.add_node(package)
         for dep_name in package.dependecies:
             q = Query(dep_name)
             dep_package = self._find_satisfier_in_set(targets, q)
             if dep_package is None:
                 # Swallow the error, since this is expected in some cases
                 continue
                 # raise Exception(
                 #     "Tried to make a graph out of packages that "
                 #     "have unresolved dependencies: " + str(q)
                 # )
             G.add_edge(package, dep_package)
     return G
Exemple #14
0
 def _get_owned(self):
     owned = set(self.targets)
     # Here we want to process the same package multiple times
     queue = set(self.targets)
     while queue:
         package = queue.pop()
         if package.reason == InstallReason.DEP:
             for depa in self.local_repo.find_dependants(package):
                 if depa not in owned:
                     break
             else:
                 owned.add(package)
         for dep_str in package.dependecies:
             deps = self.local_repo.find_packages(Query(dep_str))
             for dep in deps:
                 queue.add(dep)
     return owned
Exemple #15
0
def search(term, reverse):

    candidates = repo.search(" ".join(term))
    if reverse:
        candidates = list(reversed(candidates))

    lst = PackageList(candidates)
    for package in lst:
        # Set all the tags
        q = Query(package.name)
        local_pkg = local_repo.find_package(q)

        # Having a local package means it's currently installed. Fill in the
        # tag with the version
        if local_pkg:
            lst.add_tag(package, InstalledTag(local_pkg.version))

    lst.present()
Exemple #16
0
    def expand(self):
        assert (self.state == TransactionState.INIT)

        (expanded, missing) = self._expand_depedencies(self.local_repo,
                                                       self.repo,
                                                       self.targets,
                                                       exclude=self.removes)
        if len(missing) > 0:
            raise MissingDependencyError(missing)

        self.depend_G = super().packages_to_graph(expanded)

        try:
            cycle = next(nx.simple_cycles(self.depend_G))
        except StopIteration:
            pass
        else:
            raise TransactionCycleError(cycle)

        self._sort_deps_to_targets()

        # Find all the packages that are upgrades rather than installs
        for package in expanded:
            if not package.is_local:
                # Search for just the name to check if we have any version
                # locally
                q = Query(package.name)
                local_package = self.local_repo.find_package(q)
                # If we have something we want to remove it first, we call that
                # an "upgrade"
                if local_package is None:
                    continue
                self.removes.append(local_package)

        conflicts = super()._find_conflicts(self.installs, self.local_repo)
        if len(conflicts) > 0:
            # There were at least some conflicts
            raise ConflictError(conflicts)
        self.state = TransactionState.EXPANDED
Exemple #17
0
    def make_profile(self, local_repo):
        modlist_path = self.cfg.profile_dir / "modlist.txt"
        with open(modlist_path, "w") as f:
            G = nx.DiGraph()
            for package in local_repo.get_all_packages():
                G.add_node(package)
                for dep_name in package.dependecies:
                    q = Query(dep_name)
                    dep = local_repo.find_package(q)

                    if dep is None:
                        # Skip mising dependecies. If we have an installed
                        # package which has a not installed dependency, then we
                        # just want to skip it. It's up to the user to make
                        # sure everything is resolved, of course assisted by
                        # the tool.  @COMPLETE it might be useful give the user
                        # some way of doing a full dependency verification of
                        # the local repo
                        continue

                    G.add_edge(package, dep)
            for package in nx.lexicographical_topological_sort(
                    G,
                    key=lambda x: x.priority):
                print("+" + package.name, file=f)
            print("*Unmanaged: Dawnguard", file=f)
            print("*Unmanaged: Dragonborn", file=f)
            print("*Unmanaged: HearthFires", file=f)
            print("*Unmanaged: HighResTexturePack01", file=f)
            print("*Unmanaged: HighResTexturePack02", file=f)
            print("*Unmanaged: HighResTexturePack03", file=f)
            print("*Unmanaged: Unofficial Dawnguard Patch", file=f)
            print("*Unmanaged: Unofficial Dragonborn Patch", file=f)
            print("*Unmanaged: Unofficial Hearthfire Patch", file=f)
            print("*Unmanaged: Unofficial High Resolution Patch", file=f)
            print("*Unmanaged: Unofficial Skyrim Patch", file=f)
Exemple #18
0
def remove(packages, no_dep):
    # Skipping dependency checking can be dangerous. Lets print a nice big
    # warning to make sure the user knows what they are doing.
    if no_dep:
        print("{Fore.YELLOW}{Style.BRIGHT}Warning:{Style.RESET_ALL}"
              " depedency checking disabled".format(Style=Style, Fore=Fore))
    qs = [Query(p) for p in packages]
    t = RemoveTransaction(local_repo, repo, downloader, no_dep)
    for q in qs:
        package = local_repo.find_package(q)
        if package is None:
            print(
                "No installed package named {Style.BRIGHT}{}{Style.RESET_ALL}".
                format(q, Style=Style))
            return
        t.add(package)

    try:
        t.expand()
    except ConflictError as e:
        # Removing these packages would mean creating these new conflicts
        # This is possible because we have bridges
        print("{Fore.RED}Conflicting packages{Fore.RESET}".format(Fore=Fore))
        for (bridge, conflict) in e.conflicts:
            print("Removing {Style.BRIGHT}{}{Style.RESET_ALL} would break "
                  "bridge of {Style.BRIGHT}{}{Style.RESET_ALL} and "
                  "{Style.BRIGHT}{}{Style.RESET_ALL}".format(bridge,
                                                             *conflict,
                                                             Style=Style))
            bridges = repo.find_bridges(*conflict, exclude=t.targets)
            for bridge in bridges:
                print("\t{Style.BRIGHT}{}{Style.RESET_ALL} is an "
                      "alternative bridge".format(bridge, Style=Style))
            if not bridges:
                print("\t {Fore.RED}Unfortunately{Fore.RESET} I don't know of "
                      "any alternative".format(Fore=Fore))
        exit(1)
    except DependencyBreakError as e:
        print("")
        print(
            "Removal would {Style.BRIGHT}break{Style.RESET_ALL} dependencies: "
            .format(Style=Style))
        for p in e.dependencies:
            print("\t{Style.BRIGHT}{}{Style.RESET_ALL} depends on "
                  "{Style.BRIGHT}{}{Style.RESET_ALL}".format(*p, Style=Style))
            exit(1)

    print("")
    print("This will remove the following packages: ")
    print("PACKAGES: {Style.BRIGHT}{}".format(", ".join(map(str, t.removes)),
                                              Style=Style))
    print("")
    Q.yes_no("Are you sure?")

    try:
        t.prepare()
    # @DEAD
    except DependencyBreakError as e:
        print("")
        print(
            "Removal would {Style.BRIGHT}break{Style.RESET_ALL} dependencies: "
            .format(Style=Style))
        for p in e.dependencies:
            print("\t{Style.BRIGHT}{}{Style.RESET_ALL} depends on "
                  "{Style.BRIGHT}{}{Style.RESET_ALL}".format(*p, Style=Style))
            exit(1)

    t.commit()
    organizer.make_profile(local_repo)
Exemple #19
0
def install(packages, explicit, upgrade, reason):
    # Create the queries from the strings
    qs = [Query(p) for p in packages]
    if upgrade:
        t = UpgradeTransaction(local_repo, repo, downloader)
    else:
        t = AddTransaction(local_repo, repo, downloader, reason, src_cache)

    # Support for loading out of tree package specifications
    for e_path in explicit:
        # @HACK this should be a config option and also maybe not specified
        # here?
        pkgsrc = cfg.source.dir
        e = Path(e_path)
        if not e.exists():
            print("Explicit package at {Style.BRIGHT}{}{Style.RESET_ALL} "
                  "not found".format(e, Style=Style, Fore=Fore))
            exit(1)
        package = load_package(e, pkgsrc, organizer.getModsDir())
        t.add(package)

    for q in qs:
        package = repo.find_package(q)
        if package is None:
            print("No package named {Style.BRIGHT}{}{Style.RESET_ALL}".format(
                q, Style=Style))
            return
        t.add(package)

    try:
        t.expand()
    except TransactionCycleError as e:
        # Some form of dependency cycle
        # @ENHANCEMENT We should really run visualize on the graph that failed.
        # That would be way more helpful
        # -- We can't use asciinet since installing it is a pain
        print("ERROR Cycle detected: ")
        print("\tCycle consists of: {}".format(", ".join(
            (p.name for p in e.cycle))))
        exit(1)
    except ConflictError as e:
        # We found some conflicts which means we need to look for some package
        # to bridge them that aren't part of the transaction
        print("{Fore.RED}Conflicting packages{Fore.RESET}".format(Fore=Fore))
        for conflict in e.conflicts:
            print("{Style.BRIGHT}{}{Style.RESET_ALL} conflicts with "
                  "{Style.BRIGHT}{}{Style.RESET_ALL}".format(*conflict,
                                                             Style=Style))
            bridges = repo.find_bridges(*conflict)
            for bridge in bridges:
                print("\t{Style.BRIGHT}{}{Style.RESET_ALL} could bridge "
                      "that conflict".format(bridge, Style=Style))
        exit(1)
    except MissingDependencyError as e:
        print(
            "{Fore.RED}Unresolved dependencies{Fore.RESET}".format(Fore=Fore))
        for missing in e.dependencies:
            print(
                "{Style.BRIGHT}{}{Style.RESET_ALL} requires "
                "{Style.BRIGHT}{}{Style.RESET_ALL} which wasn't found".format(
                    *missing, Style=Style))
        exit(1)

    if not t.targets:
        print("Nothing to do".format(Style=Style, Fore=Fore))
        exit(0)

    print("")
    print("This will install the following packages: ")
    print("-> " + Style.BRIGHT + ", ".join(map(str, t.installs)))
    print("")
    Q.yes_no("Are you sure?")

    t.prepare()
    t.commit()
    organizer.make_profile(local_repo)
Exemple #20
0
    def expand(self):
        # If theres no targets, expand to all local packages
        if not self.targets:
            self.targets = self.local_repo.get_all_packages()

        upgrades = self._find_upgrade_required(self.targets)
        self.removes = [u[0] for u in upgrades]
        self.installs = [u[1] for u in upgrades]
        self.targets = [
            u[1] for u in upgrades if u[0].reason == InstallReason.REQ
        ]

        # So the dependencies might have changed. I don't think we want to
        # remove unused dependencies, but we do want to pull in new ones. We
        # might also stop providing something, which could invalidate some
        # other part of the local database.
        # Basically we will want to do the following:
        #   - Check if all dependencies are filled
        #   - Fill new ones (maybe asking?)
        #   - Check that all the people depending on us still have their
        #   dependencies filled after the transaction
        # During all of these steps we need to make sure that we aren't
        # matching packages that this transaction is going to remove, but
        # include packages we are about to install.

        # Find all packages touched by something we are removing

        new_missing = set()

        for package in self.removes:
            dependants = self.local_repo.find_dependants(package)
            for dependant in dependants:

                # If the dependant is queued for removal we don't really care
                # if we are going to break dependencies
                if dependant in self.removes:
                    continue

                for dep_str in dependant.dependecies:
                    q = Query(dep_str)
                    # If it didn't match before then we don't care
                    # This is important because we don't want to pop up and
                    # error now if the user purposefully broke some
                    # dependencies at some point, but if we are breaking
                    # something we want to report that
                    if not q.matches(package):
                        continue
                    # Are we going to install something that fixes it?
                    dep_sat = super()._find_satisfier_in_set(self.installs, q)
                    if dep_sat is not None:
                        continue
                    # Do we have something else that fixes it?
                    dep_sat = self.local_repo.find_package(
                        q, exclude=self.removes)
                    if dep_sat is not None:
                        continue
                    # This upgrade is going to be a problem
                    # @COMPLETE We should dump the package here as well.
                    new_missing.add((dependant, q))

        if len(new_missing) > 0:
            raise MissingDependencyError(new_missing)

        # @HACK: Reset removes, since the add transaction finds all packages
        # that are actually upgrades
        self.removes = []

        self.targets = self.installs
        super().expand()