[game-data-packager] 04/05: Add support for multi-package games, and use it to support Team Arena

Simon McVittie smcv at debian.org
Sat Jan 3 16:21:07 UTC 2015


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 999ce42ca0e9d6257d6cf2925e5ee87a59ce83ab
Author: Simon McVittie <smcv at debian.org>
Date:   Sat Jan 3 16:20:05 2015 +0000

    Add support for multi-package games, and use it to support Team Arena
---
 Makefile                                           |   4 +
 data/{jedi-academy-data.yaml => ja.yaml}           |   1 +
 data/{jedi-outcast-data.yaml => jk2.yaml}          |   1 +
 data/quake3-data.copyright                         |   2 +-
 data/quake3-data.postinst                          |   9 +
 data/quake3-data.postrm                            |   9 +
 data/quake3-data.preinst                           |   9 +
 data/quake3-data.prerm                             |   9 +
 data/quake3-team-arena-data.control.m4             |  20 ++
 ....copyright => quake3-team-arena-data.copyright} |   2 +-
 data/{quake3-data.yaml => quake3.yaml}             |  74 ++++--
 data/{rott-data.yaml => rott.yaml}                 |   1 +
 data/{rtcw-data.yaml => rtcw.yaml}                 |   1 +
 lib/game_data_packager/__init__.py                 | 249 ++++++++++++++-------
 lib/game_data_packager/__main__.py                 | 109 ++++++++-
 lib/via-python                                     |   7 +-
 supported/ja                                       |   2 +-
 supported/jk2                                      |   2 +-
 supported/quake3                                   |  10 +-
 supported/rott                                     |   4 +-
 supported/rtcw                                     |   2 +-
 21 files changed, 406 insertions(+), 121 deletions(-)

