#!/usr/bin/python
+
import os
+import shutil
+import smtplib
import urlgrabber
import urlgrabber.progress
import urllib
import chroot
import util
+from exception import *
from constants import *
__cache = {
"group_names" : None,
}
-def find_package_info(name, toolchain=False):
+# Python 2.4 does not have that email module, so
+# we disable the mail function here.
+try:
+ import email.mime.multipart
+ import email.mime.text
+ have_email = 1
+except ImportError:
+ have_email = 0
+
+try:
+ import hashlib
+ have_hashlib = 1
+except ImportError:
+ import sha
+ have_hashlib = 0
+
+def find_package_info(name, toolchain=False, **kwargs):
for repo in get_repositories(toolchain):
if not os.path.exists(os.path.join(repo.path, name, name + ".nm")):
continue
- return PackageInfo(name, repo=repo)
+ return PackageInfo(name, repo=repo, **kwargs)
-def find_package(name, toolchain=False):
+def find_package(name, naoki, toolchain=False):
package = find_package_info(name, toolchain)
if package:
- package = backend.Package(package)
+ return package.getPackage(naoki)
- return package
+ return None
-def parse_package_info(names, toolchain=False):
+def parse_package_info(names, toolchain=False, **kwargs):
packages = []
for name in names:
- package = find_package_info(name, toolchain)
+ package = find_package_info(name, toolchain, **kwargs)
if package:
packages.append(package)
ret.extend(l1)
return ret
+def calc_hash(data):
+ if have_hashlib:
+ obj = hashlib.sha1(data)
+ else:
+ obj = sha.new(data)
+
+ return obj.hexdigest()
+
def download(files, logger=None):
for file in files:
filepath = os.path.join(TARBALLDIR, file)
try:
gobj = g.urlopen(url)
except urlgrabber.grabber.URLGrabError, e:
- logger.error("Could not retrieve %s - %s" % (url, e))
+ if logger:
+ logger.error("Could not retrieve %s - %s" % (url, e))
raise
- # XXX Need to check SHA1 sum here
+ data = gobj.read()
+ gobj.close()
+
+ if gobj.hdr.has_key("X-Hash-Sha1"):
+ hash_server = gobj.hdr["X-Hash-Sha1"]
+ msg = "Comparing hashes - %s" % hash_server
+
+ hash_calculated = calc_hash(data)
+ if hash_calculated == hash_server:
+ if logger:
+ logger.debug(msg + " - OK")
+ else:
+ if logger:
+ logger.error(msg + " - ERROR")
+ raise DownloadError, "Hash sum of downloaded file does not match"
fobj = open(filepath, "w")
- fobj.write(gobj.read())
+ fobj.write(data)
fobj.close()
- gobj.close()
+
class PackageInfo(object):
__data = {}
- def __init__(self, name, repo=None):
+ def __init__(self, name, repo=None, arch=arches.current["name"]):
self._name = name
self.repo = repo
- self.arch = arches.current["name"]
+ self.arch = arch
- #def __cmp__(self, other):
- # return cmp(self.name, other.name)
+ def __cmp__(self, other):
+ return cmp(self.name, other.name)
def __repr__(self):
return "<PackageInfo %s>" % self.name
"fingerprint" : self.fingerprint,
"files" : self.package_files,
"group" : self.group,
+ "id" : self.id,
"license" : self.license,
"maintainer" : self.maintainer,
"name" : self.name,
def _dependencies(self, s, recursive=False, toolchain=False):
c = s + "_CACHE"
if not self._data.has_key(c):
- deps = parse_package(self._data.get(s).split(" "), toolchain=toolchain)
+ deps = parse_package_info(self._data.get(s).split(" "), toolchain=toolchain)
self._data.update({c : depsolve(deps, recursive)})
return self._data.get(c)
@property
def fingerprint(self):
- return "%d" % os.stat(self.filename).st_mtime
+ return "%d" % self.last_change
@property
def group(self):
def id(self):
return "%s-%s-%s" % (self.name, self.version, self.release)
+ @property
+ def last_build(self):
+ file = os.path.join(PACKAGESDIR, self.package_files[0])
+ if not os.path.exists(file):
+ return 0
+
+ return os.stat(file).st_mtime
+
+ @property
+ def last_change(self):
+ return os.stat(self.filename).st_mtime
+
@property
def license(self):
return self._data.get("PKG_LICENSE")
class Package(object):
def __init__(self, name, naoki, toolchain=False):
self.info = find_package_info(name, toolchain)
+
+ assert naoki
self.naoki = naoki
#self.log.debug("Initialized package object %s" % name)
def __getattr__(self, attr):
return getattr(self.info, attr)
+ @property
+ def name(self):
+ return self.info.name
+
def build(self):
- environment = chroot.Environment(self)
+ environment = chroot.PackageEnvironment(self)
environment.build()
def download(self):
download(self.info.objects, logger=self.log)
def extract(self, dest):
- files = [os.path.join(PACKAGESDIR, file) for file in self.package_files]
+ files = [os.path.join(PACKAGESDIR, file) for file in self.info.package_files]
if not files:
return
util.do("%s --root=%s %s" % (os.path.join(TOOLSDIR, "decompressor"),
dest, " ".join(files)), shell=True)
+ def getEnvironment(self, *args, **kwargs):
+ return chroot.PackageEnvironment(self, *args, **kwargs)
+
@property
def log(self):
- return self.naoki.logging.getBuildLogger(self.info.id)
+ return self.naoki.logging.getBuildLogger(os.path.join(self.repo.name, self.info.id))
def get_repositories(toolchain=False):
def path(self):
return os.path.join(PKGSDIR, self.name)
-if __name__ == "__main__":
- pi = PackageInfo("core/grub")
- print pi.dependencies
+class BinaryRepository(object):
+ DIRS = ("db", "packages")
+
+ def __init__(self, name, naoki=None, arch=None):
+ self.name = name
+ self.arch = arch or arches.current
+ self.repo = Repository(self.name)
+
+ assert naoki
+ self.naoki = naoki
+
+ def build(self):
+ if not self.buildable:
+ raise Exception, "Cannot build repository"
+
+ # Create temporary directory layout
+ util.rm(self.repopath("tmp"))
+ for dir in self.DIRS:
+ util.mkdir(self.repopath("tmp", dir))
+
+ # Copy packages
+ for package in self.packages:
+ for file in package.package_files:
+ shutil.copy(os.path.join(PACKAGESDIR, file),
+ self.repopath("tmp", "packages"))
+
+ # TODO check repository's sanity
+ # TODO create repoview
+ f = open(self.repopath("tmp", "db", "package-list.txt"), "w")
+ for package in self.packages:
+ s = "%-40s" % package.fmtstr("%(name)s-%(version)s-%(release)s")
+ s += " | %s\n" % package.summary
+ f.write(s)
+ f.close()
+
+ for dir in self.DIRS:
+ util.rm(self.repopath(dir))
+ shutil.move(self.repopath("tmp", dir), self.repopath(dir))
+ util.rm(self.repopath("tmp"))
+
+ def clean(self):
+ if os.path.exists(self.path):
+ self.log.debug("Cleaning up repository: %s" % self.path)
+ util.rm(self.path)
+
+ def repopath(self, *args):
+ return os.path.join(self.path, *args)
+
+ @property
+ def buildable(self):
+ for package in self.packages:
+ if package.built:
+ continue
+ return False
+
+ return True
+
+ @property
+ def log(self):
+ return self.naoki.log
+
+ @property
+ def packages(self):
+ packages = []
+ for package in parse_package_info(get_package_names(), arch=self.arch["name"]):
+ if not package.repo.name == self.name:
+ continue
+ packages.append(package)
+ return packages
+
+ @property
+ def path(self):
+ return os.path.join(REPOSDIR, self.name, self.arch["name"])
+
+def report_error_by_mail(package):
+ log = package.naoki.log
+
+ # Do not send a report if no recipient is configured
+ if not config["error_report_recipient"]:
+ return
+
+ if not have_email:
+ log.error("Can't send mail because this python version does not support this")
+ return
+
+ try:
+ connection = smtplib.SMTP(config["smtp_server"])
+ #connection.set_debuglevel(1)
+
+ if config["smtp_user"] and config["smtp_password"]:
+ connection.login(config["smtp_user"], config["smtp_password"])
+
+ except SMTPConnectError, e:
+ log.error("Could not establish a connection to the smtp server: %s" % e)
+ return
+ except SMTPAuthenticationError, e:
+ log.error("Could not successfully login to the smtp server: %s" % e)
+ return
+
+ msg = email.mime.multipart.MIMEMultipart()
+ msg["From"] = config["error_report_sender"]
+ msg["To"] = config["error_report_recipient"]
+ msg["Subject"] = config["error_report_subject"] % package.all
+ msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
+
+ body = """\
+The package %(name)s had a difficulty to build itself.
+This email will give you a short report about the error.
+
+Package information:
+ Name : %(name)s - %(summary)s
+ Version : %(version)s
+ Release : %(release)s
+
+ This package in maintained by %(maintainer)s.
+
+
+A detailed logfile is attached.
+
+Sincerely,
+ Naoki
+ """ % package.all
+
+ msg.attach(email.mime.text.MIMEText(body))
+
+ # Read log and append it to mail
+ logfile = os.path.join(LOGDIR, package.id + ".log")
+ if os.path.exists(logfile):
+ log = []
+ f = open(logfile)
+ line = f.readline()
+ while line:
+ line = line.rstrip("\n")
+ if line.endswith(LOG_MARKER):
+ # Reset log
+ log = []
+
+ log.append(line)
+ line = f.readline()
+
+ f.close()
+
+ log = email.mime.text.MIMEText("\n".join(log), _subtype="plain")
+ log.add_header('Content-Disposition', 'attachment',
+ filename="%s.log" % package.id)
+ msg.attach(log)
+
+ try:
+ connection.sendmail(config["error_report_sender"],
+ config["error_report_recipient"], msg.as_string())
+ except Exception, e:
+ log.error("Could not send error report: %s: %s" % (e.__class__.__name__, e))
+ return
+
+ connection.quit()