summaryrefslogtreecommitdiffstats
path: root/BuildManager
diff options
context:
space:
mode:
authorBogdano Arendartchuk <bogdano@mandriva.org>2007-05-07 21:51:40 +0000
committerBogdano Arendartchuk <bogdano@mandriva.org>2007-05-07 21:51:40 +0000
commit11e2e30360a24bcc22ca4231fca495e5d846abb5 (patch)
tree9bf91715b22e91a7b739c72a08f6a3f4395c1c25 /BuildManager
downloadbm-11e2e30360a24bcc22ca4231fca495e5d846abb5.tar
bm-11e2e30360a24bcc22ca4231fca495e5d846abb5.tar.gz
bm-11e2e30360a24bcc22ca4231fca495e5d846abb5.tar.bz2
bm-11e2e30360a24bcc22ca4231fca495e5d846abb5.tar.xz
bm-11e2e30360a24bcc22ca4231fca495e5d846abb5.zip
Imported bm-2.1 from2.1
svn+ssh://svn.mandriva.com/svn/packages/cooker/bm/current/SOURCES/bm-2.1.tar.bz2 at r24959.
Diffstat (limited to 'BuildManager')
-rw-r--r--BuildManager/__init__.py24
-rw-r--r--BuildManager/build.py231
-rw-r--r--BuildManager/clean.py37
-rw-r--r--BuildManager/fileutil.py187
-rw-r--r--BuildManager/optionparser.py34
-rw-r--r--BuildManager/package.py243
-rw-r--r--BuildManager/rpmver.py62
7 files changed, 818 insertions, 0 deletions
diff --git a/BuildManager/__init__.py b/BuildManager/__init__.py
new file mode 100644
index 0000000..eea669a
--- /dev/null
+++ b/BuildManager/__init__.py
@@ -0,0 +1,24 @@
+import logging
+import sys, os
+
+__all__ = ["Error", "logger"]
+
+class Error(Exception): pass
+
+def getlogger():
+ class Formatter(logging.Formatter):
+ def format(self, record):
+ if record.levelname == "INFO":
+ record.llevelname = ""
+ else:
+ record.llevelname = record.levelname.lower()+": "
+ return logging.Formatter.format(self, record)
+ formatter = Formatter("%(llevelname)s%(message)s")
+ handler = logging.StreamHandler(sys.stderr)
+ handler.setFormatter(formatter)
+ logger = logging.getLogger("bm")
+ logger.addHandler(handler)
+ return logger
+
+logger = getlogger()
+
diff --git a/BuildManager/build.py b/BuildManager/build.py
new file mode 100644
index 0000000..3bf93c5
--- /dev/null
+++ b/BuildManager/build.py
@@ -0,0 +1,231 @@
+from BuildManager.fileutil import *
+from BuildManager.package import *
+from BuildManager import *
+import thread
+import popen2
+import select
+import fcntl
+import thread
+import sys, os
+import time
+import shutil
+
+__all__ = ["PackageBuilder"]
+
+GLOBAL_PKGLIST_LOCK = thread.allocate_lock()
+
+STAGE_UNPACK = 0
+STAGE_PREP = 1
+STAGE_COMPILE = 2
+STAGE_INSTALL = 3
+STAGE_SOURCE = 4
+STAGE_BINARY = 5
+STAGE_ALL = 6
+
+STAGE_DICT = {"unpack": STAGE_UNPACK,
+ "prep": STAGE_PREP,
+ "compile": STAGE_COMPILE,
+ "install": STAGE_INSTALL,
+ "source": STAGE_SOURCE,
+ "binary": STAGE_BINARY,
+ "all": STAGE_ALL}
+
+class PackageBuilder:
+ def __init__(self, opts):
+ self.opts = opts
+ self.stage = STAGE_DICT[opts.mode]
+
+ def run(self):
+ self.pkglist = PackageList()
+ logger.info("creating package list")
+ for filename in self.opts.args:
+ pkg = Package(filename, self.opts.build_log)
+ for ignore in self.opts.ignore:
+ if ignore.match(pkg.name):
+ break
+ else:
+ self.pkglist.append(pkg)
+ for dir in self.opts.filter_renew:
+ filterpkglist(pkglist, dir, "not_has_ge")
+ for dir in self.opts.filter_refresh:
+ filterpkglist(pkglist, dir, "not_has_lt")
+ self.pkgsleft = len(self.pkglist)
+ if self.pkgsleft == 0:
+ logger.info("no packages to process")
+ return True
+ elif self.pkgsleft > 1 and self.pkgsleft % 10 != 0:
+ logger.info("package list has %d packages" % pkgsleft)
+ self.pkglist_lock = thread.allocate_lock()
+ self.failures = 0
+ if self.opts.parallel != 1:
+ logger.info("starting threads")
+ for i in range(self.opts.parallel-1):
+ thread.start_new_thread(self_processlist, ())
+ self._processlist()
+ while self.pkgsleft > 0:
+ time.sleep(2)
+ return not self.failures
+
+ def _processlist(self):
+ while 1:
+ self.pkglist_lock.acquire()
+ if not self.pkglist:
+ self.pkglist_lock.release()
+ return 1
+ if self.pkgsleft % 10 == 0:
+ logger.info("package list has %d packages" % self.pkgsleft)
+ pkg = self.pkglist.pop(0)
+ logger.info("processing package %s-%s-%s" %
+ (pkg.name, pkg.version, pkg.release))
+ self.pkglist_lock.release()
+ ret = buildpkg(pkg=pkg,
+ stage=self.stage,
+ unpack_dir=self.opts.unpack_dir,
+ passtrough=" ".join(self.opts.options),
+ show_log=self.opts.show_log,
+ dryrun=self.opts.dryrun)
+ if ret:
+ if pkg.type == "srpm":
+ if self.opts.move_succeeded_srpm:
+ move_file(pkg.file,
+ self.opts.move_succeeded_srpm,
+ dryrun=self.opts.dryrun)
+ elif self.opts.copy_succeeded_srpm:
+ copy_file(pkg.file,
+ self.opts.copy_succeeded_srpm,
+ dryrun=self.opts.dryrun)
+ elif self.opts.remove_succeeded_srpm:
+ logger.info("removing %s" % pkg.file)
+ if not self.opts.dryrun:
+ os.unlink(pkg.file)
+ if self.opts.move_srpm:
+ dir = os.path.join(pkg.builddir, "SRPMS")
+ for file in os.listdir(dir):
+ move_file(os.path.join(dir, file),
+ self.opts.move_srpm,
+ dryrun=self.opts.dryrun)
+ if self.opts.move_rpm:
+ dir = os.path.join(pkg.builddir, "RPMS")
+ for subdir in os.listdir(dir):
+ subdir = os.path.join(dir, subdir)
+ for file in os.listdir(subdir):
+ move_file(os.path.join(subdir, file),
+ self.opts.move_rpm,
+ dryrun=self.opts.dryrun)
+ if self.opts.move_log:
+ move_file(pkg.log,
+ self.opts.move_log,
+ dryrun=self.dryrun)
+ if self.opts.clean or self.opts.clean_on_success:
+ if pkg.builddir != "/":
+ logger.info("cleaning build directory")
+ if not self.opts.dryrun:
+ shutil.rmtree(pkg.builddir)
+ else:
+ self.failures += 1
+ if pkg.type == "srpm":
+ if self.opts.move_failed_srpm:
+ move_file(pkg.file,
+ self.opts.move_failed_srpm,
+ dryrun=self.opts.dryrun)
+ elif self.opts.copy_failed_srpm:
+ copy_file(pkg.file,
+ self.opts.copy_failed_srpm,
+ dryrun=self.opts.dryrun)
+ elif self.opts.remove_failed_srpm:
+ logger.info("removing %s" % pkg.file)
+ if not self.opts.dryrun:
+ os.unlink(pkg.file)
+ if self.opts.move_failed_log:
+ move_file(pkg.log,
+ self.opts.move_failed_log,
+ dryrun=self.opts.dryrun)
+ if self.opts.clean:
+ if pkg.builddir != "/":
+ logger.info("cleaning build directory")
+ if not self.opts.dryrun:
+ shutil.rmtree(pkg.builddir)
+ self.pkglist_lock.acquire()
+ self.pkgsleft -= 1
+ self.pkglist_lock.release()
+
+def filterpkglist(pkglist, directory, rule):
+ filterlist = PackageList()
+ logger.info("creating package list filter for "+directory)
+ for filename in os.listdir(directory):
+ filename = os.path.join(directory, filename)
+ if os.path.isfile(filename):
+ filterlist.append(Package(filename))
+ logger.info("filtering")
+ if rule[:4] == "not_":
+ filterfunc_tmp = getattr(filterlist, rule[4:])
+ filterfunc = lambda x: not filterfunc_tmp(x)
+ else:
+ filterfunc = getattr(filterlist, rule)
+ pkglist[:] = filter(filterfunc, pkglist)
+
+
+def buildpkglist(pkglist, stage, unpack_dir, passtrough="",
+ show_log=0, dryrun=0):
+ while 1:
+ GLOBAL_PKGLIST_LOCK.acquire()
+ if not pkglist:
+ GLOBAL_PKGLIST_LOCK.release()
+ return 1
+ pkg = pkglist[0]
+ del pkglist[0]
+ GLOBAL_PKGLIST_LOCK.release()
+ buildpkg(pkg, stage, unpack_dir, passtrough, show_log, dryrun)
+
+def buildpkg(pkg, stage, unpack_dir, passtrough="", show_log=0, dryrun=0):
+ stagestr = ["unpacking",
+ "running prep stage",
+ "running prep and compile stage",
+ "running prep, compile, and install stages",
+ "building source package",
+ "building binary packages",
+ "building source and binary packages"][stage]
+ logger.info(stagestr)
+ ret = 0
+ if pkg.type == "srpm" and not (dryrun or pkg.unpack(unpack_dir)):
+ logger.error("failed unpacking")
+ return 0
+ else:
+ status = 0
+ if stage != STAGE_UNPACK:
+ stagechar = ["p","c","i","s","b","a"][stage-1]
+ if not dryrun and os.path.isdir(pkg.builddir+"/BUILDROOT"):
+ tmppath = " --define '_tmppath %s/BUILDROOT'" % pkg.builddir
+ else:
+ tmppath = ""
+ cmd = "rpm -b%s --define '_topdir %s'%s %s %s 2>&1" % \
+ (stagechar,pkg.builddir,tmppath,passtrough,pkg.spec)
+ logger.debug("rpm command: "+cmd)
+ if not dryrun:
+ log = open(pkg.log, "w")
+ pop = popen2.Popen3(cmd)
+ fc = pop.fromchild
+ flags = fcntl.fcntl (fc.fileno(), fcntl.F_GETFL, 0)
+ flags = flags | os.O_NONBLOCK
+ fcntl.fcntl (fc.fileno(), fcntl.F_SETFL, flags)
+ while 1:
+ r,w,x = select.select([fc.fileno()], [], [], 2)
+ if r:
+ data = fc.read()
+ if show_log:
+ sys.stdout.write(data)
+ log.write(data)
+ log.flush()
+ status = pop.poll()
+ if status != -1:
+ break
+ log.close()
+ if status == 0:
+ logger.info("succeeded!")
+ ret = 1
+ else:
+ logger.error("failed!")
+ ret = 0
+ return ret
+
+# vim:ts=4:sw=4
diff --git a/BuildManager/clean.py b/BuildManager/clean.py
new file mode 100644
index 0000000..e0e3f93
--- /dev/null
+++ b/BuildManager/clean.py
@@ -0,0 +1,37 @@
+from BuildManager.fileutil import *
+from BuildManager.package import *
+from BuildManager import *
+import os
+
+class PackageCleaner:
+ def __init__(self, opts):
+ self.opts = opts
+
+ def run(self):
+ pkglist = PackageList()
+ pkglist_check = PackageList()
+ logger.info("creating package list")
+ for filename in self.opts.args:
+ pkglist.append(Package(filename))
+ if self.opts.check:
+ for dir in self.opts.check:
+ logger.info("creating package check list for "+dir)
+ for entry in os.listdir(dir):
+ entrypath = os.path.join(dir, entry)
+ if os.path.isfile(entrypath):
+ pkglist_check.append(Package(entrypath))
+ logger.info("processing package list")
+ for pkg in pkglist[:]:
+ if pkglist.has_gt(pkg) or pkglist_check.has_gt(pkg):
+ pkglist.remove(pkg)
+ if self.opts.move:
+ move_file(pkg.file, self.opts.move,
+ dryrun=self.opts.dryrun)
+ elif self.opts.copy:
+ copy_file(pkg.file, self.opts.copy,
+ dryrun=self.opts.dryrun)
+ else:
+ logger.info("removing "+pkg.file)
+ if not self.opts.dryrun:
+ os.unlink(pkg.file)
+ return True
diff --git a/BuildManager/fileutil.py b/BuildManager/fileutil.py
new file mode 100644
index 0000000..93dd09d
--- /dev/null
+++ b/BuildManager/fileutil.py
@@ -0,0 +1,187 @@
+# This module was originally part of distutils.
+from BuildManager import *
+import os
+
+__all__ = ["copy_file", "move_file"]
+
+# for generating verbose output in 'copy_file()'
+_copy_action = { None: 'copying',
+ 'hard': 'hard linking',
+ 'sym': 'symbolically linking' }
+
+
+def _copy_file_contents (src, dst, buffer_size=16*1024):
+ """Copy the file 'src' to 'dst'; both must be filenames. Any error
+ opening either file, reading from 'src', or writing to 'dst', raises
+ BuildManagerFileError. Data is read/written in chunks of 'buffer_size'
+ bytes (default 16k). No attempt is made to handle anything apart from
+ regular files.
+ """
+ # Stolen from shutil module in the standard library, but with
+ # custom error-handling added.
+
+ fsrc = None
+ fdst = None
+ try:
+ try:
+ fsrc = open(src, 'rb')
+ except os.error, (errno, errstr):
+ raise Error, "could not open %s: %s" % (src, errstr)
+
+ try:
+ fdst = open(dst, 'wb')
+ except os.error, (errno, errstr):
+ raise Error, "could not create %s: %s" % (dst, errstr)
+
+ while 1:
+ try:
+ buf = fsrc.read(buffer_size)
+ except os.error, (errno, errstr):
+ raise Error, "could not read from %s: %s" % (src, errstr)
+
+ if not buf:
+ break
+
+ try:
+ fdst.write(buf)
+ except os.error, (errno, errstr):
+ raise Error, "could not write to %s: %s" % (dst, errstr)
+
+ finally:
+ if fdst:
+ fdst.close()
+ if fsrc:
+ fsrc.close()
+
+def copy_file (src, dst, preserve_mode=1, preserve_times=1, link=None,
+ dryrun=False):
+
+ """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is
+ copied there with the same name; otherwise, it must be a filename. (If
+ the file exists, it will be ruthlessly clobbered.) If 'preserve_mode'
+ is true (the default), the file's mode (type and permission bits, or
+ whatever is analogous on the current platform) is copied. If
+ 'preserve_times' is true (the default), the last-modified and
+ last-access times are copied as well.
+
+ 'link' allows you to make hard links (os.link) or symbolic links
+ (os.symlink) instead of copying: set it to "hard" or "sym"; if it is
+ None (the default), files are copied. Don't set 'link' on systems that
+ don't support it: 'copy_file()' doesn't check if hard or symbolic
+ linking is available.
+
+ Under Mac OS, uses the native file copy function in macostools; on
+ other systems, uses '_copy_file_contents()' to copy file contents.
+
+ Return a tuple (dest_name, copied): 'dest_name' is the actual name of
+ the output file, and 'copied' is true if the file was copied (or would
+ have been copied, if 'dryrun' true).
+ """
+ from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE
+
+ if not os.path.isfile(src):
+ raise Error, "can't copy '%s': " \
+ "doesn't exist or not a regular file" % src
+
+ if os.path.isdir(dst):
+ dir = dst
+ dst = os.path.join(dst, os.path.basename(src))
+ else:
+ dir = os.path.dirname(dst)
+
+ try:
+ action = _copy_action[link]
+ except KeyError:
+ raise ValueError, "invalid value '%s' for 'link' argument" % link
+ if os.path.basename(dst) == os.path.basename(src):
+ logger.info("%s %s to %s" % (action, src, dir))
+ else:
+ logger.info("%s %s to %s" % (action, src, dst))
+
+ if dryrun:
+ return (dst, 1)
+
+ # If linking (hard or symbolic), use the appropriate system call
+ # (Unix only, of course, but that's the caller's responsibility)
+ if link == 'hard':
+ if not (os.path.exists(dst) and os.path.samefile(src, dst)):
+ os.link(src, dst)
+ elif link == 'sym':
+ if not (os.path.exists(dst) and os.path.samefile(src, dst)):
+ os.symlink(src, dst)
+
+ # Otherwise (not linking), copy the file contents and
+ # (optionally) copy the times and mode.
+ else:
+ _copy_file_contents(src, dst)
+ if preserve_mode or preserve_times:
+ st = os.stat(src)
+ if preserve_times:
+ os.utime(dst, (st[ST_ATIME], st[ST_MTIME]))
+ if preserve_mode:
+ os.chmod(dst, S_IMODE(st[ST_MODE]))
+
+ return (dst, 1)
+
+def move_file (src, dst, dryrun=False):
+
+ """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will
+ be moved into it with the same name; otherwise, 'src' is just renamed
+ to 'dst'. Return the new full name of the file.
+
+ Handles cross-device moves on Unix using 'copy_file()'. What about
+ other systems???
+ """
+ from os.path import exists, isfile, isdir, basename, dirname
+ import errno
+
+ logger.info("moving %s to %s" % (src, dst))
+
+ if dryrun:
+ return dst
+
+ if not isfile(src):
+ raise Error, "can't move %s: not a regular file" % src
+
+ if isdir(dst):
+ dst = os.path.join(dst, basename(src))
+ elif exists(dst):
+ raise Error, "can't move %s: destination %s already exists" % \
+ (src, dst)
+
+ if not isdir(dirname(dst)):
+ raise Error, "can't move %s: destination %s not a valid path" % \
+ (src, dst)
+
+ copy_it = 0
+ try:
+ os.rename(src, dst)
+ except os.error, (num, msg):
+ if num == errno.EXDEV:
+ copy_it = 1
+ else:
+ raise Error, "couldn't move %s to %s: %s" % (src, dst, msg)
+
+ if copy_it:
+ copy_file(src, dst)
+ try:
+ os.unlink(src)
+ except os.error, (num, msg):
+ try:
+ os.unlink(dst)
+ except os.error:
+ pass
+ raise Error, "couldn't move %s to %s by copy/delete: " \
+ "delete %s failed: %s" % (src, dst, src, msg)
+
+ return dst
+
+def write_file (filename, contents):
+ """Create a file with the specified name and write 'contents' (a
+ sequence of strings without line terminators) to it.
+ """
+ f = open(filename, "w")
+ for line in contents:
+ f.write(line + "\n")
+ f.close()
+
diff --git a/BuildManager/optionparser.py b/BuildManager/optionparser.py
new file mode 100644
index 0000000..ea1ae68
--- /dev/null
+++ b/BuildManager/optionparser.py
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+from BuildManager import Error
+import optparse
+
+__all__ = ["OptionParser"]
+
+class CapitalizeHelpFormatter(optparse.IndentedHelpFormatter):
+
+ def format_usage(self, usage):
+ return optparse.IndentedHelpFormatter \
+ .format_usage(self, usage).capitalize()
+
+ def format_heading(self, heading):
+ return optparse.IndentedHelpFormatter \
+ .format_heading(self, heading).capitalize()
+
+class OptionParser(optparse.OptionParser):
+
+ def __init__(self, usage=None, help=None, **kwargs):
+ if not "formatter" in kwargs:
+ kwargs["formatter"] = CapitalizeHelpFormatter()
+ optparse.OptionParser.__init__(self, usage, **kwargs)
+ self._overload_help = help
+
+ def format_help(self, formatter=None):
+ if self._overload_help:
+ return self._overload_help
+ else:
+ return optparse.OptionParser.format_help(self, formatter)
+
+ def error(self, msg):
+ raise Error, msg
+
+# vim:et:ts=4:sw=4
diff --git a/BuildManager/package.py b/BuildManager/package.py
new file mode 100644
index 0000000..ce73333
--- /dev/null
+++ b/BuildManager/package.py
@@ -0,0 +1,243 @@
+from BuildManager import *
+from BuildManager.rpmver import rpmVersionCompare
+from UserList import UserList
+import commands
+import string
+import os
+import re
+
+try:
+ import rpm
+except ImportError:
+ rpm = None
+
+__all__ = ["Package", "PackageList"]
+
+def subst(s, vars):
+ def _subst(match, vars=vars):
+ name = match.group(1)
+ return str(vars[name])
+ try:
+ return re.sub(r'\$([a-zA-Z]+)', _subst, s)
+ except KeyError, var:
+ raise Error, "variable $%s not declared" % var
+
+class Package:
+ def __init__(self, file, log=None):
+ self._package = None
+ ext = self._filename_extension(file)
+ if not ext:
+ raise Error, "unknown file extension of "+file
+ if not globals().has_key("_package_"+ext):
+ raise Error, "unknown package extension of "+file
+ self._package = globals()["_package_"+ext](file, log)
+
+ def __getattr__(self, name):
+ return getattr(self._package, name)
+
+ def _filename_extension(self, filename):
+ try:
+ dotpos = filename.rindex(".")
+ except ValueError:
+ pass
+ else:
+ return filename[dotpos+1:]
+
+class _package:
+ def __init__(self, file, log):
+ self.file = file
+ self.absfile = os.path.abspath(file)
+ self.type = None
+ self.name = None
+ self.version = None
+ self.release = None
+ self.epoch = None
+ self.spec = None
+ self.builddir = None
+ self.log = log or "$builddir/SPECS/log.$name"
+ self._init()
+
+ def __cmp__(self, pkg):
+ rc = cmp(self.name, pkg.name)
+ if rc: return rc
+ return rpmVersionCompare(self.epoch, self.version, self.release,
+ pkg.epoch, pkg.version, pkg.release)
+
+ def _expand_log(self):
+ substdict = {"builddir":self.builddir,
+ "name":self.name,
+ "version":self.version,
+ "release":self.release}
+ self.log = subst(self.log, substdict)
+
+class _package_spec(_package):
+ def _rpm_vars(self, str, vars):
+ end = -1
+ ret = []
+ while 1:
+ start = string.find(str, "%{", end+1)
+ if start == -1:
+ ret.append(str[end+1:])
+ break
+ ret.append(str[end+1:start])
+ end = string.find(str, "}", start)
+ if end == -1:
+ ret.append(str[start:])
+ break
+ varname = str[start+2:end]
+ if vars.has_key(varname):
+ ret.append(vars[varname])
+ else:
+ ret.append(str[start:end+1])
+ return string.join(ret,"")
+
+ def _init(self):
+ self.spec = self.absfile
+ self.builddir = os.path.dirname(os.path.dirname(self.absfile))
+ ret = os.system("mkdir -p %s/{SOURCES,SPECS,BUILD,SRPMS,RPMS,BUILDROOT}" % self.builddir)
+ try:
+ f = open(self.spec,"r")
+ except IOError, e:
+ raise BuildManagerFileError, \
+ "couldn't open spec file %s" % self.absfile
+ defines = {}
+ for line in f.readlines():
+ lowerline = string.lower(line)
+ if not self.name and lowerline[:5] == "name:":
+ self.name = self._rpm_vars(string.strip(line[5:]), defines)
+ elif not self.version and lowerline[:8] == "version:":
+ self.version = self._rpm_vars(string.strip(line[8:]), defines)
+ elif not self.release and lowerline[:8] == "release:":
+ self.release = self._rpm_vars(string.strip(line[8:]), defines)
+ elif lowerline[:7] == "%define":
+ token = string.split(line[7:])
+ if len(token) == 2:
+ defines[token[0]] = self._rpm_vars(token[1], defines)
+ if self.name and self.version and self.release:
+ break
+ else:
+ raise Error, "spec file %s doesn't define name, " \
+ "version or release" % self.file
+ self.type = "spec"
+ self._expand_log()
+
+class _package_rpm(_package):
+ def _init(self):
+ if not rpm:
+ cmd = "rpm -qp --queryformat='%%{NAME} %%{EPOCH} %%{VERSION} %%{RELEASE} %%{SOURCERPM}' %s"%self.file
+ status, output = commands.getstatusoutput(cmd)
+ if status != 0:
+ raise BuildManagerPackageError, \
+ "error querying rpm file %s" % self.file
+ else:
+ tokens = string.split(output, " ")
+ if len(tokens) != 5:
+ raise Error, \
+ "unexpected output querying rpm file %s: %s" % \
+ (self.file, output)
+ else:
+ self.name, self.epoch, self.version, self.release, srpm = tokens
+ if self.epoch == "(none)":
+ self.epoch = None
+ if srpm != "(none)":
+ self.type = "rpm"
+ else:
+ self.type = "srpm"
+ else:
+ # Boost up query if rpm module is available
+ file = open(self.file)
+ if hasattr(rpm, "headerFromPackage"):
+ h = rpm.headerFromPackage(file.fileno())[0]
+ else:
+ ts = rpm.TransactionSet()
+ h = ts.hdrFromFdno(file.fileno())
+ file.close()
+ self.name = h[rpm.RPMTAG_NAME]
+ self.epoch = h[rpm.RPMTAG_EPOCH]
+ self.version = h[rpm.RPMTAG_VERSION]
+ self.release = h[rpm.RPMTAG_RELEASE]
+ if h[rpm.RPMTAG_SOURCERPM]:
+ self.type = "rpm"
+ else:
+ self.type = "srpm"
+
+ def unpack(self, unpackdir):
+ if self.type == "srpm":
+ self.builddir = self._builddir_create(unpackdir)
+ if self.builddir:
+ self._expand_log()
+ return self._install_srpm()
+
+ def _builddir_create(self, unpackdir):
+ unpackdir = os.path.abspath(unpackdir)
+ builddir = "%s/%s-%s-%s-topdir" % (unpackdir, self.name, self.version, self.release)
+ ret = os.system("mkdir -p %s/{SOURCES,SPECS,BUILD,SRPMS,RPMS,BUILDROOT}" % builddir)
+ if ret != 0:
+ raise BuildManagerPackageError, \
+ "error creating builddir at %s" % builddir
+ else:
+ return builddir
+
+ def _install_srpm(self):
+ cmd = "rpm -i --define '_topdir %s' %s &> %s"%(self.builddir,self.file,self.log)
+ status, output = commands.getstatusoutput(cmd)
+ if status != 0:
+ raise Error, "error installing package "+self.file
+ else:
+ spec = self.builddir+"/SPECS/"+self.name+".spec"
+ if not os.path.isfile(spec):
+ listdir = os.listdir(self.builddir+"/SPECS")
+ for file in listdir[:]:
+ if file[-5:] != ".spec":
+ listdir.remove(file)
+ if len(listdir) != 1:
+ raise Error, "can't guess spec file for "+self.file
+ else:
+ self.spec = self.builddir+"/SPECS/"+listdir[0]
+ return 1
+ else:
+ self.spec = spec
+ return 1
+
+class PackageList(UserList):
+ def has_lt(self, pkg):
+ for mypkg in self.data:
+ if mypkg.name == pkg.name \
+ and mypkg.type == pkg.type \
+ and mypkg < pkg:
+ return 1
+ return 0
+
+ def has_le(self, pkg):
+ for mypkg in self.data:
+ if mypkg.name == pkg.name \
+ and mypkg.type == pkg.type \
+ and mypkg <= pkg:
+ return 1
+ return 0
+
+ def has_eq(self, pkg):
+ for mypkg in self.data:
+ if mypkg.name == pkg.name \
+ and mypkg.type == pkg.type \
+ and mypkg == pkg:
+ return 1
+ return 0
+
+ def has_ge(self, pkg):
+ for mypkg in self.data:
+ if mypkg.name == pkg.name \
+ and mypkg.type == pkg.type \
+ and mypkg >= pkg:
+ return 1
+ return 0
+
+ def has_gt(self, pkg):
+ for mypkg in self.data:
+ if mypkg.name == pkg.name \
+ and mypkg.type == pkg.type \
+ and mypkg > pkg:
+ return 1
+ return 0
+
+# vim:ts=4:sw=4:et
diff --git a/BuildManager/rpmver.py b/BuildManager/rpmver.py
new file mode 100644
index 0000000..cb3269c
--- /dev/null
+++ b/BuildManager/rpmver.py
@@ -0,0 +1,62 @@
+# compare alpha and numeric segments of two versions
+# return 1: first is newer than second
+# 0: first and second are the same version
+# -1: second is newer than first
+def rpmVersionCompare(e1, v1, r1, e2, v2, r2):
+ if e1 and not e2:
+ return 1
+ if not e1 and e2:
+ return -1
+ if e1 and e2:
+ if e1 < e2:
+ return -1
+ if e1 > e2:
+ return 1
+ rc = rpmvercmp(v1, v2)
+ if rc: return rc
+ return rpmvercmp(r1, r2)
+
+# compare alpha and numeric segments of two versions
+# return 1: a is newer than b
+# 0: a and b are the same version
+# -1: b is newer than a
+def rpmvercmp(a, b):
+ if a == b:
+ return 0
+ ai = 0
+ bi = 0
+ la = len(a)
+ lb = len(b)
+ while ai < la and bi < lb:
+ while ai < la and not a[ai].isalnum(): ai += 1
+ while bi < lb and not b[bi].isalnum(): bi += 1
+ aj = ai
+ bj = bi
+ if a[aj].isdigit():
+ while aj < la and a[aj].isdigit(): aj += 1
+ while bj < lb and b[bj].isdigit(): bj += 1
+ isnum = 1
+ else:
+ while aj < la and a[aj].isalpha(): aj += 1
+ while bj < lb and b[bj].isalpha(): bj += 1
+ isnum = 0
+ if aj == ai or bj == bi:
+ return -1
+ if isnum:
+ while ai < la and a[ai] == '0': ai += 1
+ while bi < lb and b[bi] == '0': bi += 1
+ if aj-ai > bj-bi: return 1
+ if bj-bi > aj-ai: return -1
+ rc = cmp(a[ai:aj], b[bi:bj])
+ if rc:
+ return rc
+ ai = aj
+ bi = bj
+ if ai == la and bi == lb:
+ return 0
+ if ai == la:
+ return -1
+ else:
+ return 1
+
+# vim:ts=4:sw=4