#!/usr/bin/env python # gen_repo - build a repository db file from a set of packages # # Author: Michael Towers (gradgrind) # # This file is part of the larch project. # # larch 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. # # larch 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 larch; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # #---------------------------------------------------------------------------- # # Version 1.5 // 7th July 2007 import os import os.path import sys import tarfile from types import * import re from subprocess import check_call # to add a package: #check_call(["repo-add", dbfile, pkg, pkg, pkg, ...]) # Regex to remove version comparison from package dependency onlyname = re.compile("([^=><]+).*") def create_db(dbname, packagesdir, dep_ignore_list): os.chdir(packagesdir) dbfile = dbname + ".db.tar.gz" if os.path.exists(dbfile): os.remove(dbfile) # Get a list of packages packages = filter(lambda s: s.endswith(".pkg.tar.gz"), os.listdir(".")) packages.sort() # Use 'repo-add' to build the repo check_call(["repo-add", dbfile] + packages) # Make a dict for keeping track of dependencies dep_dict = {} for p in packages: pkg_dict = get_pkg_info(p) pkg_name = pkg_dict["pkgname"] pkg_dbname = pkg_name + "-" + pkg_dict["pkgver"] # Add dependency info to dependency dict for d in pkg_dict["depend"]: # But also need to cater for versioning!!! # I will just ignore it here ... dm = onlyname.match(d) if not dm: if d: print "DEBUG: package %s, dependency = '%s'" % (pkg_name, d) continue d = dm.group(1) if not dep_dict.has_key(d): dep_dict[d] = [False] dep_dict[d].append(pkg_name) # Mark packages provided by this one for p in (pkg_dict["provides"] + [pkg_name]): if dep_dict.has_key(p): dep_dict[p][0] = True else: dep_dict[p] = [True] # Mark packages in ignore list for p in dep_ignore_list: if dep_dict.has_key(p): dep_dict[p][0] = True # Now display unsatisfied dependencies # Should add the possibility of declaring a list of packages # available (e.g. the base set, or all those on the live CD ..." print "-------------\nUnsatisfied dependencies:" for d, r in dep_dict.items(): if not r[0]: print " ", d, "- needed by: ", for p in r[1:]: print p, " ", print "" def get_pkg_info(pkg): tf = tarfile.open(pkg, "r:gz") pkginfo = tf.extractfile(".PKGINFO") pkg_dict = {# the first ones go to 'desc' "pkgname" : None, "pkgver" : None, # from here they are optional, and can occur more than once "depend" : [], "provides" : [], } while True: l = pkginfo.readline().strip() if not l: break if l[0] == "#": continue split3 = l.split(None, 2) while len(split3) < 3: split3.append("") key, eq, value = split3 if not pkg_dict.has_key(key): continue val = pkg_dict[key] if val == None: pkg_dict[key] = value continue if not isinstance(val, ListType): print "Unexpected situation ...\n key [oldvalue] <- newvalue" print key, "[%s]" % val, "<-", value sys.exit(1) pkg_dict[key].append(value) pkginfo.close() return pkg_dict def cat(path): """Python version of 'cat'""" fp = open(path, "r") op = "" for l in fp: op += l fp.close() return op def usage(): print """ gen_repo package-dir [repo-name] [-- ignore-list] Generate a pacman db file for the packages in package-dir. If repo-name is given, this will be used as the name for the repository, otherwise the name of the directory containing the packages will be used. All dependencies of the packages in the repository will be listed to standard output, but a list of packages not to be included in this list can be specified: ignore-list should be either a file containing the names of packages not to be listed as dependencies (separated by space or newline), or a directory containing 'package directories', like /var/abs/base or /var/lib/pacman/local """ sys.exit(1) if __name__ == "__main__": if len(sys.argv) < 2: usage() if os.getuid() != 0: print "Must be root to run this" sys.exit(1) pkgdir = sys.argv[1] if (len(sys.argv) == 2) or (sys.argv[2] == "--"): dbname = os.path.basename(os.path.abspath(pkgdir)) i = 2 else: dbname = sys.argv[2] i = 3 if len(sys.argv) == i: ignore_list = [] elif (len(sys.argv) == i+2) and (sys.argv[i] == "--"): ignore_list = sys.argv[i+1] else: usage() if not os.path.isdir(pkgdir): print "\n1st argument must be a directory" sys.exit(1) print "\nCreating pacman database (%s.db.tar.gz) file in %s" % (dbname, pkgdir) if ignore_list: # Get list of packages to be ignored in dependency list if os.path.isfile(ignore_list): # A simple file containing the names of packages to ignore # separated by space or newline. ignore_list = cat(ignore_list).split() elif os.path.isdir(ignore_list): # A directory containing packages or package-directories (like in abs) l = os.listdir(ignore_list) # See if there are packages in this directory lp = filter(lambda s: s.endswith(".pkg.tar.gz"), l) if lp: l = map(lambda s: s.replace(".pkg.tar.gz", ""), lp) re1 = re.compile("(.+)-[^-]+?-[0-9]+") ignore_list = [] for f in l: m = re1.match(f) if m: ignore_list.append(m.group(1)) else: # the directory contains just the package names (like abs) ignore_list.append(m) else: print "!!! Invalid ignore-list" usage() create_db(dbname, pkgdir, ignore_list)