diff --git a/Makefile b/Makefile
index 68cba87..623a98b 100644
--- a/Makefile
+++ b/Makefile
@@ -7,6 +7,10 @@ default: $(DIRS)
 	chmod 0644 ./out/changelog.gz
 	install -m644 data/*.yaml out/
 	install -m644 data/*.copyright out/
+	install -m755 data/*.preinst out/
+	install -m755 data/*.postinst out/
+	install -m755 data/*.prerm out/
+	install -m755 data/*.postrm out/
 	set -e; for x in data/*.*.m4; do \
 		o="out/$${x#data/}"; \
 		o="$${o%.m4}"; \
diff --git a/data/jedi-academy-data.yaml b/data/ja.yaml
similarity index 99%
rename from data/jedi-academy-data.yaml
rename to data/ja.yaml
index b7fade4..0642121 100644
--- a/data/jedi-academy-data.yaml
+++ b/data/ja.yaml
@@ -1,6 +1,7 @@
 %YAML 1.2
 ---
 package: jedi-academy-data
+type: full
 
 # There's really no point in compressing the .deb; the only compressible
 # things in it are the control files and copyright information,
diff --git a/data/jedi-outcast-data.yaml b/data/jk2.yaml
similarity index 99%
rename from data/jedi-outcast-data.yaml
rename to data/jk2.yaml
index f2ce48e..aa404ed 100644
--- a/data/jedi-outcast-data.yaml
+++ b/data/jk2.yaml
@@ -1,6 +1,7 @@
 %YAML 1.2
 ---
 package: jedi-outcast-data
+type: full
 
 # Like Jedi Academy, there isn't much point in compressing this .deb.
 compress_deb: false
diff --git a/data/quake3-data.copyright b/data/quake3-data.copyright
index ed8c741..8f3d36a 100644
--- a/data/quake3-data.copyright
+++ b/data/quake3-data.copyright
@@ -1,7 +1,7 @@
 This package was generated using game-data-packager.
 Copyright © 2008-2013 Jonathan Dowland <jmtd at debian.org>.
 
-The pk3 files under "/usr/share/games/quake3" are user-supplied
+The pk3 files under "/usr/share/games" are user-supplied
 files that are not covered by the copyright or licence of this 
 package; they are copyright (c) 1999 by id Software, all rights reserved.
 
diff --git a/data/quake3-data.postinst b/data/quake3-data.postinst
new file mode 100644
index 0000000..a5bbaf4
--- /dev/null
+++ b/data/quake3-data.postinst
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+dpkg-maintscript-helper dir_to_symlink \
+	/usr/share/games/quake3/baseq3 \
+	../quake3-data/baseq3 \
+	37 \
+	quake3-data \
+	-- \
+	"$@"
diff --git a/data/quake3-data.postrm b/data/quake3-data.postrm
new file mode 100644
index 0000000..a5bbaf4
--- /dev/null
+++ b/data/quake3-data.postrm
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+dpkg-maintscript-helper dir_to_symlink \
+	/usr/share/games/quake3/baseq3 \
+	../quake3-data/baseq3 \
+	37 \
+	quake3-data \
+	-- \
+	"$@"
diff --git a/data/quake3-data.preinst b/data/quake3-data.preinst
new file mode 100644
index 0000000..a5bbaf4
--- /dev/null
+++ b/data/quake3-data.preinst
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+dpkg-maintscript-helper dir_to_symlink \
+	/usr/share/games/quake3/baseq3 \
+	../quake3-data/baseq3 \
+	37 \
+	quake3-data \
+	-- \
+	"$@"
diff --git a/data/quake3-data.prerm b/data/quake3-data.prerm
new file mode 100644
index 0000000..a5bbaf4
--- /dev/null
+++ b/data/quake3-data.prerm
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+dpkg-maintscript-helper dir_to_symlink \
+	/usr/share/games/quake3/baseq3 \
+	../quake3-data/baseq3 \
+	37 \
+	quake3-data \
+	-- \
+	"$@"
diff --git a/data/quake3-team-arena-data.control.m4 b/data/quake3-team-arena-data.control.m4
new file mode 100644
index 0000000..f73b9e8
--- /dev/null
+++ b/data/quake3-team-arena-data.control.m4
@@ -0,0 +1,20 @@
+Package: quake3-team-arena-data
+Version: VERSION
+Section: non-free/games
+Priority: optional
+Architecture: all
+Multi-Arch: foreign
+Depends: quake3-data (>= 38)
+Installed-Size: 500000
+Maintainer: Debian Games Team <pkg-games-devel at lists.alioth.debian.org>
+Description: Quake III Team Arena commercial data files
+ This package contains the commercial data from id Software's addon pack
+ "Quake III Team Arena" for the game "Quake III Arena", and was generated
+ using the "game-data-packager" program from the Debian package of
+ the same name.
+ .
+ To play Team Arena using this data, install the quake3 package
+ and select it from the Mods menu.
+ .
+ To run a dedicated server, install the quake3-server package
+ and run the server with the option "+set fs_game missionpack".
diff --git a/data/quake3-data.copyright b/data/quake3-team-arena-data.copyright
similarity index 93%
copy from data/quake3-data.copyright
copy to data/quake3-team-arena-data.copyright
index ed8c741..8f3d36a 100644
--- a/data/quake3-data.copyright
+++ b/data/quake3-team-arena-data.copyright
@@ -1,7 +1,7 @@
 This package was generated using game-data-packager.
 Copyright © 2008-2013 Jonathan Dowland <jmtd at debian.org>.
 
-The pk3 files under "/usr/share/games/quake3" are user-supplied
+The pk3 files under "/usr/share/games" are user-supplied
 files that are not covered by the copyright or licence of this 
 package; they are copyright (c) 1999 by id Software, all rights reserved.
 
diff --git a/data/quake3-data.yaml b/data/quake3.yaml
similarity index 59%
rename from data/quake3-data.yaml
rename to data/quake3.yaml
index 1cc21f4..4cab2bb 100644
--- a/data/quake3-data.yaml
+++ b/data/quake3.yaml
@@ -1,25 +1,55 @@
 %YAML 1.2
 ---
-package: quake3-data
-install_to: usr/share/games/quake3
+shortname: quake3
 compress_deb: false
 
-install_files_from_cksums: |
-  # Retail
-  1131927141 479493658 baseq3/pak0.pk3
-  # Patch
-  3518406034 374405 baseq3/pak1.pk3
-  243530540 7511182 baseq3/pak2.pk3
-  987913107 276305 baseq3/pak3.pk3
-  1445361719 9600350 baseq3/pak4.pk3
-  3989134593 191872 baseq3/pak5.pk3
-  1938352652 7346884 baseq3/pak6.pk3
-  241293937 320873 baseq3/pak7.pk3
-  1541013579 454478 baseq3/pak8.pk3
+packages:
+  quake3-data:
+    type: full
+    # We deliberately only put baseq3 in the search path, not
+    # missionpack, so that the engine doesn't think missionpack
+    # is installed unless its pak0.pk3 is present.
+    symlinks:
+      usr/share/games/quake3/baseq3:
+        usr/share/games/quake3-data/baseq3
+    install_to: usr/share/games/quake3-data
+    install_files_from_cksums: |
+      # Retail
+      1131927141 479493658 baseq3/pak0.pk3
+      # Patch
+      3518406034 374405 baseq3/pak1.pk3
+      243530540 7511182 baseq3/pak2.pk3
+      987913107 276305 baseq3/pak3.pk3
+      1445361719 9600350 baseq3/pak4.pk3
+      3989134593 191872 baseq3/pak5.pk3
+      1938352652 7346884 baseq3/pak6.pk3
+      241293937 320873 baseq3/pak7.pk3
+      1541013579 454478 baseq3/pak8.pk3
+      _ 383888 missionpack/pak1.pk3
+      _ 376234 missionpack/pak2.pk3
+      _ 384282 missionpack/pak3.pk3
+
+  quake3-team-arena-data:
+    type: expansion
+    symlinks:
+      usr/share/games/quake3/missionpack/pak0.pk3:
+        usr/share/games/quake3-team-arena-data/missionpack/pak0.pk3
+      usr/share/games/quake3/missionpack/pak1.pk3:
+        usr/share/games/quake3-data/missionpack/pak1.pk3
+      usr/share/games/quake3/missionpack/pak2.pk3:
+        usr/share/games/quake3-data/missionpack/pak2.pk3
+      usr/share/games/quake3/missionpack/pak3.pk3:
+        usr/share/games/quake3-data/missionpack/pak3.pk3
+    install_to: usr/share/games/quake3-team-arena-data
+    install_files:
+      missionpack/pak0.pk3:
+        size: 351623691
+        distinctive_size: true
 
 files:
   linuxq3apoint-1.32b-3.x86.run:
     size: 30923961
+    distinctive_size: true
     download:
       quake3-mirrors:
         path: .
@@ -38,6 +68,9 @@ files:
     - baseq3/pak6.pk3
     - baseq3/pak7.pk3
     - baseq3/pak8.pk3
+    - missionpack/pak1.pk3
+    - missionpack/pak2.pk3
+    - missionpack/pak3.pk3
     md5: c71fdddccb20e8fc393d846e9c61d685
     sha1: 802d84af0d515db50a496c4c55d1f1c4f40a9239
     sha256: c36132c5556b35e01950f1e9c646235033a5130f87ad776ba2bc7becf4f4f186
@@ -55,6 +88,10 @@ md5sums: |
   873888a73055c023f6c38b8ca3f2ce05  baseq3/pak6.pk3
   8fd38c53ed814b64f6ab03b5290965e4  baseq3/pak7.pk3
   d8b96d429ca4a9c289071cb7e77e14d2  baseq3/pak8.pk3
+  e8ba9e3bf06210930bc0e7fdbcdd01c2  missionpack/pak0.pk3
+  8b8a51f9edf2671dfeb53e0405bac3c6  missionpack/pak1.pk3
+  4e02be54614ca7dbed81b5c44a19302d  missionpack/pak2.pk3
+  e7f91f4eb6e3d28d978b8cca54631695  missionpack/pak3.pk3
 
 sha1sums: |
   9d588ea65e92944d3e23eeb6ec08f1dd666f4658  baseq3/pak0.pk3
@@ -66,6 +103,10 @@ sha1sums: |
   5716d9b9099067549f46ca77cf2cc7f8c0f498da  baseq3/pak6.pk3
   c20d9301aa833baaffed2194be46c89c42b5b4d1  baseq3/pak7.pk3
   657e2d95041ca37f7cfb23fa23850d0aad09e26c  baseq3/pak8.pk3
+  f34711efdc2de9ce9283f7b0eb4cd6fd40926fb0  missionpack/pak0.pk3
+  ce4b4cbee92a84479e620c2ca178aae7358576e5  missionpack/pak1.pk3
+  ed74b17a12ed8627ce9b15aa80487e90c65cf01d  missionpack/pak2.pk3
+  50adbfade3d9342a6ab75004e0013557243a30a5  missionpack/pak3.pk3
 
 sha256sums: |
   7ce8b3910620cd50a09e4f1100f426e8c6180f68895d589f80e6bd95af54bcae  baseq3/pak0.pk3
@@ -77,6 +118,10 @@ sha256sums: |
   bb4f0ae2bf603b050fb665436d3178ce7c1c20360e67bacf7c14d93daff38daf  baseq3/pak6.pk3
   de6283ce23e3486a2622c5dbf73d3721a59f24debd380e90f43a97d952fea283  baseq3/pak7.pk3
   812c9e97f231e89cefede3848c6110b7bd34245093af6f22c2cacde3e6b15663  baseq3/pak8.pk3
+  fdb5fe4f15f22bd270628d9b3153b733ca4548207722e768051c08c9dbff9135  missionpack/pak0.pk3
+  9818e99ba58d91f231a650a3c42559d1c5661cb3c0dfd033ef4225ba8ecdfd60  missionpack/pak1.pk3
+  6cc56de0bf7b58e30164ab487c51deb0fa7c62dbd7d0a5421a5d5aa58f31a3f4  missionpack/pak2.pk3
+  77c0bcbb61be81a389d8959b76969a801a5e589d97ab8aeb2cb7ced54f187fc7  missionpack/pak3.pk3
 
 # Not used yet
 sha512sums: |
@@ -89,6 +134,7 @@ sha512sums: |
   7a229cebaa262716aa80bfd59ba1f5207c988e48580e2a801de644f3f1c0ac82d64e0cc83b8cc780400f5a0bb1e09a98b1f640edace30101b517eec73a514f06  baseq3/pak6.pk3
   f9442fa0dfa6391056a082fa552014e27ad2620cb291302655c16265afd5c8bdb304ef3b06ae1255d57d50c00ecae030d4337708d01bd67af252fc006be87244  baseq3/pak7.pk3
   dcae254ac87a1d8453d5fb301105d9f1e55234e8044d6631f07f645f56c37b3b11aa168df1fe4ce9838f9f7c5e5ae4d6837286aef6297cae44094f750692ab5f  baseq3/pak8.pk3
+  16b36521c2d659f314d7380245f36e587ac7ebbea30cb323db4b84c2c63e4f947f28afb6fd17cbe86fe4b2ad2cccb8de3c809da7eba22f7f644c619d1be07bf8  missionpack/pak0.pk3
 
 ...
 # vim:set sw=2 sts=2 et:
diff --git a/data/rott-data.yaml b/data/rott.yaml
similarity index 99%
rename from data/rott-data.yaml
rename to data/rott.yaml
index fdc4838..2769f3e 100644
--- a/data/rott-data.yaml
+++ b/data/rott.yaml
@@ -2,6 +2,7 @@
 ---
 package: rott-data
 install_to: usr/share/games/rott
+type: demo
 
 install_files_from_cksums: |
   742977862  27028   DEMO1_3.DMO
diff --git a/data/rtcw-data.yaml b/data/rtcw.yaml
similarity index 99%
rename from data/rtcw-data.yaml
rename to data/rtcw.yaml
index b2c5d3b..151f908 100644
--- a/data/rtcw-data.yaml
+++ b/data/rtcw.yaml
@@ -1,6 +1,7 @@
 %YAML 1.2
 ---
 package: rtcw-data
+type: full
 
 # Like Jedi Academy, there isn't much point in compressing this .deb.
 compress_deb: false
diff --git a/lib/game_data_packager/__init__.py b/lib/game_data_packager/__init__.py
index 23ba75e..d49e7a8 100644
--- a/lib/game_data_packager/__init__.py
+++ b/lib/game_data_packager/__init__.py
@@ -148,7 +148,7 @@ class WantedFile(HashedFile):
         self.distinctive_name = True
         self.distinctive_size = False
         self.download = None
-        self._install_as = None
+        self.install_as = name
         self.install_to = None
         self._look_for = []
         self.optional = False
@@ -168,24 +168,6 @@ class WantedFile(HashedFile):
         self._look_for = set(x.lower() for x in value)
 
     @property
-    def install(self):
-        return (self._install_as is not None)
-    @install.setter
-    def install(self, value):
-        if value:
-            if self._install_as is None:
-                self._install_as = self.name
-        else:
-            self._install_as = None
-
-    @property
-    def install_as(self):
-        return self._install_as
-    @install_as.setter
-    def install_as(self, value):
-        self._install_as = value
-
-    @property
     def size(self):
         return self._size
     @size.setter
@@ -205,7 +187,6 @@ class WantedFile(HashedFile):
             'distinctive_name': self.distinctive_name,
             'distinctive_size': self.distinctive_size,
             'download': self.download,
-            'install': self.install,
             'install_as': self.install_as,
             'install_to': self.install_to,
             'look_for': list(self.look_for),
@@ -217,13 +198,62 @@ class WantedFile(HashedFile):
         }
 
 class GameDataPackage(object):
+    def __init__(self, name):
+        # The name of the binary package
+        self.name = name
+
+        # Where we install files.
+        # For instance, if this is 'usr/share/games/quake3' and we have
+        # a WantedFile with install_as='baseq3/pak1.pk3' then we would
+        # put 'usr/share/games/quake3/baseq3/pak1.pk3' in the .deb.
+        # The default is 'usr/share/games/' plus the binary package's name.
+        self.install_to = 'usr/share/games/' + name
+
+        # symlink => real file (the opposite way round that debhelper does it,
+        # because the links must be unique but the real files are not
+        # necessarily)
+        self.symlinks = {}
+
+        # set of names of WantedFile instances to be installed
+        self._install = set()
+
+        # type of package: full, demo or expansion
+        # full packages include quake-registered, quake2-full-data, quake3-data
+        # demo packages include quake-shareware, quake2-demo-data
+        # expansion packages include quake-armagon, quake-music, quake2-rogue
+        self._type = 'full'
+
+    @property
+    def install(self):
+        return self._install
+    @install.setter
+    def install(self, value):
+        self._install = set(value)
+
+    @property
+    def type(self):
+        return self._type
+    @type.setter
+    def type(self, value):
+        assert value in ('full', 'demo', 'expansion'), value
+        self._type = value
+
+    def to_yaml(self):
+        return {
+            'install': sorted(self.install),
+            'install_to': self.install_to,
+            'name': self.name,
+            'symlinks': self.symlinks,
+        }
+
+class GameData(object):
     def __init__(self,
-            name,
+            shortname,
             datadir='/usr/share/games/game-data-packager',
             etcdir='/etc/game-data-packager',
             workdir=None):
-        # The name of the binary package
-        self.name = name
+        # The name of the game
+        self.shortname = shortname
 
         # game-data-packager's configuration directory
         self.etcdir = etcdir
@@ -237,11 +267,27 @@ class GameDataPackage(object):
         # Clean up these directories on exit.
         self._cleanup_dirs = set()
 
+        # binary package name => GameDataPackage
+        self.packages = {}
+
         # If true, we may compress the .deb. If false, don't.
         self.compress_deb = True
 
-        self.yaml = yaml.load(open(os.path.join(self.datadir, name + '.yaml')))
-        assert self.yaml['package'] == name
+        self.yaml = yaml.load(open(os.path.join(self.datadir,
+            shortname + '.yaml')))
+
+        if 'package' in self.yaml:
+            package = GameDataPackage(self.yaml['package'])
+            self.packages[self.yaml['package']] = package
+            assert 'packages' not in self.yaml
+        else:
+            assert self.yaml['packages']
+            assert 'install_files' not in self.yaml
+
+            # these do not make sense at top level if there is more than
+            # one package
+            assert 'symlinks' not in self.yaml
+            assert 'install_files_from_cksums' not in self.yaml
 
         # Map from WantedFile name to instance.
         # { 'baseq3/pak1.pk3' => WantedFile instance }
@@ -260,39 +306,23 @@ class GameDataPackage(object):
         # Failed downloads
         self.download_failed = set()
 
-        # Where we install files.
-        # For instance, if this is 'usr/share/games/quake3' and we have
-        # a WantedFile with install_as='baseq3/pak1.pk3' then we would
-        # put 'usr/share/games/quake3/baseq3/pak1.pk3' in the .deb.
-        # The default is 'usr/share/games/' plus the binary package's name.
-        self.install_to = 'usr/share/games/' + name
-
-        if 'install_to' in self.yaml:
-            self.install_to = self.yaml['install_to']
-
-        # symlink => real file (the opposite way round that debhelper does it,
-        # because the links must be unique but the real files are not
-        # necessarily)
-        self.symlinks = {}
-
         self._populate_files(self.yaml.get('files'))
         self._populate_files(self.yaml.get('install_files'), install=True)
 
-        if 'symlinks' in self.yaml:
-            self.symlinks = self.yaml['symlinks']
+        if 'package' in self.yaml:
+            self._populate_package(next(iter(self.packages.values())),
+                    self.yaml)
 
-        if 'install_files_from_cksums' in self.yaml:
-            for line in self.yaml['install_files_from_cksums'].splitlines():
-                stripped = line.strip()
-                if stripped == '' or stripped.startswith('#'):
-                    continue
+        if 'packages' in self.yaml:
+            for binary, data in self.yaml['packages'].items():
+                # these should only be at top level, since they are global
+                assert 'md5sums' not in data, binary
+                assert 'sha1sums' not in data, binary
+                assert 'sha256sums' not in data, binary
 
-                _, size, filename = line.split(None, 2)
-                f = self._ensure_file(filename)
-                size = int(size)
-                assert (f.size == size or f.size is None), (f.size, size)
-                f.size = size
-                f.install = True
+                package = GameDataPackage(binary)
+                self.packages[binary] = package
+                self._populate_package(package, data)
 
         for alg in ('md5', 'sha1', 'sha256'):
             if alg + 'sums' in self.yaml:
@@ -318,6 +348,10 @@ class GameDataPackage(object):
                 yaml.safe_dump(self.to_yaml()))
 
         # consistency check
+        for package in self.packages.values():
+            for installable in package.install:
+                assert installable in self.files, installable
+
         for filename, wanted in self.files.items():
             if wanted.alternatives:
                 for alt in wanted.alternatives:
@@ -352,6 +386,7 @@ class GameDataPackage(object):
     def to_yaml(self):
         files = {}
         providers = {}
+        packages = {}
 
         for filename, f in self.files.items():
             files[filename] = f.to_yaml()
@@ -359,18 +394,57 @@ class GameDataPackage(object):
         for provided, by in self.providers.items():
             providers[provided] = list(by)
 
+        for name, package in self.packages.items():
+            packages[name] = package.to_yaml()
+
         return {
-            'package': self.name,
+            'packages': packages,
             'providers': providers,
             'files': files,
-            'install_to': self.install_to,
         }
 
-    def _populate_files(self, d, **kwargs):
+    def _populate_package(self, package, d):
+        if 'type' in d:
+            package.type = d['type']
+
+        if 'symlinks' in d:
+            package.symlinks = d['symlinks']
+
+        if 'install_to' in d:
+            package.install_to = d['install_to']
+
+        if 'install_files_from_cksums' in d:
+            for line in d['install_files_from_cksums'].splitlines():
+                stripped = line.strip()
+                if stripped == '' or stripped.startswith('#'):
+                    continue
+
+                _, size, filename = line.split(None, 2)
+                f = self._ensure_file(filename)
+                size = int(size)
+                assert (f.size == size or f.size is None), (f.size, size)
+                f.size = size
+                package.install.add(filename)
+
+        self._populate_files(d.get('install_files'), install=True,
+                install_package=package)
+
+    def _populate_files(self, d, install=False, install_package=None,
+            **kwargs):
         if d is None:
             return
 
+        if install and install_package is None:
+            assert len(self.packages) == 1
+            install_package = next(iter(self.packages.values()))
+
         for filename, data in d.items():
+            if data.get('install', install):
+                if install_package is None:
+                    assert len(self.packages) == 1
+                    install_package = next(iter(self.packages.values()))
+                install_package.install.add(filename)
+
             f = self._ensure_file(filename)
 
             for k in kwargs:
@@ -381,7 +455,6 @@ class GameDataPackage(object):
                     'distinctive_name',
                     'distinctive_size',
                     'download',
-                    'install',
                     'install_as',
                     'install_to',
                     'look_for',
@@ -451,6 +524,10 @@ class GameDataPackage(object):
         return True
 
     def consider_file(self, path, really_should_match_something):
+        if not os.path.exists(path):
+            # dangling symlink
+            return
+
         match_path = '/' + path.lower()
         size = os.path.getsize(path)
 
@@ -497,19 +574,27 @@ class GameDataPackage(object):
             logger.warning('file "%s" does not exist or is not a file or ' +
                     'directory', path)
 
-    def fill_gaps(self, download=False, log=True):
+    def fill_gaps(self, package, download=False, log=True):
+        assert package is not None
+
+        logger.debug('trying to fill any gaps for %s', package.name)
+
         possible = True
 
-        for (filename, wanted) in self.files.items():
-            if wanted.install and filename not in self.found:
+        for filename in package.install:
+            if filename not in self.found:
+                wanted = self.files[filename]
+
                 for alt in wanted.alternatives:
                     if alt in self.found:
                         break
                 else:
-                    logger.debug('gap needs to be filled: %s', filename)
+                    logger.debug('gap needs to be filled for %s: %s',
+                            package.name, filename)
 
-        for (filename, wanted) in self.files.items():
-            if wanted.install and filename not in self.found:
+        for filename in package.install:
+            if filename not in self.found:
+                wanted = self.files[filename]
                 alt_possible = False
 
                 for alt in wanted.alternatives:
@@ -517,12 +602,13 @@ class GameDataPackage(object):
                     if alt in self.found:
                         alt_possible = True
                         break
-                    elif self.fill_gap(self.files[alt], download=download):
+                    elif self.fill_gap(self.files[alt], download=download,
+                            log=log):
                         alt_possible = True
 
                 if alt_possible:
                     pass
-                elif not self.fill_gap(wanted, download=download):
+                elif not self.fill_gap(wanted, download=download, log=log):
                     possible = False
 
         return possible
@@ -806,16 +892,15 @@ class GameDataPackage(object):
 
         return True
 
-    def check_complete(self, log=False):
+    def check_complete(self, package, log=False):
         # Got everything?
         complete = True
-        for (filename, wanted) in self.files.items():
-            if not wanted.install:
-                continue
-
+        for filename in package.install:
             if filename in self.found:
                 continue
 
+            wanted = self.files[filename]
+
             for alt in wanted.alternatives:
                 if alt in self.found:
                     break
@@ -838,13 +923,13 @@ class GameDataPackage(object):
 
         return complete
 
-    def fill_dest_dir(self, destdir):
-        if not self.check_complete(log=True):
+    def fill_dest_dir(self, package, destdir):
+        if not self.check_complete(package, log=True):
             return False
 
-        docdir = os.path.join(destdir, 'usr/share/doc', self.name)
+        docdir = os.path.join(destdir, 'usr/share/doc', package.name)
         mkdir_p(docdir)
-        shutil.copyfile(os.path.join(self.datadir, self.name + '.copyright'),
+        shutil.copyfile(os.path.join(self.datadir, package.name + '.copyright'),
                 os.path.join(docdir, 'copyright'))
         shutil.copyfile(os.path.join(self.datadir, 'changelog.gz'),
                 os.path.join(docdir, 'changelog.gz'))
@@ -855,12 +940,16 @@ class GameDataPackage(object):
         assert destdir == self.workdir + '/slipstream.unpacked'
         debdir = os.path.join(self.workdir, 'DEBIAN')
         mkdir_p(debdir)
-        shutil.copyfile(os.path.join(self.datadir, self.name + '.control'),
+        shutil.copyfile(os.path.join(self.datadir, package.name + '.control'),
                 os.path.join(debdir, 'control'))
 
-        for (filename, wanted) in self.files.items():
-            if not wanted.install:
-                continue
+        for ms in ('preinst', 'postinst', 'prerm', 'postrm'):
+            maintscript = os.path.join(self.datadir, package.name + '.' + ms)
+            if os.path.isfile(maintscript):
+                shutil.copy(maintscript, os.path.join(debdir, ms))
+
+        for filename in package.install:
+            wanted = self.files[filename]
 
             if filename in self.found:
                 copy_from = self.found[filename]
@@ -878,7 +967,7 @@ class GameDataPackage(object):
                 logger.debug('Found %s at %s', wanted.name, copy_from)
                 copy_to = os.path.join(destdir,
                         (wanted.install_to if wanted.install_to is not None
-                            else self.install_to),
+                            else package.install_to),
                         wanted.install_as)
                 copy_to_dir = os.path.dirname(copy_to)
                 logger.debug('Copying to %s', copy_to)
@@ -889,7 +978,7 @@ class GameDataPackage(object):
                 subprocess.check_call(['cp', '--reflink=auto', copy_from,
                     copy_to])
 
-        for symlink, real_file in self.symlinks.items():
+        for symlink, real_file in package.symlinks.items():
             symlink = symlink.lstrip('/')
             real_file = real_file.lstrip('/')
 
diff --git a/lib/game_data_packager/__main__.py b/lib/game_data_packager/__main__.py
index 5e760ef..c41fa90 100644
--- a/lib/game_data_packager/__main__.py
+++ b/lib/game_data_packager/__main__.py
@@ -16,10 +16,14 @@
 # /usr/share/common-licenses/GPL-2.
 
 import argparse
+import logging
 import os
 import sys
 
-from . import GameDataPackage
+from . import GameData
+
+logging.basicConfig()
+logger = logging.getLogger('game-data-packager')
 
 def go(argv):
     parser = argparse.ArgumentParser(description='Package game files.',
@@ -29,28 +33,113 @@ def go(argv):
             metavar='DIRECTORY|FILE')
     args = parser.parse_args(argv[1:])
 
-    with GameDataPackage(argv[0],
+    with GameData(argv[0],
             datadir=os.environ['DATADIR'],
             workdir=os.environ['WORKDIR'],
             etcdir=os.environ['ETCDIR'],
-            ) as package:
+            ) as game:
 
         if args.repack:
-            args.paths.insert(0, '/' + package.install_to)
+            can_repack = False
+            absent = set()
+
+            for package in game.packages.values():
+                path = '/' + package.install_to
+                if os.path.isdir(path):
+                    args.paths.insert(0, path)
+                    can_repack = True
+                elif (package.name == 'quake3-data' and
+                        os.path.isdir('/usr/share/games/quake3')):
+                    # FIXME: this is a hack, it would be better to
+                    # have alternative locations defined in the YAML
+                    args.paths.insert(0, path)
+                    can_repack = True
+                else:
+                    absent.add(path)
+
+            if not can_repack:
+                raise SystemExit('cannot repack %s: could not open %r' %
+                        (package, sorted(absent)))
 
         for arg in args.paths:
-            package.consider_file_or_dir(arg)
+            game.consider_file_or_dir(arg)
 
-        if not package.fill_gaps(log=True):
-            # cannot possibly continue
-            sys.exit(1)
+        possible = set()
+
+        for package in game.packages.values():
+            if argv[0] in game.packages and package.name != argv[0]:
+                continue
+
+            if game.fill_gaps(package, log=False):
+                logger.debug('%s is possible', package.name)
+                possible.add(package)
 
-        package.fill_gaps(download=True)
+        if not possible:
+            logger.debug('No packages were possible')
+            # Repeat the process for the first (hopefully only)
+            # demo/shareware package, so we can log its errors.
+            for package in game.packages.values():
+                if package.type == 'demo':
+                    if game.fill_gaps(package=package, log=True):
+                        logger.error('%s unexpectedly succeeded on second ' +
+                                'attempt. Please report this as a bug',
+                                package.name)
+                        possible.add(package)
+                    else:
+                        sys.exit(1)
+            else:
+                # If no demo, repeat the process for the first
+                # (hopefully only) full package, so we can log *its* errors.
+                for package in game.packages.values():
+                    if package.type == 'full':
+                        if game.fill_gaps(package=package, log=True):
+                            logger.error('%s unexpectedly succeeded on ' +
+                                    'second attempt. Please report this as '
+                                    'a bug', package.name)
+                            possible.add(package)
+                        else:
+                            sys.exit(1)
+                else:
+                    raise SystemExit('Unable to complete any packages. ' +
+                            'Please provide more files or directories.')
 
-        if not package.fill_dest_dir(os.environ['DESTDIR']):
+        package = None
+
+        # We can only build one package for now, because the shell script
+        # wrapper can only do one. Pick the "best": full if we have the
+        # right stuff, or expansion if we have the right stuff, or demo.
+        if len(possible) == 1:
+            for p in possible:
+                package = p
+        else:
+            for p in possible:
+                if p.type == 'full':
+                    package = p
+                    break
+            else:
+                for p in possible:
+                    if p.type == 'expansion':
+                        package = p
+                        break
+                else:
+                    # just pick the first one
+                    for p in possible:
+                        package = p
+
+        logger.debug('chose to produce %s', package.name)
+
+        if not game.fill_gaps(package=package, download=True, log=True):
+            raise SystemExit('Failed to download necessary files for %s',
+                    package.name)
+
+        if not game.fill_dest_dir(package, os.environ['DESTDIR']):
             sys.exit(1)
 
     # FIXME: make the .deb (currently done in shell script by the wrapper)
 
+    # This is a hack to communicate the name of the .deb to the shell
+    # script...
+    open(os.environ['WORKDIR'] + '/DEB_NAME', 'w').write(package.name)
+
 if __name__ == '__main__':
     go(sys.argv[1:])
diff --git a/lib/via-python b/lib/via-python
index 6604e95..cd9ef74 100644
--- a/lib/via-python
+++ b/lib/via-python
@@ -32,7 +32,7 @@ gdp_data_driven () {
     if [ -n "${DEBUG:-}" ]; then
         export DEBUG
     fi
-    python3 -m game_data_packager "$deb" "$@"
+    python3 -m game_data_packager "$@"
     exit $?
     )
 
@@ -47,6 +47,10 @@ gdp_data_driven () {
 
     # We still do this bit in shell script, for now
 
+    if [ -e "$WORKDIR/DEB_NAME" ]; then
+        deb="$(cat "$WORKDIR/DEB_NAME")"
+    fi
+
     if [ "" = "$OUTDIR" ]; then
         OUTFILE="$WORKDIR/out.deb"
     else
@@ -64,6 +68,7 @@ gdp_data_driven () {
     ( cd "$WORKDIR" && slipstream_instsize )
     ( cd "$WORKDIR" && slipstream_repack "$OUTFILE" )
 
+    rm -f "$WORKDIR/DEB_NAME"
     rm -f "$WORKDIR/DO-NOT-COMPRESS"
     rm -fr "$WORKDIR/tmp"
     rm -fr "$DESTDIR"
diff --git a/supported/ja b/supported/ja
index 180ea4b..e30f07a 100644
--- a/supported/ja
+++ b/supported/ja
@@ -34,5 +34,5 @@ game-data-packager ${SHORTNAME} --repack\n\
 }
 
 go () {
-    gdp_data_driven "$@"
+    gdp_data_driven ja "$@"
 }
diff --git a/supported/jk2 b/supported/jk2
index b38f789..a803ff9 100644
--- a/supported/jk2
+++ b/supported/jk2
@@ -34,5 +34,5 @@ game-data-packager ${SHORTNAME} --repack\n\
 }
 
 go () {
-    gdp_data_driven "$@"
+    gdp_data_driven jk2 "$@"
 }
diff --git a/supported/quake3 b/supported/quake3
index f30b736..3e85d7c 100644
--- a/supported/quake3
+++ b/supported/quake3
@@ -2,17 +2,9 @@
 
 SHORTNAME=quake3
 LONGNAME="Quake III Arena"
-deb=quake3-data
-basedir='/usr/share/games/quake3'
 
 . $LIBDIR/via-python
 
-usage() {
-echo "quake3 game arguments:"
-printf "\tmountpoint - Quake III Arena CD mount-point or baseq3/pak0.pk3 filename\n"
-printf "\tpoint release - ${POINTFILE}\n"
-}
-
 go () {
-    gdp_data_driven "$@"
+    gdp_data_driven quake3 "$@"
 }
diff --git a/supported/rott b/supported/rott
index cd3a248..fd34534 100644
--- a/supported/rott
+++ b/supported/rott
@@ -23,7 +23,7 @@ go() {
                     rott_usage >&2
                     exit 1
             fi
-            gdp_data_driven --
+            gdp_data_driven rott --
             ;;
         2)
             if [ "$1" != "-f" ]; then
@@ -31,7 +31,7 @@ go() {
                 rott_usage >&2
                 exit 1
             fi
-            gdp_data_driven "$2"
+            gdp_data_driven rott "$2"
             ;;
         *)
             usage >&2
diff --git a/supported/rtcw b/supported/rtcw
index dd991ac..aa43540 100644
--- a/supported/rtcw
+++ b/supported/rtcw
@@ -36,5 +36,5 @@ game-data-packager ${SHORTNAME} --repack\n\
 }
 
 go () {
-    gdp_data_driven "$@"
+    gdp_data_driven rtcw "$@"
 }

-- 
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