[game-data-packager] 03/06: Turn inter-package relationships into an object
Simon McVittie
smcv at debian.org
Mon Jan 25 01:14:28 UTC 2016
This is an automated email from the git hooks/post-receive script.
smcv pushed a commit to branch master
in repository game-data-packager.
commit dd581327e0d14e8ebd9d76cfc50cdd93b9da1471
Author: Simon McVittie <smcv at debian.org>
Date: Mon Jan 25 00:16:29 2016 +0000
Turn inter-package relationships into an object
---
Makefile | 2 +
data/lgeneral.yaml | 2 +-
data/morrowind.yaml | 10 +-
data/quake2.yaml | 8 +-
game_data_packager/__init__.py | 166 ++++++++++++++++++++-----------
game_data_packager/build.py | 103 +++++++++++--------
game_data_packager/data.py | 64 ++++++++++++
game_data_packager/packaging/__init__.py | 62 +++++++++++-
game_data_packager/packaging/arch.py | 19 ++++
game_data_packager/packaging/deb.py | 38 +++++++
game_data_packager/packaging/rpm.py | 19 ++++
tests/deb.py | 68 +++++++++++++
tests/rpm.py | 54 ++++++++++
tools/check_syntax.py | 6 ++
14 files changed, 507 insertions(+), 114 deletions(-)
diff --git a/Makefile b/Makefile
index 9effe78..55d1001 100644
--- a/Makefile
+++ b/Makefile
@@ -121,7 +121,9 @@ clean:
check:
LC_ALL=C $(PYFLAKES3) game_data_packager/*.py game_data_packager/*/*.py runtime/*.py tools/*.py || :
+ LC_ALL=C GDP_UNINSTALLED=1 PYTHONPATH=. $(PYTHON) tests/deb.py
LC_ALL=C GDP_UNINSTALLED=1 PYTHONPATH=. $(PYTHON) tests/hashed_file.py
+ LC_ALL=C GDP_UNINSTALLED=1 PYTHONPATH=. $(PYTHON) tests/rpm.py
LC_ALL=C GDP_UNINSTALLED=1 PYTHONPATH=. $(PYTHON) tests/umod.py
LC_ALL=C GDP_UNINSTALLED=1 PYTHONPATH=. $(PYTHON) tools/check_syntax.py
LC_ALL=C GDP_UNINSTALLED=1 PYTHONPATH=. $(PYTHON) tools/check_equivalence.py
diff --git a/data/lgeneral.yaml b/data/lgeneral.yaml
index 244e8af..97d26d9 100644
--- a/data/lgeneral.yaml
+++ b/data/lgeneral.yaml
@@ -13,7 +13,7 @@ packages:
longname: Panzer General data for LGeneral
install_to: usr/share/games/lgeneral
debian:
- build-depends: lgc-pg
+ build_depends: lgc-pg
# pg-data.tar.gz is not actually needed, but it's small, and putting it
# in the .deb means we can easily repack it if lgc-pg changes
#
diff --git a/data/morrowind.yaml b/data/morrowind.yaml
index c306a3d..2940047 100644
--- a/data/morrowind.yaml
+++ b/data/morrowind.yaml
@@ -42,7 +42,7 @@ packages:
- morrowind-complete-en-data
provides: morrowind-data
lang: en
- build-depends: [openmw-launcher]
+ build_depends: [openmw-launcher]
install:
- Morrowind assets from unshield
- Morrowind English GOTY assets from unshield
@@ -58,7 +58,7 @@ packages:
- morrowind-complete-en-data
provides: morrowind-data
lang: en
- build-depends: [openmw-launcher]
+ build_depends: [openmw-launcher]
install:
- Morrowind assets from unshield
- Morrowind English GOTY assets from unshield
@@ -77,7 +77,7 @@ packages:
- morrowind-complete-en-data
provides: morrowind-data
lang: en
- build-depends: [openmw-launcher]
+ build_depends: [openmw-launcher]
install:
- Morrowind assets from unshield
- Morrowind English GOTY assets from unshield
@@ -93,7 +93,7 @@ packages:
morrowind-complete-en-data:
provides: morrowind-data
lang: en
- build-depends: [openmw-launcher]
+ build_depends: [openmw-launcher]
install:
- Morrowind assets from unshield
- Morrowind English GOTY assets from unshield
@@ -117,7 +117,7 @@ packages:
morrowind-fr-data:
provides: morrowind-data
lang: fr
- build-depends: [openmw-launcher]
+ build_depends: [openmw-launcher]
install:
- Morrowind assets from unshield
- Morrowind French assets from unshield
diff --git a/data/quake2.yaml b/data/quake2.yaml
index 67eb5b3..2b85d7e 100644
--- a/data/quake2.yaml
+++ b/data/quake2.yaml
@@ -111,11 +111,11 @@ packages:
id: 2330
path: "common/Quake 2/xatrix"
debian:
- build-depends: [gcc, make, libc6-dev]
+ build_depends: [gcc, make, libc6-dev]
provides: quake2-xatrix
replaces: quake2-xatrix
fedora:
- build-depends: [gcc, make, glibc-devel]
+ build_depends: [gcc, make, glibc-devel]
expansion_for: quake2-full-data
# this is what Makefile says has been tested
architecture: amd64 i386 ia64 sparc64
@@ -167,11 +167,11 @@ packages:
gog:
url: quake_ii_quad_damage
debian:
- build-depends: [gcc, make, libc6-dev]
+ build_depends: [gcc, make, libc6-dev]
provides: quake2-rogue
replaces: quake2-rogue
fedora:
- build-depends: [gcc, make, glibc-devel]
+ build_depends: [gcc, make, glibc-devel]
expansion_for: quake2-full-data
# this is what Makefile says has been tested
architecture: amd64 i386 ia64 sparc64
diff --git a/game_data_packager/__init__.py b/game_data_packager/__init__.py
index 321218e..f34618c 100644
--- a/game_data_packager/__init__.py
+++ b/game_data_packager/__init__.py
@@ -31,7 +31,7 @@ import zipfile
import yaml
from .build import (PackagingTask)
-from .data import (WantedFile)
+from .data import (PackageRelation, WantedFile)
from .paths import (DATADIR, USE_VFS)
from .util import ascii_safe
from .version import (DISTRO, FORMAT, GAME_PACKAGE_VERSION)
@@ -53,18 +53,22 @@ class GameDataPackage(object):
self.demo_for = set()
self._better_versions = set()
self.expansion_for = None
- # use this to group together dubs
- self.provides = None
# use this for games with demo_for/better_version/provides
self.mutually_exclusive = False
# expansion for a package outside of this yaml file;
# may be another GDP package or a package not made by GDP
self.expansion_for_ext = None
- # distro-agnostic depedencies inside the same .yaml file
- # that can't be handled with expansion_for heuristics
- # *) on Fedora this maps to 'Requires:'
- self._depends = set()
+ self.relations = dict(
+ breaks=[],
+ build_depends=[],
+ conflicts=[],
+ depends=[],
+ provides=[],
+ recommends=[],
+ replaces=[],
+ suggests=[],
+ )
# The optional marketing name of this version
self.longname = None
@@ -188,16 +192,6 @@ class GameDataPackage(object):
return None
@property
- def depends(self):
- return self._depends
- @depends.setter
- def depends(self, value):
- if type(value) is str:
- self._depends = set([value])
- else:
- self._depends = set(value)
-
- @property
def optional(self):
return self._optional
@optional.setter
@@ -249,7 +243,6 @@ class GameDataPackage(object):
'aliases',
'better_versions',
'demo_for',
- 'depends',
'dotemu',
'gog',
'origin',
@@ -265,6 +258,14 @@ class GameDataPackage(object):
else:
ret[k] = v
+ for relation, related in self.relations.items():
+ # The .to_data() of a PackageRelation doesn't have a defined
+ # sorting order, so do a Schwartzian transform to get a
+ # stable order
+ tmp = sorted([(str(x), x.to_data()) for x in related])
+ tmp = [x[1] for x in tmp]
+ ret[relation] = tmp
+
if expand and self.install_files is not None:
if self.install_files:
ret['install'] = sorted(f.name for f in self.install_files)
@@ -660,7 +661,7 @@ class GameData(object):
def _populate_package(self, package, d):
for k in ('expansion_for', 'expansion_for_ext', 'longname', 'symlinks', 'install_to',
- 'description', 'depends',
+ 'description',
'rip_cd', 'architecture', 'aliases', 'better_versions', 'langs', 'mutually_exclusive',
'copyright', 'engine', 'lang', 'component', 'section', 'disks', 'provides',
'steam', 'gog', 'dotemu', 'origin', 'url_misc', 'wiki', 'copyright_notice',
@@ -672,6 +673,34 @@ class GameData(object):
assert 'better_versions' not in d
package.better_versions = set([d['better_version']])
+ for rel in package.relations:
+ if rel in d:
+ related = d[rel]
+
+ if isinstance(related, (str, dict)):
+ related = [related]
+ else:
+ assert isinstance(related, list)
+
+ for x in related:
+ pr = PackageRelation(x)
+ # Fedora doesn't handle alternatives, everything must
+ # be handled with virtual packages. Assume the same is
+ # true for everything except dpkg.
+ assert not pr.alternatives, pr
+
+ if pr.contextual:
+ for context, specific in pr.contextual.items():
+ assert (context == 'deb' or
+ not specific.alternatives), pr
+
+ if pr.package == 'libjpeg.so.62':
+ # we can't really translate versions for libjpeg,
+ # since it could be either libjpeg6b or libjpeg-turbo
+ assert pr.version is None
+
+ package.relations[rel].append(pr)
+
for port in (
# packaging formats (we treat "debian" as "any dpkg-based"
# for historical reasons)
@@ -679,24 +708,37 @@ class GameData(object):
# specific distributions
'arch', 'fedora', 'mageia', 'suse',
):
- if port in d:
- package.specifics[port] = d[port]
-
- # FIXME: this object's contents should be 1:1 mapped from the
- # YAML, and not format- or distribution-specific.
- # Distribution-specific stuff should be done in the PackagingTask
- # or PackagingSystem
- if port in d and (FORMAT == port or DISTRO == port or
- (FORMAT == 'deb' and port == 'debian')):
- for k in ('engine', 'install_to', 'description', 'provides'):
- if k in d[port]:
+
+ for k in d.get(port, {}):
+ if k in ('engine', 'install_to', 'description'):
+ # FIXME: this object's contents should be 1:1 mapped
+ # from the YAML, and not format- or distribution-specific.
+ # Distribution-specific stuff should be done in the
+ # PackagingTask or PackagingSystem
+ if port in d and (FORMAT == port or DISTRO == port or
+ (FORMAT == 'deb' and port == 'debian')):
setattr(package, k, d[port][k])
+ elif k in package.relations:
+ related = d[port][k]
+
+ if isinstance(related, str):
+ related = [related]
+
+ for r in related:
+ if port == 'debian':
+ # we treat "debian:" as meaning "any dpkg-based"
+ pr = PackageRelation({'deb': r})
+ else:
+ pr = PackageRelation({port: r})
+ assert not pr.alternatives, pr
- # Fedora doesn't handle alternatives, everything must be handled with
- # virtual packages
- if FORMAT == 'rpm':
- for dep in package.depends:
- assert '|' not in dep, (package.name, package.depends)
+ if pr.package == 'libjpeg.so.62':
+ assert pr.version is None
+
+ package.relations[k].append(pr)
+ else:
+ raise AssertionError('%s: unknown key %r in port %r' %
+ (package.name, k, port))
assert self.copyright or package.copyright, package.name
assert package.component in ('main', 'contrib', 'non-free', 'local')
@@ -705,24 +747,19 @@ class GameData(object):
assert type(package.langs) is list
assert type(package.mutually_exclusive) is bool
- if 'debian' in d:
- debian = d['debian']
- assert type(debian) is dict
- for k, v in debian.items():
- assert k in ('breaks', 'conflicts', 'depends', 'provides',
- 'recommends', 'replaces', 'suggests',
- 'build-depends'), (package.name, debian)
- assert type(v) in (str, list), (package.name, debian)
- if type(v) == str:
- assert ',' not in v, (package.name, debian)
- package.specifics['debian'][k] = [v]
- assert package.name not in v, \
- "A package shouldn't extraneously %s itself" % k
-
- if 'provides' in d:
- assert type(package.provides) is str
- assert package.name != package.provides, \
- "A package shouldn't extraneously provide itself"
+ for rel, related in package.relations.items():
+ for pr in related:
+ packages = set()
+ if pr.contextual:
+ for p in pr.contextual.values():
+ packages.add(p.package)
+ elif pr.alternatives:
+ for p in pr.alternatives:
+ packages.add(p.package)
+ else:
+ packages.add(pr.package)
+ assert package.name not in packages, \
+ "%s should not be in its own %s set" % (package.name, rel)
if 'install_to' in d:
assert '$assets/' + package.name != d['install_to'] + '-data', \
@@ -1051,13 +1088,30 @@ class GameData(object):
# check internal depedencies
for demo_for_item in package.demo_for:
assert demo_for_item in self.packages, demo_for_item
+
if package.expansion_for:
if package.expansion_for not in self.packages:
- for p in self.packages.values():
- if package.expansion_for == p.provides:
+ # It needs to be provided on all distributions,
+ # so we can ignore contextual package relations,
+ # which have package = None.
+ #
+ # We also already asserted that distro-independent
+ # relations don't have alternatives (not that they
+ # would be meaningful for provides).
+ provider = None
+
+ for other in self.packages.values():
+ for provided in other.relations['provides']:
+ if package.expansion_for == provided.package:
+ provider = other
+ break
+
+ if provider is not None:
break
else:
- raise Exception('virtual pkg %s not found' % package.expansion_for)
+ raise Exception('%s: %s: virtual package %s not found' %
+ (self.shortname, package.name,
+ package.expansion_for))
if package.better_versions:
for v in package.better_versions:
diff --git a/game_data_packager/build.py b/game_data_packager/build.py
index 4c1d547..63ec622 100644
--- a/game_data_packager/build.py
+++ b/game_data_packager/build.py
@@ -1320,21 +1320,8 @@ class PackagingTask(object):
'--options=!all,use-set,type,uid,gid,mode,time,size,md5,sha256,link']
+ sorted(files), env={'LANG':'C'}, cwd=destdir)
- def __merge_relationships(self, package, related, key):
- # copy it so we don't modify it in-place
- related = set(related)
-
- if FORMAT in package.specifics:
- related |= set(package.specifics[FORMAT].get(key, ()))
-
- if FORMAT == 'deb' and 'debian' in package.specifics:
- # we treat "debian" as "any dpkg-based" for historical reasons
- related |= set(package.specifics['debian'].get(key, ()))
-
- if DISTRO in package.specifics:
- related |= set(package.specifics[DISTRO].get(key, ()))
-
- return related
+ def __merge_relations(self, package, rel):
+ return set(self.packaging.format_relations(package.relations[rel]))
def fill_dest_dir_rpm(self, package, destdir, compress, architecture, release):
specfile = os.path.join(self.get_workdir(), '%s.spec' % package.name)
@@ -1400,19 +1387,39 @@ class PackagingTask(object):
else:
spec.write('Group: Amusements/Games\n')
spec.write('BuildArch: %s\n' % architecture)
- if package.provides:
- spec.write('Provides: %s\n' % package.provides)
+
+ for p in self.__merge_relations(package, 'provides'):
+ spec.write('Provides: %s\n' % p)
+
if package.mutually_exclusive:
- spec.write('Conflicts: %s\n' % package.provides)
+ spec.write('Conflicts: %s\n' % p)
+
if package.expansion_for:
spec.write('Requires: %s\n' % package.expansion_for)
else:
engine = package.engine or self.game.engine
+
if engine and len(engine.split()) == 1:
spec.write('Requires: %s\n' % engine)
- for p in self.__merge_relationships(package, package.depends,
- 'depends'):
+
+ for p in self.__merge_relations(package, 'depends'):
spec.write('Requires: %s\n' % p)
+
+ for p in (self.__merge_relations(package, 'conflicts'),
+ self.__merge_relations(package, 'breaks')):
+ spec.write('Conflicts: %s\n' % p)
+
+ for p in self.__merge_relations(package, 'recommends'):
+ # FIXME: some RPM distributions do have recommends;
+ # which ones?
+ pass
+
+ for p in self.__merge_relations(package, 'suggests'):
+ # FIXME: likewise
+ pass
+
+ # FIXME: replaces?
+
if not compress or not self.compress_deb or package.rip_cd:
spec.write('%define _binary_payload w0.gzdio\n')
elif self.compress_deb == ['-Zgzip', '-z1']:
@@ -1643,22 +1650,20 @@ class PackagingTask(object):
control['Architecture'] = self.packaging.get_architecture(package.architecture)
dep = dict()
- debian = package.specifics.get('debian', {})
- for field in ('breaks', 'conflicts', 'provides',
- 'recommends', 'replaces', 'suggests'):
- dep[field] = set(debian.get(field,[]))
- dep['depends'] = self.__merge_relationships(package, package.depends,
- 'depends')
+ for rel in package.relations:
+ if rel == 'build_depends':
+ continue
+
+ dep[rel] = self.__merge_relations(package, rel)
+ logger.debug('%s %s %s', package.name, rel, ', '.join(dep[rel]))
if package.mutually_exclusive:
dep['conflicts'] |= package.demo_for
dep['conflicts'] |= package.better_versions
- if package.provides:
- dep['provides'].add(package.provides)
- if package.mutually_exclusive:
- dep['replaces'].add(package.provides)
+ if package.mutually_exclusive:
+ dep['replaces'] |= dep['provides']
engine = package.engine or self.game.engine
if engine and '>=' in engine:
@@ -1686,9 +1691,13 @@ class PackagingTask(object):
# dependencies derived from *other* package's data
for other_package in self.game.packages.values():
- if (other_package.expansion_for and
- other_package.expansion_for in (package.name, package.provides)):
+ if other_package.expansion_for:
+ if package.name == other_package.expansion_for:
dep['suggests'].add(other_package.name)
+ else:
+ for p in package.relations['provides']:
+ if p.package == other_package.expansion_for:
+ dep['suggests'].add(other_package.name)
if other_package.mutually_exclusive:
if package.name in other_package.better_versions:
@@ -2181,15 +2190,15 @@ class PackagingTask(object):
possible.discard(package)
for package in set(possible):
- debian = package.specifics.get('debian', {})
- if 'build-depends' in debian:
- for tool in debian['build-depends']:
- tool = tool.strip()
- if not which(tool) and not self.packaging.is_installed(tool):
- logger.error('package "%s" is needed to build "%s"' %
- (tool, package.name))
- possible.discard(package)
- self.missing_tools.add(tool)
+ build_depends = self.__merge_relations(package, 'build_depends')
+ for tool in build_depends:
+ tool = tool.strip()
+
+ if not which(tool) and not self.packaging.is_installed(tool):
+ logger.error('package "%s" is needed to build "%s"' %
+ (tool, package.name))
+ possible.discard(package)
+ self.missing_tools.add(tool)
logger.debug('possible packages: %r', set(p.name for p in possible))
if not possible:
@@ -2216,12 +2225,18 @@ class PackagingTask(object):
package.name, package.lang)
possible.discard(package)
continue
+
# keep only preferred language for this virtual package
- if package.provides:
+ provides = self.__merge_relations(package, 'provides')
+
+ if provides:
for other_p in possible:
if other_p.name == package.name:
continue
- if other_p.provides != package.provides:
+ other_provides = self.__merge_relations(other_p,
+ 'provides')
+ if other_provides - provides:
+ # it provides something this one doesn't
continue
if score < lang_score(other_p.lang):
logger.info('will not produce "%s" '
@@ -2668,7 +2683,7 @@ class PackagingTask(object):
packages = set()
for t in self.missing_tools:
- p = self.packaging.PACKAGE_MAP.get(t, t)
+ p = self.packaging.package_for_tool(t)
if p is not None:
packages.add(p)
diff --git a/game_data_packager/data.py b/game_data_packager/data.py
index b1de792..12fba35 100644
--- a/game_data_packager/data.py
+++ b/game_data_packager/data.py
@@ -295,3 +295,67 @@ class WantedFile(HashedFile):
ret[k] = v
return ret
+
+class PackageRelation:
+ def __init__(self, rel):
+ assert isinstance(rel, str) or isinstance(rel, dict)
+ assert ',' not in rel
+
+ self.package = None
+ self.version = None
+ self.version_operator = None
+ self.alternatives = []
+ self.contextual = {}
+
+ if isinstance(rel, dict):
+ for context, specific in rel.items():
+ assert isinstance(context, str), context
+ assert isinstance(specific, str), specific
+ self.contextual[context] = PackageRelation(specific)
+ elif '|' in rel:
+ self.alternatives = [PackageRelation(bit.strip())
+ for bit in rel.split('|')]
+ else:
+ for operator in '>=', '>>', '<=', '<<', '=':
+ if operator in rel:
+ package, version = rel.split(operator)
+ package = package.rstrip('(')
+ self.package = package.strip()
+ version = version.rstrip(')')
+ self.version = version.strip()
+ self.version_operator = operator
+ break
+ else:
+ self.package = rel
+
+ assert self.package.strip() == self.package, repr(self.package)
+
+ def to_data(self):
+ if self.contextual:
+ data = {}
+
+ for context, specific in self.contextual.items():
+ data[context] = specific.to_data()
+
+ return data
+
+ if self.alternatives:
+ return ' | '.join([alt.to_data() for alt in self.alternatives])
+
+ return str(self)
+
+ def __str__(self):
+ if self.contextual:
+ return repr(self)
+
+ if self.alternatives:
+ return ' | '.join([str(s) for s in self.alternatives])
+
+ if self.version is None:
+ return self.package
+
+ return '%s (%s %s)' % (self.package, self.version_operator,
+ self.version)
+
+ def __repr__(self):
+ return 'PackageRelation(' + repr(self.to_data()) + ')'
diff --git a/game_data_packager/packaging/__init__.py b/game_data_packager/packaging/__init__.py
index c474d74..eb16b49 100644
--- a/game_data_packager/packaging/__init__.py
+++ b/game_data_packager/packaging/__init__.py
@@ -27,10 +27,23 @@ class PackagingSystem(metaclass=ABCMeta):
LICENSEDIR = 'usr/share/doc'
CHECK_CMD = None
INSTALL_CMD = None
- # by default pgm 'unzip' is provided by package 'unzip' etc...
- # only exceptions needs to be listed
- # 'None' means that this pgm is not packaged by $distro
- PACKAGE_MAP = dict()
+
+ # Exceptions to our normal heuristic for mapping a tool to a package:
+ # the executable tool 'unzip' is in the unzip package, etc.
+ #
+ # Only exceptions need to be listed.
+ #
+ # 'NotImplemented' means that this dependency is not packaged by
+ # the distro.
+ PACKAGE_MAP = {}
+
+ # Exceptions to our normal heuristic for mapping an abstract package name
+ # to a package:
+ #
+ # - the library 'libfoo.so.0' is in a package that Provides libfoo.so.0
+ # (suitable for RPM)
+ # - anything else is in the obvious package name
+ RENAME_PACKAGES = {}
# we keep Debian codification as reference, as it
# - has the most architectures supported
@@ -41,6 +54,9 @@ class PackagingSystem(metaclass=ABCMeta):
def __init__(self):
self._architecture = None
self._foreign_architectures = set()
+ # contexts to use when evaluating format- or distro-specific
+ # dependencies, in order by preference
+ self._contexts = ('generic',)
def read_architecture(self):
arch = os.uname()[4]
@@ -119,6 +135,44 @@ class PackagingSystem(metaclass=ABCMeta):
def override_lintian(self, destdir, package, tag, args):
pass
+ def format_relations(self, relations):
+ """Yield a native dependency representation for this packaging system
+ for each gdp.data.PackagingRelation in relations.
+ """
+ for pr in relations:
+ if pr.contextual:
+ for c in self._contexts:
+ if c in pr.contextual:
+ for x in self.format_relations([pr.contextual[c]]):
+ yield x
+
+ break
+ else:
+ yield self.format_relation(pr)
+
+ @abstractmethod
+ def format_relation(self, pr):
+ """Return a native dependency representation for this packaging system
+ and the given gdp.data.PackagingRelation. It is guaranteed
+ that pr.contextual is empty.
+ """
+ raise NotImplementedError
+
+ def rename_package(self, dependency):
+ """Given an abstract package name, return the corresponding
+ package name in this packaging system.
+
+ Abstract package names are mostly the same as for Debian,
+ except that libraries are represented as libfoo.so.0.
+ """
+ return self.RENAME_PACKAGES.get(dependency, dependency)
+
+ def package_for_tool(self, tool):
+ """Given an executable name, return the corresponding
+ package name in this packaging system.
+ """
+ return self.PACKAGE_MAP.get(tool, tool)
+
def get_native_packaging_system():
# lazy import when actually needed
from ..version import (FORMAT)
diff --git a/game_data_packager/packaging/arch.py b/game_data_packager/packaging/arch.py
index 48f6d2b..3b85e74 100644
--- a/game_data_packager/packaging/arch.py
+++ b/game_data_packager/packaging/arch.py
@@ -40,6 +40,10 @@ class ArchPackaging(PackagingSystem):
'i386': 'i686',
}
+ def __init__(self):
+ super(ArchPackaging, self).__init__()
+ self._contexts = ('arch', 'generic')
+
def read_architecture(self):
super(ArchPackaging, self).read_architecture()
# https://wiki.archlinux.org/index.php/Multilib
@@ -80,5 +84,20 @@ class ArchPackaging(PackagingSystem):
def install_packages(self, pkgs, method=None, gain_root='su'):
run_as_root(['pacman', '-U'] + list(pkgs), gain_root)
+ def format_relation(self, pr):
+ assert not pr.contextual
+ assert not pr.alternatives
+
+ if pr.version is not None:
+ op = pr.version_operator
+
+ if op in ('<<', '>>'):
+ op = op[0]
+
+ # foo>=1.0
+ return '%s%s%s' % (self.rename_package(pr.package), op, pr.version)
+
+ return self.rename_package(pr.package)
+
def get_distro_packaging():
return ArchPackaging()
diff --git a/game_data_packager/packaging/deb.py b/game_data_packager/packaging/deb.py
index a9f1cf1..ef4650b 100644
--- a/game_data_packager/packaging/deb.py
+++ b/game_data_packager/packaging/deb.py
@@ -38,11 +38,17 @@ class DebPackaging(PackagingSystem):
'7z': 'p7zip-full',
'unrar-nonfree': 'unrar',
}
+ RENAME_PACKAGES = {
+ 'libSDL-1.2.so.0': 'libsdl1.2debian',
+ 'libgcc_s.so.1': 'libgcc1',
+ 'libjpeg.so.62': 'libjpeg62-turbo | libjpeg62',
+ }
def __init__(self):
super(DebPackaging, self).__init__()
self.__installed = None
self.__available = None
+ self._contexts = ('deb', 'generic')
def read_architecture(self):
self._architecture = check_output(['dpkg',
@@ -138,6 +144,24 @@ class DebPackaging(PackagingSystem):
# gdebi-gtk etc.
subprocess.call([method] + list(debs))
+ def rename_package(self, p):
+ mapped = super(DebPackaging, self).rename_package(p)
+
+ if mapped != p:
+ return mapped
+
+ p = p.lower().replace('_', '-')
+
+ if '.so.' in p:
+ lib, version = p.split('.so.', 1)
+
+ if lib[-1] in '012345679':
+ lib += '-'
+
+ return lib + version
+
+ return p
+
def override_lintian(self, destdir, package, tag, args):
assert type(package) is str
lintiandir = os.path.join(destdir, 'usr/share/lintian/overrides')
@@ -145,5 +169,19 @@ class DebPackaging(PackagingSystem):
with open(os.path.join(lintiandir, package), 'a', encoding='utf-8') as l:
l.write('%s: %s %s\n' % (package, tag, args))
+ def format_relation(self, pr):
+ assert not pr.contextual
+
+ if pr.alternatives:
+ return ' | '.join([self.format_relation(p)
+ for p in pr.alternatives])
+
+ if pr.version is not None:
+ # foo (>= 1.0)
+ return '%s (%s %s)' % (self.rename_package(pr.package),
+ pr.version_operator, pr.version)
+
+ return self.rename_package(pr.package)
+
def get_distro_packaging():
return DebPackaging()
diff --git a/game_data_packager/packaging/rpm.py b/game_data_packager/packaging/rpm.py
index 77dfb4a..1f056f9 100644
--- a/game_data_packager/packaging/rpm.py
+++ b/game_data_packager/packaging/rpm.py
@@ -34,6 +34,10 @@ class RpmPackaging(PackagingSystem):
'amd64': 'x86_64',
}
+ def __init__(self, distro):
+ super(RpmPackaging, self).__init__()
+ self._contexts = (distro, 'rpm', 'generic')
+
def is_installed(self, package):
return 0 == subprocess.call(['rpm', '-q', package],
stdout=subprocess.DEVNULL,
@@ -72,6 +76,21 @@ class RpmPackaging(PackagingSystem):
' using rpm instead') % method)
run_as_root(['rpm', '-U'] + list(rpms), gain_root)
+ def format_relation(self, pr):
+ assert not pr.contextual
+ assert not pr.alternatives
+
+ if pr.version is not None:
+ op = pr.version_operator
+
+ if op in ('<<', '>>'):
+ op = op[0]
+
+ # foo >= 1.0
+ return '%s %s %s' % (self.rename_package(pr.package), op,
+ pr.version)
+
+ return self.rename_package(pr.package)
# XXX: dnf is written in python3 and has a stable public api,
# it is likely faster to use it instead of calling 'dnf' pgm.
diff --git a/tests/deb.py b/tests/deb.py
new file mode 100644
index 0000000..d9fe984
--- /dev/null
+++ b/tests/deb.py
@@ -0,0 +1,68 @@
+#!/usr/bin/python3
+# encoding=utf-8
+#
+# Copyright © 2016 Simon McVittie <smcv at debian.org>
+#
+# This program 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 2
+# of the License, or (at your option) any later version.
+#
+# This program 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.
+#
+# You can find the GPL license text on a Debian system under
+# /usr/share/common-licenses/GPL-2.
+
+import unittest
+
+from game_data_packager.data import (PackageRelation)
+from game_data_packager.packaging.deb import (DebPackaging)
+
+class DebTestCase(unittest.TestCase):
+ def setUp(self):
+ pass
+
+ def test_rename_package(self):
+ dp = DebPackaging()
+
+ def t(in_, out):
+ self.assertEqual(dp.rename_package(in_), out)
+
+ # generic
+ t('libc.so.6', 'libc6')
+ t('libalut.so.0', 'libalut0')
+ t('libXxf86dga.so.1', 'libxxf86dga1')
+ t('libopenal.so.1', 'libopenal1')
+ t('libstdc++.so.6', 'libstdc++6')
+ t('libdbus-1.so.3', 'libdbus-1-3')
+
+ # special cases
+ t('libSDL-1.2.so.0', 'libsdl1.2debian')
+ t('libgcc_s.so.1', 'libgcc1')
+ t('libjpeg.so.62', 'libjpeg62-turbo | libjpeg62')
+
+ def test_relation(self):
+ dp = DebPackaging()
+
+ def t(in_, out):
+ self.assertEqual(
+ sorted(dp.format_relations(map(PackageRelation, in_))),
+ out)
+
+ t(['libc.so.6'], ['libc6'])
+ t(['libc.so.6 (>= 2.19)'], ['libc6 (>= 2.19)'])
+ t(['libjpeg.so.62'], ['libjpeg62-turbo | libjpeg62'])
+ t(['libopenal.so.1 | bundled-openal'], ['libopenal1 | bundled-openal'])
+ t(['libc.so.6', 'libopenal.so.1'], ['libc6', 'libopenal1'])
+ t([dict(deb='foo', rpm='bar')], ['foo'])
+ t([dict(deb='foo', rpm='bar', generic='baz')], ['foo'])
+ t([dict(rpm='bar', generic='baz')], ['baz'])
+ t([dict(rpm='bar')], [])
+
+ def tearDown(self):
+ pass
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/tests/rpm.py b/tests/rpm.py
new file mode 100644
index 0000000..c3db71b
--- /dev/null
+++ b/tests/rpm.py
@@ -0,0 +1,54 @@
+#!/usr/bin/python3
+# encoding=utf-8
+#
+# Copyright © 2016 Simon McVittie <smcv at debian.org>
+#
+# This program 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 2
+# of the License, or (at your option) any later version.
+#
+# This program 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.
+#
+# You can find the GPL license text on a Debian system under
+# /usr/share/common-licenses/GPL-2.
+
+import unittest
+
+from game_data_packager.data import (PackageRelation)
+from game_data_packager.packaging.rpm import (RpmPackaging)
+
+class DebTestCase(unittest.TestCase):
+ def setUp(self):
+ pass
+
+ def test_relation(self):
+ # a random distribution that we don't actually support
+ rp = RpmPackaging('caldera')
+
+ def t(in_, out):
+ self.assertEqual(
+ sorted(rp.format_relations(map(PackageRelation, in_))),
+ out)
+
+ t(['libc.so.6'], ['libc.so.6'])
+ t(['libc.so.6 (>= 2.19)'], ['libc.so.6 >= 2.19'])
+ t(['libjpeg.so.62'], ['libjpeg.so.62'])
+ t(['libc.so.6', 'libopenal.so.1'], ['libc.so.6', 'libopenal.so.1'])
+ t([dict(deb='foo', rpm='bar')], ['bar'])
+ t([dict(deb='foo', rpm='bar', generic='baz')], ['bar'])
+ t([dict(deb='foo', generic='baz')], ['baz'])
+ t([dict(deb='foo')], [])
+ t([dict(rpm='foo', caldera='bar', fedora='baz')], ['bar'])
+
+ with self.assertRaises(AssertionError):
+ rp.format_relation(
+ PackageRelation('libopenal.so.1 | bundled-openal'))
+
+ def tearDown(self):
+ pass
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/tools/check_syntax.py b/tools/check_syntax.py
index 0148a24..6c8e230 100755
--- a/tools/check_syntax.py
+++ b/tools/check_syntax.py
@@ -15,12 +15,18 @@
# You can find the GPL license text on a Debian system under
# /usr/share/common-licenses/GPL-2.
+import logging
import os
import yaml
from game_data_packager import load_games
from game_data_packager.util import ascii_safe
+if os.environ.get('DEBUG') or os.environ.get('GDP_DEBUG'):
+ logging.getLogger().setLevel(logging.DEBUG)
+else:
+ logging.getLogger().setLevel(logging.INFO)
+
if __name__ == '__main__':
for name, game in load_games().items():
game.load_file_data()
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/game-data-packager.git
More information about the Pkg-games-commits
mailing list