Testsuite: Run UIs in Qemu and check running processes and more (#982)

* Testsuite: Run UIs in Qemu and check running processes (and other changes)

* When `pmbootstrap qemu` gets killed, it now takes down the Qemu process with it
* `test/check_checksums.py` got a new optional `--build` parameter, which makes
  it build all changed packages instead of just checking the checksums
* We run this before running the testsuite now, so all changed packages get
  built before running tests (otherwise tests would hang without any output
  while a changed package is building)
* New testcase, that zaps all chroots, installs a specific UI (xfce4 and
  plasma-mobile currently, easy to extend), runs it via Qemu and checks the
  running processes via SSH.
* Version checking testcase: rewritten to include Alpine's testsuite file in
  our source tree, so we don't need to clone their git repo anymore. Now it
  is enabled for Travis.
* All this gives us a nice 10% code coverage boost
* Increased the `hello-world` pkgrel to verify that the Travis job is working.

* Various fixes
* Build device-packages for the device arch and don't raise an
  exception, but print a note if --ignore-depends is not specified
  and therefore the kernel gets installed, too.
* Don't use --force when building in Travis (because abuild doesn't
  check the checksums then. Bug report on the way.)
* Don't run the building process in the background, but wait for its
  completion
* Exit with 1 when showing usage in check_checksums.py
This commit is contained in:
Oliver Smith 2018-02-02 00:16:29 +00:00 committed by GitHub
parent 5eb75afc08
commit 5e85d72ca0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 996 additions and 60 deletions

View File

@ -5,15 +5,15 @@ addons:
apt:
sources:
- debian-sid
packages:
- shellcheck
install: "pip install flake8 pytest-cov python-coveralls"
install:
- sudo apt install shellcheck qemu-system-x86
- pip install flake8 pytest-cov python-coveralls
script:
- test/static_code_analysis.sh
- yes "" | ./pmbootstrap.py init
- ./pmbootstrap.py kconfig_check
- test/check_checksums.py --build
- test/testcases_fast.sh
- test/check_checksums.py
after_success:
- coveralls
after_failure:

View File

@ -1,6 +1,6 @@
pkgname=hello-world
pkgver=1
pkgrel=3
pkgrel=4
pkgdesc="hello world program to be built in the testsuite"
url="https://en.wikipedia.org/wiki/%22Hello,_World!%22_program"
arch="all"

View File

