Hi, On Mon, 17 Oct 2016 07:20:43 +0800 Paul Wise <p...@debian.org> wrote: > I too would like to see a satisfy subcommand that could be run like this: > > apt satisfy 'python3, pyflakes3 | pyflakes (>= 0.7.3), pyflakes3 | pyflakes > (<< 1.1.0-1)' > > The reason I want this is so that I can make check-all-the-things run apt > for checks that have missing commands and get the right packages installed > no matter what the distribution it is being run on. > > $ check-all-the-things --install apt --checks pyflakes3 > sudo aptÃÂ satisfy 'python3, pyflakes3 | pyflakes (>= 0.7.3), pyflakes3 | > pyflakes (<< 1.1.0-1)' > > Right now there are workarounds that can be used for this being missing: > > * create a debian/control file containing a single Build-Depends line (apt > > 1.2.2)
this has the disadvantage of always pulling in the build-essential package. > * create a full Debian binary package containing the right Depends This has the disadvantage that it installs a meta-package on the user's system. > * Use python-apt to do the resolution and emit `apt install pkg=1.2.3` > commands? I don't see a way to achieve this with python-apt. Even when manually iterating through the given dependency string and checking which package in the apt cache satisfies the dependency, the solution would not always be correct. Here is an example in which this approach would fail: Suppose there are the packages A, B, C and D. A depends on D and D conflicts with C. Now the wants the following resolved: 'A | B, C' With the algorithm sketched above, A and C would be picked for installation but the solution would be invalid because A and C conflict via D. To avoid this situation, the code has to traverse all dependency trees which would in turn lead to the implementation of a full-fledged resolver. Certainly this is not desired. > The first two are easy but I'm not sure about the last one. I see no way to use python-apt for tapping into apt's own resolver. On IRC you also mentioned the possibility to use dose3. A solution using dose3 would look the following way: Use apt-get indextargets to get the list of Packages files apt uses and then use "apt-helper cat-file" to feed the information to dose-deb-coinstall as the background packages. Then as the foreground packages, pass a dummy binary package containing a Depends line that you want to satisfy. The tool will then output an installation set in deb822 format on standard output. While this will create a valid solution, it might not be a very good solution because the package selection does not take into account the packages that are already installed on the user's system and it also does not adhere to apt's pinning or its idea about installing real packages before virtual ones. In this email I want to create a fourth option. It takes the installed packages into account, uses the apt resolver and even respects pinning values. We'll be using apt's EDSP interface. Essentially the solution works by creating an EDSP file that represents the current state of the system and then adding a custom header and dummy binary package to it and feed all that to /usr/lib/apt/solvers/apt. The EDSP returned by the program is then the solution that can be mapped to package name/version/arch triplets which can be used to assemble an apt-get install command line. You can find my script attached. You use it like this: $ ./apt-satisfy "apache2 (>= 1.3.37) | httpd" apt install libaprutil1:amd64=1.5.4-3 libaprutil1-dbd-sqlite3:amd64=1.5.4-3 apache2-data:all=2.4.23-8 libaprutil1-ldap:amd64=1.5.4-3 apache2-bin:amd64=2.4.23-8 apache2:amd64=2.4.23-8 apache2-utils:amd64=2.4.23-8 apt-mark auto libaprutil1:amd64 libaprutil1-dbd-sqlite3:amd64 apache2-data:all libaprutil1-ldap:amd64 apache2-bin:amd64 apache2:amd64 apache2-utils:amd64 It prints two commands that you can directly execute. The first command installs the right packages and the second command marks the newly installed packages as automatically installed. The latter is to make sure that these packages which have no real origin (the user never directly requested a real package to be installed) can easily be autoremoved. In the context of check-all-the-things, marking the packages as auto will not even lead to an autoremoval because check-all-the-things Recommends them. What do you think? Thanks! cheers, josch
#!/bin/sh set -eu depends=$@ edspfile=$(mktemp) solution=$(mktemp) trap "rm -r \"$edspfile\" \"$solution\"" EXIT # simulate installation of a random package - we choose apt because that has to exist # # we expect the dump solver to fail with exit 100 APT_EDSP_DUMP_FILENAME="$edspfile" apt-get --simulate install --solver dump apt >/dev/null 2>&1 && [ $? -eq 100 ] request=$(grep-dctrl -F Request '' -s Request -n "$edspfile") architecture=$(grep-dctrl -F Request '' -s Architecture -n "$edspfile") architectures=$(grep-dctrl -F Request '' -s Architectures -n "$edspfile") # generate the APT-ID of the dummy package from the largest id plus one newid=$(grep-dctrl -F Package '' -s APT-ID -n "$edspfile" | sort -n | tail -1) newid=$((newid+1)) # Replace the EDSP header and add a dummy package at the top { cat << END Request: $request Architecture: $architecture Architectures: $architectures Install: apt-satisfy-dummy:$architecture Package: apt-satisfy-dummy Architecture: $architecture Version: 1 APT-ID: $newid Depends: $depends APT-Pin: 990 END grep-dctrl -F Package '' "$edspfile"; } | /usr/lib/apt/solvers/apt > "$solution" # FIXME: instead of using grep and cat we want to use grep-dctrl but # unfortunately the EDSP that /usr/lib/apt/solvers/apt outputs is not valid # deb822 if grep Error: "$solution"; then cat "$solution" exit fi selector=$(grep-dctrl -F Install '' -n -s Install "$solution" | paste -s -d"|") install=$(grep-dctrl --eregex -F APT-ID "^($selector)$" -n -s Package,Architecture,Version "$edspfile" \ | paste -sd ":= " ) echo apt install $install auto=$(grep-dctrl --eregex -F APT-ID "^($selector)$" -n -s Package,Architecture "$edspfile" \ | paste -sd ": " ) echo apt-mark auto $auto
signature.asc
Description: signature