@ -17,11 +17,37 @@ You should have received a copy of the GNU General Public License
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
"""
import fnmatch
import logging
import os
import pmb.config
import pmb.chroot.apk
import pmb.parse.arch
def arch_from_deviceinfo(args, pkgname, aport):
"""
The device- packages are noarch packages. But it only makes sense to build
them for the device's architecture, which is specified in the deviceinfo
file.
:returns: None (no deviceinfo file)
arch from the deviceinfo (e.g. "armhf")
"""
# Require a deviceinfo file in the aport
if not pkgname.startswith("device-"):
return
deviceinfo = aport + "/deviceinfo"
if not os.path.exists(deviceinfo):
return
# Return its arch
device = pkgname.split("-", 1)[1]
arch = pmb.parse.deviceinfo(args, device)["arch"]
logging.verbose(pkgname + ": arch from deviceinfo: " + arch)
return arch
def arch(args, pkgname):
"""
Find a good default in case the user did not specify for which architecture
@ -32,6 +58,10 @@ def arch(args, pkgname):
APKBUILD.
"""
aport = pmb.build.find_aport(args, pkgname)
ret = arch_from_deviceinfo(args, pkgname, aport)
if ret:
return ret
apkbuild = pmb.parse.apkbuild(args, aport + "/APKBUILD")
if "noarch" in apkbuild["arch"] or "all" in apkbuild["arch"]:
return args.arch_native

View File

@ -43,48 +43,28 @@ import pmb.parse
import pmb.qemu
def _build_verify_usage_device_package(args, pkgname):
def _build_device_depends_note(args, pkgname):
"""
Detect if the user is about to build a device- package for the wrong
architecture. The package is noarch, but the dependencies (kernel!) will get
pulled in with the same arch as dependency.
Previously 'pmbootstrap build device-...' built the device package in the
native chroot without installing its dependencies (e.g. armhf kernel!) and
created a symlink to all supported architectures.
Not installing depends while building is incompatible with how Alpine's
abuild does it, so we changed the behavior. Now pmbootstrap reads the
device's architecture from the deviceinfo file and automatically builds
for that architecture, if you did not specify any architecture. And the
dependencies get installed correctly before the build.
To make migration easier for the users, we show a hint if building a device
package was requested.
"""
# Skip non-device-packages
if not pkgname.startswith("device-"):
# Only relevant for device packages when -i is not set
if not pkgname.startswith("device-") or getattr(args, "ignore_depends"):
return
# Only continue when the --arch parameter is *not* the device architecture
deviceinfo = args.aports + "/device/" + pkgname + "/deviceinfo"
if not os.path.exists(deviceinfo):
return
device = pkgname.split("-", 1)[1]
arch = pmb.parse.deviceinfo(args, device)["arch"]
if args.arch == arch:
return
# Abort with a big note
logging.info("Dependency handling in 'pmbootstrap build' has been"
" changed.")
logging.info("Previously we only built and installed the 'makedepends'"
" from the APKBUILDs, now we use the 'depends', too.")
logging.info("")
logging.info("Your options:")
logging.info("* Ignore depends (fast, old behavior, may cause problems"
" with some packages):")
logging.info(" pmbootstrap build " + pkgname + " -i")
logging.info("* Build with depends (kernel!) and specify the right"
" architecture:")
logging.info(" pmbootstrap build " + pkgname + " --arch=" + arch)
logging.info("")
logging.info("This change was necessary to be more compatible with Alpine's"
" abuild.")
logging.info("The default architecture is the native one (" +
args.arch_native + " in your case), so you need to overwrite")
logging.info("it now to get the kernel dependency of your device package"
" for the right architecture.")
logging.info("Sorry for the inconvenience.")
logging.info("")
raise RuntimeError("Missing -i or --arch parameter")
logging.info("NOTE: " + device + "'s kernel will be installed as dependency"
" before building (old behavior: 'pmbootstrap build -i')")
def _parse_flavor(args):
@ -135,10 +115,10 @@ def build(args):
if args.strict:
pmb.chroot.zap(args, False)
# Detect wrong usage for device- packages
# Detect old usage for device- packages
if not args.ignore_depends:
for package in args.packages:
_build_verify_usage_device_package(args, package)
_build_device_depends_note(args, package)
# Build all packages
for package in args.packages:

View File

@ -18,8 +18,9 @@ along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
"""
import logging
import os
import shutil
import re
import signal
import shutil
import pmb.build
import pmb.chroot
@ -218,6 +219,11 @@ def resize_image(args, img_size_new, img_path):
raise RuntimeError("The system image size must be " + img_size_str + " or greater")
def sigterm_handler(number, frame):
raise RuntimeError("pmbootstrap was terminated by another process,"
" and killed the Qemu VM it was running.")
def run(args):
"""
Run a postmarketOS image in qemu
@ -253,13 +259,15 @@ def run(args):
logging.info("* (ssh) ssh -p {port} {user}@localhost".format(**vars(args)))
logging.info("* (telnet) telnet localhost " + str(args.port + 1))
# Run Qemu (or Qemu + SPICE)
# Run Qemu (or Qemu + SPICE) and kill it together with pmbootstrap
process = None
try:
signal.signal(signal.SIGTERM, sigterm_handler)
process = pmb.helpers.run.user(args, qemu, background=spice_enabled)
if spice:
pmb.helpers.run.user(args, spice)
except KeyboardInterrupt:
# Don't show a trace when pressing ^C
pass
finally:
if process:

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python3
import os
import subprocess
import sys
def get_changed_files():
@ -62,7 +63,27 @@ def check_checksums(package):
exit(1)
def check_build(packages):
command = (["./pmbootstrap.py", "--details-to-stdout", "build",
"--strict"] + list(packages))
try:
process = subprocess.Popen(command)
process.communicate()
except subprocess.CalledProcessError as e:
print("** Building failed")
exit(1)
if __name__ == "__main__":
# Allow to specify "--build" to build instead of only verifying checksums
build = False
if len(sys.argv) > 1:
if sys.argv[1] == "--build":
build = True
else:
print("usage: {} [--build]".format(sys.argv[0]))
exit(1)
if 'TRAVIS_COMMIT_RANGE' in os.environ:
print('Checking commit range: {}'.format(os.environ['TRAVIS_COMMIT_RANGE']))
if 'TRAVIS_PULL_REQUEST_BRANCH' in os.environ:
@ -74,6 +95,10 @@ if __name__ == "__main__":
print("No aports packages changed in this commit")
exit(0)
for package in packages:
print("Checking {} for correct checksums".format(package))
check_checksums(package)
if build:
print("Building in strict mode: " + ", ".join(packages))
check_build(packages)
else:
for package in packages:
print("Checking {} for correct checksums".format(package))
check_checksums(package)

View File

@ -0,0 +1,169 @@
"""
Copyright 2017 Oliver Smith
This file is part of pmbootstrap.
pmbootstrap is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
pmbootstrap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
"""
"""
This file runs various installations and boots into them with Qemu, then checks
via SSH if expected processes are running.
We use an extra config file (based on ~/.config/pmbootstrap.cfg), because we
need to change it a lot (e.g. UI, username, ...).
"""
import os
import pytest
import sys
import shutil
import shlex
import time
import logging
# Import from parent directory
pmb_src = os.path.realpath(os.path.join(os.path.dirname(__file__) + "/.."))
sys.path.append(pmb_src)
import pmb.chroot.apk_static
import pmb.parse.apkindex
import pmb.helpers.logging
import pmb.helpers.run
import pmb.parse.bootimg
@pytest.fixture
def args(request):
import pmb.parse
sys.argv = ["pmbootstrap.py", "chroot"]
args = pmb.parse.arguments()
args.log = args.work + "/log_testsuite.txt"
pmb.helpers.logging.init(args)
request.addfinalizer(args.logfd.close)
return args
def ssh_create_askpass_script(args):
"""Create /tmp/y.sh, which we need to automatically login via SSH."""
with open(args.work + "/chroot_native/tmp/y.sh", "w") as handle:
handle.write("#!/bin/sh\necho y\n")
pmb.chroot.root(args, ["chmod", "+x", "/tmp/y.sh"])
def pmbootstrap_run(args, config, parameters, background=False):
"""Execute pmbootstrap.py with a test pmbootstrap.conf."""
return pmb.helpers.run.user(args, ["./pmbootstrap.py", "-c", config] +
parameters, working_dir=pmb_src,
background=background)
def pmbootstrap_yes(args, config, parameters):
"""
Execute pmbootstrap.py with a test pmbootstrap.conf, and pipe "yes"
into it (so we can do a fully automated installation, using "y" as
password everywhere).
"""
command = "yes | ./pmbootstrap.py -c " + shlex.quote(config)
for parameter in parameters:
command += " " + shlex.quote(parameter)
return pmb.helpers.run.user(args, ["/bin/sh", "-c", command],
working_dir=pmb_src)
class Qemu(object):
def __init__(self, request):
self.process = None
request.addfinalizer(self.terminate)
def terminate(self):
if self.process:
self.process.terminate()
else:
print("WARNING: The Qemu process wasn't set, so it could not be"
" terminated.")
def run(self, args, tmpdir, ui="none"):
# Copy and adjust user's pmbootstrap.cfg
config = str(tmpdir) + "/pmbootstrap.cfg"
shutil.copyfile(os.path.expanduser("~") + "/.config/pmbootstrap.cfg",
config)
pmbootstrap_run(args, config, ["config", "device", "qemu-amd64"])
pmbootstrap_run(args, config, ["config", "extra_packages", "none"])
pmbootstrap_run(args, config, ["config", "user", "testuser"])
pmbootstrap_run(args, config, ["config", "ui", ui])
pmbootstrap_run(args, config, ["config", "qemu_native_mesa_driver", "dri-swrast"])
# Prepare native chroot
pmbootstrap_run(args, config, ["-y", "zap"])
pmb.chroot.apk.install(args, ["openssh-client"])
ssh_create_askpass_script(args)
# Create and run system image
pmbootstrap_yes(args, config, ["install", "--no-fde"])
self.process = pmbootstrap_run(args, config, ["qemu", "--display",
"none"], background=True)
logging.info("(test) wait 90s until the VM booted up")
time.sleep(90)
@pytest.fixture
def qemu(request):
return Qemu(request)
def ssh_run(args, command):
"""
Run a command in the Qemu VM on localhost via SSH.
:param command: flat string of the command to execute, e.g. "ps au"
:returns: the result from the SSH server
"""
ret = pmb.chroot.user(args, ["SSH_ASKPASS=/tmp/y.sh", "DISPLAY=", "ssh",
"-o", "UserKnownHostsFile=/dev/null",
"-o", "StrictHostKeyChecking=no",
"-p", "2222", "testuser@localhost", "--",
command],
return_stdout=True)
return ret
def is_running(args, programs):
"""
Simple check that looks for program names in the output of "ps ax".
This is error-prone, only use it with programs that have a unique name.
"""
all = ssh_run(args, "ps ax")
ret = True
for program in programs:
if program in all:
continue
print(program + ": not found in 'ps ax'!")
ret = False
return ret
def test_xfce4(args, tmpdir, qemu):
qemu.run(args, tmpdir, "xfce4")
assert is_running(args, ["xfce4-session", "xfdesktop", "xfce4-panel",
"Thunar", "dbus-daemon", "xfwm4"])
# self-test of is_running()
assert is_running(args, ["invalid-process"]) is False
def test_plasma_mobile(args, tmpdir, qemu):
# NOTE: Once we have plasma mobile running properly without GL, we can check
# for more processes
qemu.run(args, tmpdir, "plasma-mobile")
assert is_running(args, ["polkitd"])

View File

@ -21,8 +21,8 @@ import sys
import pytest
# Import from parent directory
sys.path.append(os.path.realpath(
os.path.join(os.path.dirname(__file__) + "/..")))
pmb_src = os.path.realpath(os.path.join(os.path.dirname(__file__) + "/.."))
sys.path.append(pmb_src)
import pmb.helpers.git
import pmb.helpers.logging
import pmb.parse.version
@ -43,11 +43,8 @@ def test_version(args):
# Fail after the first error or print a grand total of failures
keep_going = False
# Clone official test file from apk-tools
pmb.helpers.git.clone(args, "apk-tools")
path = args.work + "/cache_git/apk-tools/test/version.data"
# Iterate over the cases from the list
# Iterate over the version tests from apk-tools
path = pmb_src + "/test/testdata/version/version.data"
mapping = {-1: "<", 0: "=", 1: ">"}
count = 0
errors = []
@ -64,8 +61,7 @@ def test_version(args):
count += 1
if real != expected:
if keep_going:
errors.append(line.rstrip() + " (got: '" + real +
"')")
errors.append(line.rstrip() + " (got: '" + real + "')")
else:
assert real == expected

View File

@ -3,11 +3,9 @@
# Disable slow testcases
# aport_in_sync_with_git: clones Alpine's aports repo
# aportgen: clones Alpine's aports repo
# version: clones Alpine's apk repo
disabled="
aport_in_sync_with_git
aportgen
version
"
# Make sure we have a valid device (#1128)

2
test/testdata/version/README vendored Normal file
View File

@ -0,0 +1,2 @@
Source:
https://git.alpinelinux.org/cgit/apk-tools/tree/test/version.data

728
test/testdata/version/version.data vendored Normal file
View File

@ -0,0 +1,728 @@
2.34 > 0.1.0_alpha
23_foo > 4_beta
1.0 < 1.0bc # invalid. do string sort
0.1.0_alpha = 0.1.0_alpha
0.1.0_alpha < 0.1.3_alpha
0.1.3_alpha > 0.1.0_alpha
0.1.0_alpha2 > 0.1.0_alpha
0.1.0_alpha < 2.2.39-r1
2.2.39-r1 > 1.0.4-r3
1.0.4-r3 < 1.0.4-r4
1.0.4-r4 < 1.6
1.6 > 1.0.2
1.0.2 > 0.7-r1
0.7-r1 < 1.0.0
1.0.0 < 1.0.1
1.0.1 < 1.1
1.1 > 1.1_alpha1
1.1_alpha1 < 1.2.1
1.2.1 > 1.2
1.2 < 1.3_alpha
1.3_alpha < 1.3_alpha2
1.3_alpha2 < 1.3_alpha3
1.3_alpha8 > 0.6.0
0.6.0 < 0.6.1
0.6.1 < 0.7.0
0.7.0 < 0.8_beta1
0.8_beta1 < 0.8_beta2
0.8_beta4 < 4.8-r1
4.8-r1 > 3.10.18-r1
3.10.18-r1 > 2.3.0b-r1
2.3.0b-r1 < 2.3.0b-r2
2.3.0b-r2 < 2.3.0b-r3
2.3.0b-r3 < 2.3.0b-r4
2.3.0b-r4 > 0.12.1
0.12.1 < 0.12.2
0.12.2 < 0.12.3
0.12.3 > 0.12
0.12 < 0.13_beta1
0.13_beta1 < 0.13_beta2
0.13_beta2 < 0.13_beta3
0.13_beta3 < 0.13_beta4
0.13_beta4 < 0.13_beta5
0.13_beta5 > 0.9.12
0.9.12 < 0.9.13
0.9.13 > 0.9.12
0.9.12 < 0.9.13
0.9.13 > 0.0.16
0.0.16 < 0.6
0.6 < 2.1.13-r3
2.1.13-r3 < 2.1.15-r2
2.1.15-r2 < 2.1.15-r3
2.1.15-r3 > 1.2.11
1.2.11 < 1.2.12.1
1.2.12.1 < 1.2.13
1.2.13 < 1.2.14-r1
1.2.14-r1 > 0.7.1
0.7.1 > 0.5.4
0.5.4 < 0.7.0
0.7.0 < 1.2.13
1.2.13 > 1.0.8
1.0.8 < 1.2.1
1.2.1 > 0.7-r1
0.7-r1 < 2.4.32
2.4.32 < 2.8-r4
2.8-r4 > 0.9.6
0.9.6 > 0.2.0-r1
0.2.0-r1 = 0.2.0-r1
0.2.0-r1 < 3.1_p16
3.1_p16 < 3.1_p17
3.1_p17 > 1.06-r6
1.06-r6 < 006
006 > 1.0.0
1.0.0 < 1.2.2-r1
1.2.2-r1 > 1.2.2
1.2.2 > 0.3-r1
0.3-r1 < 9.3.2-r4
9.3.2-r4 < 9.3.4-r2
9.3.4-r2 > 9.3.4
9.3.4 > 9.3.2
9.3.2 < 9.3.4
9.3.4 > 1.1.3
1.1.3 < 2.16.1-r3
2.16.1-r3 = 2.16.1-r3
2.16.1-r3 > 2.1.0-r2
2.1.0-r2 < 2.9.3-r1
2.9.3-r1 > 0.9-r1
0.9-r1 > 0.8-r1
0.8-r1 < 1.0.6-r3
1.0.6-r3 > 0.11
0.11 < 0.12
0.12 < 1.2.1-r1
1.2.1-r1 < 1.2.2.1
1.2.2.1 < 1.4.1-r1
1.4.1-r1 < 1.4.1-r2
1.4.1-r2 > 1.2.2
1.2.2 < 1.3
1.3 > 1.0.3-r6
1.0.3-r6 < 1.0.4
1.0.4 < 2.59
2.59 < 20050718-r1
20050718-r1 < 20050718-r2
20050718-r2 > 3.9.8-r5
3.9.8-r5 > 2.01.01_alpha10
2.01.01_alpha10 > 0.94
0.94 < 1.0
1.0 > 0.99.3.20040818
0.99.3.20040818 > 0.7
0.7 < 1.21-r1
1.21-r1 > 0.13
0.13 < 0.90.1-r1
0.90.1-r1 > 0.10.2
0.10.2 < 0.10.3
0.10.3 < 1.6
1.6 < 1.39
1.39 > 1.00_beta2
1.00_beta2 > 0.9.2
0.9.2 < 5.94-r1
5.94-r1 < 6.4
6.4 > 2.6-r5
2.6-r5 > 1.4
1.4 < 2.8.9-r1
2.8.9-r1 > 2.8.9
2.8.9 > 1.1
1.1 > 1.0.3-r2
1.0.3-r2 < 1.3.4-r3
1.3.4-r3 < 2.2
2.2 > 1.2.6
1.2.6 < 7.15.1-r1
7.15.1-r1 > 1.02
1.02 < 1.03-r1
1.03-r1 < 1.12.12-r2
1.12.12-r2 < 2.8.0.6-r1
2.8.0.6-r1 > 0.5.2.7
0.5.2.7 < 4.2.52_p2-r1
4.2.52_p2-r1 < 4.2.52_p4-r2
4.2.52_p4-r2 > 1.02.07
1.02.07 < 1.02.10-r1
1.02.10-r1 < 3.0.3-r9
3.0.3-r9 > 2.0.5-r1
2.0.5-r1 < 4.5
4.5 > 2.8.7-r1
2.8.7-r1 > 1.0.5
1.0.5 < 8
8 < 9
9 > 2.18.3-r10
2.18.3-r10 > 1.05-r18
1.05-r18 < 1.05-r19
1.05-r19 < 2.2.5
2.2.5 < 2.8
2.8 < 2.20.1
2.20.1 < 2.20.3
2.20.3 < 2.31
2.31 < 2.34
2.34 < 2.38
2.38 < 20050405
20050405 > 1.8
1.8 < 2.11-r1
2.11-r1 > 2.11
2.11 > 0.1.6-r3
0.1.6-r3 < 0.47-r1
0.47-r1 < 0.49
0.49 < 3.6.8-r2
3.6.8-r2 > 1.39
1.39 < 2.43
2.43 > 2.0.6-r1
2.0.6-r1 > 0.2-r6
0.2-r6 < 0.4
0.4 < 1.0.0
1.0.0 < 10-r1
10-r1 > 4
4 > 0.7.3-r2
0.7.3-r2 > 0.7.3
0.7.3 < 1.95.8
1.95.8 > 1.1.19
1.1.19 > 1.1.5
1.1.5 < 6.3.2-r1
6.3.2-r1 < 6.3.3
6.3.3 > 4.17-r1
4.17-r1 < 4.18
4.18 < 4.19
4.19 > 4.3.0
4.3.0 < 4.3.2-r1
4.3.2-r1 > 4.3.2
4.3.2 > 0.68-r3
0.68-r3 < 1.0.0
1.0.0 < 1.0.1
1.0.1 > 1.0.0
1.0.0 = 1.0.0
1.0.0 < 1.0.1
1.0.1 < 2.3.2-r1
2.3.2-r1 < 2.4.2
2.4.2 < 20060720
20060720 > 3.0.20060720
3.0.20060720 < 20060720
20060720 > 1.1
1.1 = 1.1
1.1 < 1.1.1-r1
1.1.1-r1 < 1.1.3-r1
1.1.3-r1 < 1.1.3-r2
1.1.3-r2 < 2.1.10-r2
2.1.10-r2 > 0.7.18-r2
0.7.18-r2 < 0.17-r6
0.17-r6 < 2.6.1
2.6.1 < 2.6.3
2.6.3 < 3.1.5-r2
3.1.5-r2 < 3.4.6-r1
3.4.6-r1 < 3.4.6-r2
3.4.6-r2 = 3.4.6-r2
3.4.6-r2 > 2.0.33
2.0.33 < 2.0.34
2.0.34 > 1.8.3-r2
1.8.3-r2 < 1.8.3-r3
1.8.3-r3 < 4.1
4.1 < 8.54
8.54 > 4.1.4
4.1.4 > 1.2.10-r5
1.2.10-r5 < 4.1.4-r3
4.1.4-r3 = 4.1.4-r3
4.1.4-r3 < 4.2.1
4.2.1 > 4.1.0
4.1.0 < 8.11
8.11 > 1.4.4-r1
1.4.4-r1 < 2.1.9.200602141850
2.1.9.200602141850 > 1.6
1.6 < 2.5.1-r8
2.5.1-r8 < 2.5.1a-r1
2.5.1a-r1 > 1.19.2-r1
1.19.2-r1 > 0.97-r2
0.97-r2 < 0.97-r3
0.97-r3 < 1.3.5-r10
1.3.5-r10 > 1.3.5-r8
1.3.5-r8 < 1.3.5-r9
1.3.5-r9 > 1.0
1.0 < 1.1
1.1 > 0.9.11
0.9.11 < 0.9.12
0.9.12 < 0.9.13
0.9.13 < 0.9.14
0.9.14 < 0.9.15
0.9.15 < 0.9.16
0.9.16 > 0.3-r2
0.3-r2 < 6.3
6.3 < 6.6
6.6 < 6.9
6.9 > 0.7.2-r3
0.7.2-r3 < 1.2.10
1.2.10 < 20040923-r2
20040923-r2 > 20040401
20040401 > 2.0.0_rc3-r1
2.0.0_rc3-r1 > 1.5
1.5 < 4.4
4.4 > 1.0.1
1.0.1 < 2.2.0
2.2.0 > 1.1.0-r2
1.1.0-r2 > 0.3
0.3 < 20020207-r2
20020207-r2 > 1.31-r2
1.31-r2 < 3.7
3.7 > 2.0.1
2.0.1 < 2.0.2
2.0.2 > 0.99.163
0.99.163 < 2.6.15.20060110
2.6.15.20060110 < 2.6.16.20060323
2.6.16.20060323 < 2.6.19.20061214
2.6.19.20061214 > 0.6.2-r1
0.6.2-r1 < 0.6.3
0.6.3 < 0.6.5
0.6.5 < 1.3.5-r1
1.3.5-r1 < 1.3.5-r4
1.3.5-r4 < 3.0.0-r2
3.0.0-r2 < 021109-r3
021109-r3 < 20060512
20060512 > 1.24
1.24 > 0.9.16-r1
0.9.16-r1 < 3.9_pre20060124
3.9_pre20060124 > 0.01
0.01 < 0.06
0.06 < 1.1.7
1.1.7 < 6b-r7
6b-r7 > 1.12-r7
1.12-r7 < 1.12-r8
1.12-r8 > 1.1.12
1.1.12 < 1.1.13
1.1.13 > 0.3
0.3 < 0.5
0.5 < 3.96.1
3.96.1 < 3.97
3.97 > 0.10.0-r1
0.10.0-r1 > 0.10.0
0.10.0 < 0.10.1_rc1
0.10.1_rc1 > 0.9.11
0.9.11 < 394
394 > 2.31
2.31 > 1.0.1
1.0.1 = 1.0.1
1.0.1 < 1.0.3
1.0.3 > 1.0.2
1.0.2 = 1.0.2
1.0.2 > 1.0.1
1.0.1 = 1.0.1
1.0.1 < 1.2.2
1.2.2 < 2.1.10
2.1.10 > 1.0.1
1.0.1 < 1.0.2
1.0.2 < 3.5.5
3.5.5 > 1.1.1
1.1.1 > 0.9.1
0.9.1 < 1.0.2
1.0.2 > 1.0.1
1.0.1 < 1.0.2
1.0.2 > 1.0.1
1.0.1 = 1.0.1
1.0.1 < 1.0.5
1.0.5 > 0.8.5
0.8.5 < 0.8.6-r3
0.8.6-r3 < 2.3.17
2.3.17 > 1.10-r5
1.10-r5 < 1.10-r9
1.10-r9 < 2.0.2
2.0.2 > 1.1a
1.1a < 1.3a
1.3a > 1.0.2
1.0.2 < 1.2.2-r1
1.2.2-r1 > 1.0-r1
1.0-r1 > 0.15.1b
0.15.1b < 1.0.1
1.0.1 < 1.06-r1
1.06-r1 < 1.06-r2
1.06-r2 > 0.15.1b-r2
0.15.1b-r2 > 0.15.1b
0.15.1b < 2.5.7
2.5.7 > 1.1.2.1-r1
1.1.2.1-r1 > 0.0.31
0.0.31 < 0.0.50
0.0.50 > 0.0.16
0.0.16 < 0.0.25
0.0.25 < 0.17
0.17 > 0.5.0
0.5.0 < 1.1.2
1.1.2 < 1.1.3
1.1.3 < 1.1.20
1.1.20 > 0.9.4
0.9.4 < 0.9.5
0.9.5 < 6.3
6.3 < 6.6
6.6 > 6.3
6.3 < 6.6
6.6 > 1.2.12-r1
1.2.12-r1 < 1.2.13
1.2.13 < 1.2.14
1.2.14 < 1.2.15
1.2.15 < 8.0.12
8.0.12 > 8.0.9
8.0.9 > 1.2.3-r1
1.2.3-r1 < 1.2.4-r1
1.2.4-r1 > 0.1
0.1 < 0.3.5
0.3.5 < 1.5.22
1.5.22 > 0.1.11
0.1.11 < 0.1.12
0.1.12 < 1.1.4.1
1.1.4.1 > 1.1.0
1.1.0 < 1.1.2
1.1.2 > 1.0.3
1.0.3 > 1.0.2
1.0.2 < 2.6.26
2.6.26 < 2.6.27
2.6.27 > 1.1.17
1.1.17 < 1.4.11
1.4.11 < 22.7-r1
22.7-r1 < 22.7.3-r1
22.7.3-r1 > 22.7
22.7 > 2.1_pre20
2.1_pre20 < 2.1_pre26
2.1_pre26 > 0.2.3-r2
0.2.3-r2 > 0.2.2
0.2.2 < 2.10.0
2.10.0 < 2.10.1
2.10.1 > 02.08.01b
02.08.01b < 4.77
4.77 > 0.17
0.17 < 5.1.1-r1
5.1.1-r1 < 5.1.1-r2
5.1.1-r2 > 5.1.1
5.1.1 > 1.2
1.2 < 5.1
5.1 > 2.02.06
2.02.06 < 2.02.10
2.02.10 < 2.8.5-r3
2.8.5-r3 < 2.8.6-r1
2.8.6-r1 < 2.8.6-r2
2.8.6-r2 > 2.02-r1
2.02-r1 > 1.5.0-r1
1.5.0-r1 > 1.5.0
1.5.0 > 0.9.2
0.9.2 < 8.1.2.20040524-r1
8.1.2.20040524-r1 < 8.1.2.20050715-r1
8.1.2.20050715-r1 < 20030215
20030215 > 3.80-r4
3.80-r4 < 3.81
3.81 > 1.6d
1.6d > 1.2.07.8
1.2.07.8 < 1.2.12.04
1.2.12.04 < 1.2.12.05
1.2.12.05 < 1.3.3
1.3.3 < 2.6.4
2.6.4 > 2.5.2
2.5.2 < 2.6.1
2.6.1 > 2.6
2.6 < 6.5.1-r1
6.5.1-r1 > 1.1.35-r1
1.1.35-r1 < 1.1.35-r2
1.1.35-r2 > 0.9.2
0.9.2 < 1.07-r1
1.07-r1 < 1.07.5
1.07.5 > 1.07
1.07 < 1.19
1.19 < 2.1-r2
2.1-r2 < 2.2
2.2 > 1.0.4
1.0.4 < 20060811
20060811 < 20061003
20061003 > 0.1_pre20060810
0.1_pre20060810 < 0.1_pre20060817
0.1_pre20060817 < 1.0.3
1.0.3 > 1.0.2
1.0.2 > 1.0.1
1.0.1 < 3.2.2-r1
3.2.2-r1 < 3.2.2-r2
3.2.2-r2 < 3.3.17
3.3.17 > 0.59s-r11
0.59s-r11 < 0.65
0.65 > 0.2.10-r2
0.2.10-r2 < 2.01
2.01 < 3.9.10
3.9.10 > 1.2.18
1.2.18 < 1.5.11-r2
1.5.11-r2 < 1.5.13-r1
1.5.13-r1 > 1.3.12-r1
1.3.12-r1 < 2.0.1
2.0.1 < 2.0.2
2.0.2 < 2.0.3
2.0.3 > 0.2.0
0.2.0 < 5.5-r2
5.5-r2 < 5.5-r3
5.5-r3 > 0.25.3
0.25.3 < 0.26.1-r1
0.26.1-r1 < 5.2.1.2-r1
5.2.1.2-r1 < 5.4
5.4 > 1.60-r11
1.60-r11 < 1.60-r12
1.60-r12 < 110-r8
110-r8 > 0.17-r2
0.17-r2 < 1.05-r4
1.05-r4 < 5.28.0
5.28.0 > 0.51.6-r1
0.51.6-r1 < 1.0.6-r6
1.0.6-r6 > 0.8.3
0.8.3 < 1.42
1.42 < 20030719
20030719 > 4.01
4.01 < 4.20
4.20 > 0.20070118
0.20070118 < 0.20070207_rc1
0.20070207_rc1 < 1.0
1.0 < 1.13.0
1.13.0 < 1.13.1
1.13.1 > 0.21
0.21 > 0.3.7-r3
0.3.7-r3 < 0.4.10
0.4.10 < 0.5.0
0.5.0 < 0.5.5
0.5.5 < 0.5.7
0.5.7 < 0.6.11-r1
0.6.11-r1 < 2.3.30-r2
2.3.30-r2 < 3.7_p1
3.7_p1 > 1.3
1.3 > 0.10.1
0.10.1 < 4.3_p2-r1
4.3_p2-r1 < 4.3_p2-r5
4.3_p2-r5 < 4.4_p1-r6
4.4_p1-r6 < 4.5_p1-r1
4.5_p1-r1 > 4.5_p1
4.5_p1 < 4.5_p1-r1
4.5_p1-r1 > 4.5_p1
4.5_p1 > 0.9.8c-r1
0.9.8c-r1 < 0.9.8d
0.9.8d < 2.4.4
2.4.4 < 2.4.7
2.4.7 > 2.0.6
2.0.6 = 2.0.6
2.0.6 > 0.78-r3
0.78-r3 > 0.3.2
0.3.2 < 1.7.1-r1
1.7.1-r1 < 2.5.9
2.5.9 > 0.1.13
0.1.13 < 0.1.15
0.1.15 < 0.4
0.4 < 0.9.6
0.9.6 < 2.2.0-r1
2.2.0-r1 < 2.2.3-r2
2.2.3-r2 < 013
013 < 014-r1
014-r1 > 1.3.1-r1
1.3.1-r1 < 5.8.8-r2
5.8.8-r2 > 5.1.6-r4
5.1.6-r4 < 5.1.6-r6
5.1.6-r6 < 5.2.1-r3
5.2.1-r3 > 0.11.3
0.11.3 = 0.11.3
0.11.3 < 1.10.7
1.10.7 > 1.7-r1
1.7-r1 > 0.1.20
0.1.20 < 0.1.23
0.1.23 < 5b-r9
5b-r9 > 2.2.10
2.2.10 < 2.3.6
2.3.6 < 8.0.12
8.0.12 > 2.4.3-r16
2.4.3-r16 < 2.4.4-r4
2.4.4-r4 < 3.0.3-r5
3.0.3-r5 < 3.0.6
3.0.6 < 3.2.6
3.2.6 < 3.2.7
3.2.7 > 0.3.1_rc8
0.3.1_rc8 < 22.2
22.2 < 22.3
22.3 > 1.2.2
1.2.2 < 2.04
2.04 < 2.4.3-r1
2.4.3-r1 < 2.4.3-r4
2.4.3-r4 > 0.98.6-r1
0.98.6-r1 < 5.7-r2
5.7-r2 < 5.7-r3
5.7-r3 > 5.1_p4
5.1_p4 > 1.0.5
1.0.5 < 3.6.19-r1
3.6.19-r1 > 3.6.19
3.6.19 > 1.0.1
1.0.1 < 3.8
3.8 > 0.2.3
0.2.3 < 1.2.15-r3
1.2.15-r3 > 1.2.6-r1
1.2.6-r1 < 2.6.8-r2
2.6.8-r2 < 2.6.9-r1
2.6.9-r1 > 1.7
1.7 < 1.7b
1.7b < 1.8.4-r3
1.8.4-r3 < 1.8.5
1.8.5 < 1.8.5_p2
1.8.5_p2 > 1.1.3
1.1.3 < 3.0.22-r3
3.0.22-r3 < 3.0.24
3.0.24 = 3.0.24
3.0.24 = 3.0.24
3.0.24 < 4.0.2-r5
4.0.2-r5 < 4.0.3
4.0.3 > 0.98
0.98 < 1.00
1.00 < 4.1.4-r1
4.1.4-r1 < 4.1.5
4.1.5 > 2.3
2.3 < 2.17-r3
2.17-r3 > 0.1.7
0.1.7 < 1.11
1.11 < 4.2.1-r11
4.2.1-r11 > 3.2.3
3.2.3 < 3.2.4
3.2.4 < 3.2.8
3.2.8 < 3.2.9
3.2.9 > 3.2.3
3.2.3 < 3.2.4
3.2.4 < 3.2.8
3.2.8 < 3.2.9
3.2.9 > 1.4.9-r2
1.4.9-r2 < 2.9.11_pre20051101-r2
2.9.11_pre20051101-r2 < 2.9.11_pre20051101-r3
2.9.11_pre20051101-r3 > 2.9.11_pre20051101
2.9.11_pre20051101 < 2.9.11_pre20061021-r1
2.9.11_pre20061021-r1 < 2.9.11_pre20061021-r2
2.9.11_pre20061021-r2 < 5.36-r1
5.36-r1 > 1.0.1
1.0.1 < 7.0-r2
7.0-r2 > 2.4.5
2.4.5 < 2.6.1.2
2.6.1.2 < 2.6.1.3-r1
2.6.1.3-r1 > 2.6.1.3
2.6.1.3 < 2.6.1.3-r1
2.6.1.3-r1 < 12.17.9
12.17.9 > 1.1.12
1.1.12 > 1.1.7
1.1.7 < 2.5.14
2.5.14 < 2.6.6-r1
2.6.6-r1 < 2.6.7
2.6.7 < 2.6.9-r1
2.6.9-r1 > 2.6.9
2.6.9 > 1.39
1.39 > 0.9
0.9 < 2.61-r2
2.61-r2 < 4.5.14
4.5.14 > 4.09-r1
4.09-r1 > 1.3.1
1.3.1 < 1.3.2-r3
1.3.2-r3 < 1.6.8_p12-r1
1.6.8_p12-r1 > 1.6.8_p9-r2
1.6.8_p9-r2 > 1.3.0-r1
1.3.0-r1 < 3.11
3.11 < 3.20
3.20 > 1.6.11-r1
1.6.11-r1 > 1.6.9
1.6.9 < 5.0.5-r2
5.0.5-r2 > 2.86-r5
2.86-r5 < 2.86-r6
2.86-r6 > 1.15.1-r1
1.15.1-r1 < 8.4.9
8.4.9 > 7.6-r8
7.6-r8 > 3.9.4-r2
3.9.4-r2 < 3.9.4-r3
3.9.4-r3 < 3.9.5-r2
3.9.5-r2 > 1.1.9
1.1.9 > 1.0.6
1.0.6 < 5.9
5.9 < 6.5
6.5 > 0.40-r1
0.40-r1 < 2.25b-r5
2.25b-r5 < 2.25b-r6
2.25b-r6 > 1.0.4
1.0.4 < 1.0.5
1.0.5 < 1.4_p12-r2
1.4_p12-r2 < 1.4_p12-r5
1.4_p12-r5 > 1.1
1.1 > 0.2.0-r1
0.2.0-r1 < 0.2.1
0.2.1 < 0.9.28-r1
0.9.28-r1 < 0.9.28-r2
0.9.28-r2 < 0.9.28.1
0.9.28.1 > 0.9.28
0.9.28 < 0.9.28.1
0.9.28.1 < 087-r1
087-r1 < 103
103 < 104-r11
104-r11 > 104-r9
104-r9 > 1.23-r1
1.23-r1 > 1.23
1.23 < 1.23-r1
1.23-r1 > 1.0.2
1.0.2 < 5.52-r1
5.52-r1 > 1.2.5_rc2
1.2.5_rc2 > 0.1
0.1 < 0.71-r1
0.71-r1 < 20040406-r1
20040406-r1 > 2.12r-r4
2.12r-r4 < 2.12r-r5
2.12r-r5 > 0.0.7
0.0.7 < 1.0.3
1.0.3 < 1.8
1.8 < 7.0.17
7.0.17 < 7.0.174
7.0.174 > 7.0.17
7.0.17 < 7.0.174
7.0.174 > 1.0.1
1.0.1 < 1.1.1-r3
1.1.1-r3 > 0.3.4_pre20061029
0.3.4_pre20061029 < 0.4.0
0.4.0 > 0.1.2
0.1.2 < 1.10.2
1.10.2 < 2.16
2.16 < 28
28 > 0.99.4
0.99.4 < 1.13
1.13 > 1.0.1
1.0.1 < 1.1.2-r2
1.1.2-r2 > 1.1.0
1.1.0 < 1.1.1
1.1.1 = 1.1.1
1.1.1 > 0.6.0
0.6.0 < 6.6.3
6.6.3 > 1.1.1
1.1.1 > 1.1.0
1.1.0 = 1.1.0
1.1.0 > 0.2.0
0.2.0 < 0.3.0
0.3.0 < 1.1.1
1.1.1 < 1.2.0
1.2.0 > 1.1.0
1.1.0 < 1.6.5
1.6.5 > 1.1.0
1.1.0 < 1.4.2
1.4.2 > 1.1.1
1.1.1 < 2.8.1
2.8.1 > 1.2.0
1.2.0 < 4.1.0
4.1.0 > 0.4.1
0.4.1 < 1.9.1
1.9.1 < 2.1.1
2.1.1 > 1.4.1
1.4.1 > 0.9.1-r1
0.9.1-r1 > 0.8.1
0.8.1 < 1.2.1-r1
1.2.1-r1 > 1.1.0
1.1.0 < 1.2.1
1.2.1 > 1.1.0
1.1.0 > 0.1.1
0.1.1 < 1.2.1
1.2.1 < 4.1.0
4.1.0 > 0.2.1-r1
0.2.1-r1 < 1.1.0
1.1.0 < 2.7.11
2.7.11 > 1.0.2-r6
1.0.2-r6 > 1.0.2
1.0.2 > 0.8
0.8 < 1.1.1-r4
1.1.1-r4 < 222
222 > 1.0.1
1.0.1 < 1.2.12-r1
1.2.12-r1 > 1.2.8
1.2.8 < 1.2.9.1-r1
1.2.9.1-r1 > 1.2.9.1
1.2.9.1 < 2.31-r1
2.31-r1 > 2.31
2.31 > 1.2.3-r1
1.2.3-r1 > 1.2.3
1.2.3 < 4.2.5
4.2.5 < 4.3.2-r2
1.3-r0 < 1.3.1-r0
1.3_pre1-r1 < 1.3.2
1.0_p10-r0 > 1.0_p9-r0
0.1.0_alpha_pre2 < 0.1.0_alpha