* dir'leri yeniden organize ediyoruz,

bir sonraki adim python package'i yapmak tam olarak
This commit is contained in:
Eray Özkural
2005-06-18 21:15:35 +00:00
parent a2ce40183f
commit b07d5c9776
25 changed files with 1350 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
# PISI package version
__version__ = "0.1"
+3
View File
@@ -0,0 +1,3 @@
# pisi.actionsapi version
__version__ = "0.1"
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
from pisi.context import Constants
const = Constants.c
def configure(parameters = None):
''' FIXME: Düzgün hale getirilecek '''
''' {EXTRA} = '--with-nls --with-libusb --with-something-usefull '''
# FIXME: I don't think its feasible to write all these parameters
# here. There should be a way to get all these... pisi.context.Constants?
configure_string = './configure --prefix=/usr \
--host=i686-pc-linux-gnu \
--mandir=/usr/share/man \
--infodir=/usr/share/info \
--datadir=/usr/share \
--sysconfdir=/etc \
--localstatedir=/var/lib {EXTRA}'
cmd = configure_string.replace('{EXTRA}', parameters)
os.system(cmd)
def make():
''' FIXME: Düzgün hale getirilecek '''
os.system('make')
def install():
''' FIXME: Düzgün hale getirilecek '''
''' {D} = /var/tmp/pisi/ _paket_adı_ /image/ '''
global const
install_string = 'make prefix={D}/usr \
datadir={D}/usr/share \
infodir={D}/usr/share/info \
localstatedir={D}/var/lib \
mandir={D}/usr/share/man \
sysconfdir={D}/etc \
install'
cmd = os.path.dirname(os.path.dirname(os.getcwd())) + \
const.install_dir_suffix
cmd = install_string.replace('{D}', cmd)
os.system(cmd)
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os, string, re, shutil
from shell import *
def gnuconfig_findnewest():
''' find the newest config.* file according to
timestamp and return it'''
locations = ['/usr/share/gnuconfig/config.sub',
'/usr/share/automake-1.8/config.sub',
'/usr/share/automake-1.7/config.sub',
'/usr/share/automake-1.6/config.sub',
'/usr/share/automake-1.5/config.sub',
'/usr/share/automake-1.4/config.sub']
newer_location = {}
for i in locations:
newer_location[i] = re.sub('\'',
'',
string.split((cat(i) |
tr(str.rstrip) |
grep ('^timestamp') |
join), '=')[1])
keys = newer_location.keys()
keys.sort()
map(newer_location.get, keys)
return os.path.dirname(newer_location.popitem()[0])
def gnuconfig_update():
''' copy newest config.* onto source's '''
newer_location = gnuconfig_findnewest()
try:
shutil.copyfile(newer_location + '/config.sub',
os.getcwd() + '/config.sub')
shutil.copyfile(newer_location + '/config.guess',
os.getcwd() + '/config.guess')
except IOError:
print 'Hata mata...'
sys.exit()
print 'GNU Config Update Finished...'
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
def libtoolize():
''' FIXME: Düzgün hale getirilecek '''
''' patch source with ltmain patches '''
# This is wrong! Action files should only operate on the build
# directory. And shouldn't depend on external files. If a patch is
# need to be applied it is PisiBuild's job to do it!
os.system('patch -sN < ' + Context.lib_dir() + '/portage-1.4.1.patch')
os.system('patch -sN < ' + Context.lib_dir() + '/sed-1.4.0.patch')
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys, re
from itertools import izip, imap, count, ifilter, ifilterfalse
def cat(filename):
return file(filename).xreadlines()
class grep:
"""keep only lines that match the regexp"""
def __init__(self,pat, flags = 0):
self.fun = re.compile(pat, flags).match
def __ror__(self, input):
return ifilter(self.fun, input)
class tr:
"""apply arbitrary transform to each sequence element"""
def __init__(self, transform):
self.tr = transform
def __ror__(self, input):
return imap(self.tr, input)
class printto:
"""print sequence elements one per line"""
def __init__(self, out = sys.stdout):
self.out = out
def __ror__(self,input):
for l in input:
print >> self.out, l
printlines = printto(sys.stdout)
class terminator:
def __init__(self,method):
self.process = method
def __ror__(self,input):
return self.process(input)
aslist = terminator(list)
asdict = terminator(dict)
astuple = terminator(tuple)
join = terminator("".join)
enum = terminator(enumerate)
class sort:
def __ror__(self,input):
ll = list(input)
ll.sort()
return ll
sort = sort()
class uniq:
def __ror__(self,input):
for i in input:
try:
if i == prev:
continue
except NameError:
pass
prev = i
yield i
uniq = uniq()
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
import time
from tempfile import mkstemp, mkdtemp
def sleep(sleep_time = 5):
time.sleep(sleep_time)
def createTmpFile():
handle, path = mkstemp()
return path
def createTmpDir():
path = mkdtemp()
return path
@@ -0,0 +1,74 @@
Note that if you update this patch, please update this one as well:
eclass/ELT-patches/portage/1.4.1
The file name can stay 1.4.1, as it will still apply to all versions. Only
when a new version of libtool comes out that it do not apply to, then the
name should be bumped, but the patch content should stay fairly the same.
--- ltmain.sh Wed Apr 3 01:19:37 2002
+++ ltmain.sh Sun May 26 19:50:52 2002
@@ -3940,9 +3940,50 @@
$echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2
exit 1
fi
- newdependency_libs="$newdependency_libs $libdir/$name"
+ # We do not want portage's install root ($D) present. Check only for
+ # this if the .la is being installed.
+ if test "$installed" = yes && test "$D"; then
+ eval mynewdependency_lib=`echo "$libdir/$name" |sed -e "s:$D:/:g" -e 's:/\+:/:g'`
+ else
+ mynewdependency_lib="$libdir/$name"
+ fi
+ # Do not add duplicates
+ if test "$mynewdependency_lib"; then
+ my_little_ninja_foo_1=`echo $newdependency_libs |$EGREP -e "$mynewdependency_lib"`
+ if test -z "$my_little_ninja_foo_1"; then
+ newdependency_libs="$newdependency_libs $mynewdependency_lib"
+ fi
+ fi
+ ;;
+ *)
+ if test "$installed" = yes; then
+ # Rather use S=WORKDIR if our version of portage supports it.
+ # This is because some ebuild (gcc) do not use $S as buildroot.
+ if test "$PWORKDIR"; then
+ S="$PWORKDIR"
+ fi
+ # We do not want portage's build root ($S) present.
+ my_little_ninja_foo_2=`echo $deplib |$EGREP -e "$S"`
+ if test -n "$my_little_ninja_foo_2" && test "$S"; then
+ mynewdependency_lib=""
+ # We do not want portage's install root ($D) present.
+ my_little_ninja_foo_3=`echo $deplib |$EGREP -e "$D"`
+ elif test -n "$my_little_ninja_foo_3" && test "$D"; then
+ eval mynewdependency_lib=`echo "$deplib" |sed -e "s:$D:/:g" -e 's:/\+:/:g'`
+ else
+ mynewdependency_lib="$deplib"
+ fi
+ else
+ mynewdependency_lib="$deplib"
+ fi
+ # Do not add duplicates
+ if test "$mynewdependency_lib"; then
+ my_little_ninja_foo_4=`echo $newdependency_libs |$EGREP -e "$mynewdependency_lib"`
+ if test -z "$my_little_ninja_foo_4"; then
+ newdependency_libs="$newdependency_libs $mynewdependency_lib"
+ fi
+ fi
;;
- *) newdependency_libs="$newdependency_libs $deplib" ;;
esac
done
dependency_libs="$newdependency_libs"
@@ -3975,6 +4005,10 @@
case $host,$output,$installed,$module,$dlname in
*cygwin*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;;
esac
+ # Do not add duplicates
+ if test "$installed" = yes && test "$D"; then
+ install_libdir=`echo "$install_libdir" |sed -e "s:$D:/:g" -e 's:/\+:/:g'`
+ fi
$echo > $output "\
# $outputname - a libtool library file
# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP
@@ -0,0 +1,14 @@
--- ltmain.sh 2003-02-13 14:54:24.000000000 +0100
+++ ltmain.sh 2003-02-13 15:24:49.000000000 +0100
@@ -48,6 +48,11 @@ EOF
exit 0
fi
+# define variables for historic ltconfig's generated by Libtool 1.3
+test -z "$SED" && SED=sed
+test -z "$EGREP" && EGREP=egrep
+test -z "$LTCC" && LTCC=${CC-gcc}
+
# The name of this program.
progname=`$echo "$0" | ${SED} 's%^.*/%%'`
modename="$progname"
+103
View File
@@ -0,0 +1,103 @@
# -*- coding: utf-8 -*-
# unpack magic
# maintainer baris and meren
#standart lisbrary modules
import os
import sys
import tarfile
import zipfile
#pisi modules
import util
class ArchiveBase(object):
def __init__(self, ctx):
self.type = ctx.spec.source.archiveType
self.fileName = os.path.basename(ctx.spec.source.archiveUri)
self.filePath = ctx.archives_dir() + '/' + self.fileName
def unpack(self, targetDir):
self.targetDir = targetDir
# first we check if we need to clean-up our working env.
if os.path.exists(self.targetDir):
util.clean_dir(self.targetDir)
else:
os.makedirs(self.targetDir)
class ArchiveTarFile(ArchiveBase):
def __init__(self, ctx):
super(ArchiveTarFile, self).__init__(ctx)
def unpack(self, targetDir):
super(ArchiveTarFile, self).unpack(targetDir)
rmode = ""
if self.type == 'tar':
rmode = 'r:'
elif self.type == 'targz':
rmode = 'r:gz'
elif self.type == 'tarbz2':
rmode = 'r:bz2'
tar = tarfile.open(self.filePath, rmode)
oldwd = os.getcwd()
os.chdir(self.targetDir)
for tarinfo in tar:
tar.extract(tarinfo)
os.chdir(oldwd)
tar.close()
class ArchiveZip(ArchiveBase):
def __init__(self, ctx):
super(ArchiveZip, self).__init__(ctx)
def unpack(self, targetDir):
super(ArchiveZip, self).unpack(targetDir)
zip = zipfile.ZipFile(self.filePath, 'r')
for file in zip.namelist():
ofile = self.targetDir + '/' + file
# a directory is present. lets continue
if os.path.isdir(ofile):
continue
# do we need to create parent directory for our file?
if not os.path.exists(os.path.dirname(ofile)):
os.mkdir(ofile)
continue
info = zip.getinfo(file)
# O.K. we know following line is dull. What we wanted to
# do was to compare the equality to 0xa0000000. But there
# is a known problem in Python regarding the hex/oct
# constants. Please see Guido's explanation at
# http://mail.python.org/pipermail/python-dev/2003-February/033029.html
if hex(info.external_attr)[2] == 'A':
target = zip.read(file)
os.symlink(target, ofile)
else:
buff = open (ofile, 'wb')
fileContent = zip.read(file)
buff.write(fileContent)
buff.close()
zip.close()
class Archive:
"""Unpack magic for Archive files..."""
def __init__(self, ctx):
"""accepted archive types:
targz, tarbz2, zip, tar"""
handlers = {
'targz': ArchiveTarFile,
'tarbz2': ArchiveTarFile,
'tar': ArchiveTarFile,
'zip': ArchiveZip
}
type = ctx.spec.source.archiveType
self.archive = handlers.get(type)(ctx)
def unpack(self, targetDir):
self.archive.unpack(targetDir)
+132
View File
@@ -0,0 +1,132 @@
# -*- coding: utf-8 -*-
# package bulding stuff
# maintainer: baris and meren
# python standard library
import os
from fetcher import Fetcher
from archive import Archive
# import pisipackage
import util
from ui import ui
class PisiBuildError(Exception):
pass
# FIXME: this eventually has to go to ui module
# Infact all ui calls has nothing to do with this build process.
# There more of them in PisiBuild...
# And maybe we should consider moving PisiBuild.build() back to pisi-build
# CLI too.
# exa: This, like all others, will have a GUI or CLI, interchangeably
def displayProgress(pd):
out = '\r%-30.30s %3d%% %12.2f %s' % \
(pd['filename'], pd['percent'], pd['rate'], pd['symbol'])
ui.info(out)
class PisiBuild:
"""PisiBuild class, provides the package build and creation routines"""
def __init__(self, context):
self.ctx = context
self.work_dir = self.ctx.pkg_work_dir()
self.spec = self.ctx.spec
def build(self):
ui.info("Building PISI source package: %s\n" % self.spec.source.name)
ui.info("Fetching source from: %s\n" % self.spec.source.archiveUri)
self.fetchArchive(displayProgress)
ui.info("Source archive is stored: %s/%s\n"
%(self.ctx.archives_dir(), self.spec.source.archiveName))
self.solveBuildDependencies()
ui.info("Unpacking archive...")
self.unpackArchive()
ui.info(" unpacked (%s)\n" % self.ctx.pkg_work_dir())
self.applyPatches()
try:
specdir = os.path.dirname(self.ctx.pspecfile)
self.actionScript = open("/".join([specdir,self.ctx.const.actions_file])).read()
except IOError, e:
ui.error ("Action Script: %s\n" % e)
return
# FIXME: It's wrong to assume that unpacked archive
# will create a name-version top-level directory.
# Archive module should give the exact location.
# (from the assumption is evil dept.)
os.chdir(self.ctx.pkg_work_dir() + "/" + self.spec.source.name + "-" + self.spec.source.version)
locals = globals = {}
try:
exec compile(self.actionScript , "error", "exec") in locals,globals
except SyntaxError, e:
ui.error ("Error : %s\n" % e)
return
self.configureSource(locals)
self.buildSource(locals)
self.installSource(locals)
# after all, we are ready to build/prepare the packages
self.buildPackages()
def fetchArchive(self, percentHook=None):
"""fetch an archive and store to ctx.archives_dir()
using fether.Fetcher"""
fetch = Fetcher(self.ctx)
# check if source already cached
destpath = fetch.filedest + "/" + fetch.filename
if os.access(destpath, os.R_OK):
if util.md5_file(destpath) == self.spec.source.archiveMD5:
ui.info('%s [cached]\n' % self.spec.source.archiveName)
return
if percentHook:
fetch.percentHook = percentHook
fetch.fetch()
# FIXME: What a ugly hack! We should really find a cleaner way for output.
if percentHook:
ui.info('\n')
def solveBuildDependencies(self):
pass
def unpackArchive(self):
archive = Archive(self.ctx)
archive.unpack(self.work_dir)
def applyPatches(self):
pass
def configureSource(self, locals):
func = self.ctx.const.setup_func
if func in locals:
ui.info("Configuring %s...\n" % self.spec.source.name)
locals[func]()
def buildSource(self, locals):
func = self.ctx.const.build_func
if func in locals:
ui.info("Building %s...\n" % self.spec.source.name)
locals[func]()
def installSource(self, locals):
func = self.ctx.const.install_func
if func in locals:
ui.info("Installing %s...\n" % self.spec.source.name)
locals[func]()
def buildPackages(self):
for package in self.spec.packages:
ui.info("** Building package %s\n" % package.name);
+48
View File
@@ -0,0 +1,48 @@
colors = {'black' : "\033[30m",
'red' : "\033[31m",
'green' : "\033[32m",
'yellow' : "\033[33m",
'blue' : "\033[34m",
'purple' : "\033[35m",
'cyan' : "\033[36m",
'white' : "\033[37m",
'brightblack' : "\033[01;30m",
'brightred' : "\033[01;31m",
'brightgreen' : "\033[01;32m",
'brightyellow' : "\033[01;33m",
'brightblue' : "\033[01;34m",
'brightmagenta' : "\033[01;35m",
'brightcyan' : "\033[01;36m",
'brightwhite' : "\033[01;37m",
'underlineblack' : "\033[04;30m",
'underlinered' : "\033[04;31m",
'underlinegreen' : "\033[04;32m",
'underlineyellow' : "\033[04;33m",
'underlineblue' : "\033[04;34m",
'underlinemagenta' : "\033[04;35m",
'underlinecyan' : "\033[04;36m",
'underlinewhite' : "\033[04;37m",
'blinkingblack' : "\033[05;30m",
'blinkingred' : "\033[05;31m",
'blinkinggreen' : "\033[05;32m",
'blinkingyellow' : "\033[05;33m",
'blinkingblue' : "\033[05;34m",
'blinkingmagenta' : "\033[05;35m",
'blinkingcyan' : "\033[05;36m",
'blinkingwhite' : "\033[05;37m",
'backgroundblack' : "\033[07;30m",
'backgroundred' : "\033[07;31m",
'backgroundgreen' : "\033[07;32m",
'backgroundyellow' : "\033[07;33m",
'backgroundblue' : "\033[07;34m",
'backgroundmagenta' : "\033[07;35m",
'backgroundcyan' : "\033[07;36m",
'backgroundwhite' : "\033[07;37m",
'default' : "\033[0m" }
def colorize(msg, color):
if colors.has_key(color):
return colors[color] + msg + colors['default']
else:
return msg
+104
View File
@@ -0,0 +1,104 @@
# -*- coding: utf-8 -*-
# PISI configuration (static and dynamic)
from specfile import SpecFile
import oo
class Constants:
"Pisi constants"
c = oo.const()
def __init__(self):
self.c.lib_dir_suffix = "/var/lib/pisi"
self.c.db_dir_suffix = "/var/db/pisi"
self.c.archives_dir_suffix = "/var/cache/pisi/archives"
self.c.tmp_dir_suffix = "/var/tmp/pisi"
# directory suffixes for build
self.c.work_dir_suffix = "/work"
self.c.install_dir_suffix = "/install"
# file/directory names
self.c.actions_file = "actions.py"
self.c.files_dir = "files"
# functions in actions_file
self.c.setup_func = "setup"
self.c.build_func = "build"
self.c.install_func = "install"
def __getattr__(self, attr):
return getattr(self.c, attr)
def __setattr__(self, attr, value):
return setattr(self.c, attr, value)
def __delattr__(self, attr):
return delattr(self.c, attr)
class Context(object):
"""Config/Context Singleton"""
class __impl:
def __init__(self):
self.const = Constants()
# self.c.destdir = '' # install default to root by default
self.destdir = './tmp' # only for ALPHA
# the idea is that destdir can be set with --destdir=...
def setSpecFile(self, pspecfile):
self.pspecfile = pspecfile
spec = SpecFile()
spec.read(pspecfile)
spec.verify() # check pspec integrity
self.spec = spec
# directory accessor functions
# here is how it goes
# x_dir: system wide directory for storing info type x
# pkg_x_dir: per package directory for storing info type x
def lib_dir(self):
return self.destdir + self.const.lib_dir_suffix
def db_dir(self):
return self.destdir + self.const.db_dir_suffix
def archives_dir(self):
return self.destdir + self.const.archives_dir_suffix
def tmp_dir(self):
return self.destdir + self.const.tmp_dir_suffix
def pkg_work_dir(self):
packageDir = self.spec.source.name + '-' \
+ self.spec.source.version + '-' + self.spec.source.release
return self.destdir + self.const.tmp_dir_suffix \
+ '/' + packageDir + self.const.work_dir_suffix
def install_dir(self):
return self.tmp_dir() + self.const.install_dir_suffix
def pkg_install_dir(self):
packageDir = self.spec.source.name + '-' \
+ self.spec.source.version + '-' + self.spec.source.release
return self.destdir + self.const.tmp_dir_suffix \
+ '/' + packageDir + self.const.install_dir_suffix
__instance = __impl()
def __init__(self, pspecfile = None):
if pspecfile != None:
self.__instance.setSpecFile(pspecfile)
def __getattr__(self, attr):
return getattr(self.__instance, attr)
def __setattr__(self, attr, value):
return setattr(self.__instance, attr, value)
# create a default context WITH NO PSPEC
ctx = Context()
+3
View File
@@ -0,0 +1,3 @@
# dependency analyzer
# maintainer: eray and caglar
+139
View File
@@ -0,0 +1,139 @@
# -*- coding: utf-8 -*-
# download magic
# maintainer: baris and meren
# python standard library modules
import urlparse
import urllib2
import os
# pisi modules
import util
class FetchError (Exception):
pass
class Fetcher:
"""Yet another Pisi tool for fetching files from various sources.."""
def __init__(self, ctx):
self.uri = ctx.spec.source.archiveUri
self.filedest = ctx.archives_dir()
util.check_dir(self.filedest)
self.scheme = "file"
self.netloc = ""
self.filepath = ""
self.filename = ""
self.percent = 0
self.rate = 0.0
self.percentHook = None
from string import split
u = urlparse.urlparse(self.uri)
self.scheme, self.netloc, self.filepath = u[0], u[1], u[2]
self.filename = os.path.basename(self.uri)
def fetch (self):
"""Return value: Fetched file's full path.."""
if self.filename == "":
self.err("filename error")
if os.access(self.filedest, os.W_OK) == False:
self.err("no perm to write to dest dir")
scheme_err = lambda: self.err("unexpected scheme")
handlers = {
'file': self.fetchLocalFile,
'http': self.fetchRemoteFile,
'ftp' : self.fetchRemoteFile
}; handlers.get(self.scheme, scheme_err)()
return self.filedest + "/" + self.filename
def doGrab(self, file, dest, totalsize):
symbols = [' B/s', 'KB/s', 'MB/s', 'GB/s']
from time import time
tt, oldsize = int(time()), 0
p = Progress(totalsize)
bs, size = 1024, 0
symbol, depth = "B/s", 0
st = time()
chunk = file.read(bs)
size = size + len(chunk)
self.percent = p.update(size)
while chunk:
dest.write(chunk)
chunk = file.read(bs)
size = size + len(chunk)
ct = time()
if int(tt) != int(ct):
self.rate = size / (ct - st)
while self.rate > 1000 and depth < 3:
self.rate /= 1024
depth += 1
symbol, depth = symbols[depth], 0
oldsize, tt = size, time()
if p.update(size):
self.percent = p.percent
if self.percentHook != None:
retval = {'filename': self.filename,
'percent' : self.percent,
'rate': self.rate,
'symbol': symbol}
self.percentHook(retval)
dest.close()
def fetchLocalFile (self):
from shutil import copyfile
if os.access(self.filepath, os.F_OK) == False:
self.err("no such file or no perm to read")
dest = open(self.filedest + "/" + self.filename , "w")
totalsize = os.path.getsize(self.filepath)
file = open(self.filepath)
self.doGrab(file, dest, totalsize)
def fetchRemoteFile (self):
from httplib import HTTPException
try:
file = urllib2.urlopen(self.uri)
headers = file.info()
except ValueError, e:
self.err('%s' % (e, ))
except IOError, e:
self.err('%s' % (e, ))
except OSError, e:
self.err('%s' % (e, ))
except HTTPException, e:
self.err(('(%s): %s') % (e.__class__.__name__, e))
if not headers is None and not headers.has_key('Content-Length'):
self.err('file not found')
else: totalsize = int(headers['Content-Length'])
dest = open(self.filedest + "/" + self.filename , "w")
self.doGrab(file, dest, totalsize)
def err (self, error):
raise FetchError(error)
class Progress:
def __init__(self, totalsize):
self.totalsize = totalsize
self.percent = 0
def update(self, size):
percent = (size * 100) / self.totalsize
if percent and self.percent is not percent:
self.percent = percent
return percent
else:
return 0
+46
View File
@@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
#import package
from specfile import *
from package import Package
import util
from context import ctx
from ui import ui
import installdb
import packagedb
import dependency
#import conflicts
class InstallError(Exception):
pass
def install_package_file(package_fn):
package = Package(package_fn, 'r')
# extract control files
util.clean_dir(ctx.install_dir())
ui.info('extracting files\n')
package.extract_PISI_files(ctx.install_dir())
# verify package
# check if we have all required files
metadata = MetaData()
metadata.read(ctx.install_dir() + '/metadata.xml')
# check package semantics
if not metadata.verify():
raise InstallError("MetaData format wrong")
# check file system requirements
# what to do if / is split into /usr, /var, etc.?
# check conflicts
# check dependencies
# unzip package in place
# update databases
# installdb
installdb.install(spec.spec.install_dir() + '/files.xml')
+52
View File
@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
# installation database
# maintainer: eray and caglar
import os
import bsddb.dbshelve as shelve
from context import ctx
import util
util.check_dir(ctx.db_dir())
d = shelve.open(ctx.db_dir() + '/install.bdb')
files_dir = ctx.archives_dir() + "/files"
class InstallDBError(Exception):
pass
def files_name(name, version, release):
return files_dir + '/' + name + '-' + version + '-' + release
def files(n, v, r):
return file(files_name(n,v,r))
def is_recorded(name, version, release):
key = name + version + release
return d.has_key(key)
def is_installed(name, version, release):
key = name + version + release
return is_recorded(name,version,release) and d[key]=='i'
def is_removed(name, version, release):
key = name + version + release
return is_recorded(name,version,release) and d[key]=='r'
def install(name, version, release, files_xml):
key = name + version + release
if is_installed(name, version, release):
raise InstallDBError("already installed")
d[key] = 'i'
util.copy_file(files_xml, files_name(name, version, release))
def remove(name, version, release):
key = name + version + release
d[key] = 'r'
def purge(name, version, release):
os.unlink(files_name(name, version, release))
key = name + version + release
del d[key]
+20
View File
@@ -0,0 +1,20 @@
# OO extensions
# thes are really cool, you can't do this in C++ :)
class const:
"Constant members implementation"
class ConstError(TypeError):
pass
def __setattr__(self, name, value):
if self.__dict__.has_key(name):
raise self.ConstError, "Can't rebind constant: %s" % name
# Binding an attribute once to a const is available
self.__dict__[name] = value
def __delattr__(self, name):
if self.__dict__.has_key(name):
raise self.ConstError, "Can't unbind constant: %s" % name
# we don't have an attribute by this name
raise NameError, name
+30
View File
@@ -0,0 +1,30 @@
# package abstraction
# provides methods to add/remove files, extract control files
# maintainer: baris and meren
class Package:
"""Package: PISI package class"""
def __init__(self, packagefn, mode):
self.filename = packagefn
self.mode = mode # bu gerekli mi?
# etc. etc.
def add_file(self, fn):
"""add a file to package"""
def extract(self, outdir):
"""extract entire package contents to directory"""
extract_dir('', outdir) # means package root
def extract_file(self, path, outdir):
"""extract file with path to outdir"""
def extract_dir(self, dir, outdir):
"""extract directory recursively"""
def extract_PISI_files(self, outdir):
"""extract PISI control files: metadata.xml, files.xml,
action scripts, etc."""
self.extract_file('metadata.xml', outdir)
self.extract_file('files.xml', outdir)
self.extract_dir('Config', outdir)
+28
View File
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# package database
# interface for update/query to local package repository
# maintainer: eray and caglar
# we basically store everything in PackageInfo class
# yes, we are cheap
import bsddb.dbshelve as shelve
import util
from context import ctx
util.check_dir(ctx.db_dir())
d = shelve.open(ctx.db_dir() + '/package.bdb')
def has_package(name):
return d.has_key(name)
def get_package(name):
return d[name]
def add_package(name, package_info):
d[name] = package_info
def remove_package(name):
del d[name]
+18
View File
@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
# package source database
# interface for update/query to local package repository
# maintainer: eray and caglar
# we basically store everything in sourceinfo class
# yes, we are cheap
import bsddb.dbshelve as shelve
util.check_dir(config.db_dir())
d = shelve.open(config.db_dir() + '/source.bdb')
def add_source(name, source_info):
d[name] = source_info
def remove_source(name):
del d[name]
+105
View File
@@ -0,0 +1,105 @@
# -*- coding: utf-8 -*-
# read/write PISI source package specification file
import xml.dom.minidom
from xmlfile import *
from os.path import basename
class PatchInfo:
def __init__(self, filenm, ctype):
self.filename = filenm
self.compressionType = ctype
def __init__(self, node):
self.filename = getNodeText(node)
self.compressionType = getNodeAttribute(node, "compressionType")
class DepInfo:
def __init__(self, node):
self.package = getNodeText(node).strip()
self.versionFrom = getNodeAttribute(node, "versionFrom")
class HistoryInfo:
def __init__(self, node):
self.date = getNodeText(getNode(node, "Date"))
self.version = getNodeText(getNode(node, "Version"))
self.release = getNodeText(getNode(node, "Release"))
class PathInfo:
def __init__(self, node):
self.pathname = getNodeText(node)
self.fileType = getNodeAttribute(node, "fileType")
# a structure to hold source information
class SourceInfo:
pass
class PackageInfo:
def __init__(self, node):
self.name = getNodeText(getNode(node, "Name"))
self.summary = getNodeText(getNode(node, "Summary"))
self.description = getNodeText(getNode(node, "Description"))
self.category = getNodeText(getNode(node, "Category"))
iDepElts = getAllNodes(node, "InstallDependencies")
self.installDeps = [DepInfo(x) for x in iDepElts]
rtDepElts = getAllNodes(node, "RuntimeDependencies")
self.runtimeDeps = [DepInfo(x) for x in rtDepElts]
self.paths = [PathInfo(x) for x in getAllNodes(node, "Files/Path")]
class SpecFile(XmlFile):
"""A class for reading/writing from/to a PSPEC (PISI SPEC) file."""
def __init__(self):
XmlFile.__init__(self,"PSPEC")
def read(self, filename):
"""Read PSPEC file"""
self.readxml(filename)
self.source = SourceInfo()
self.source.name = self.getChildText("Source/Name")
archiveNode = self.getNode("Source/Archive")
self.source.archiveUri = getNodeText(archiveNode).strip()
self.source.archiveName = basename(self.source.archiveUri)
self.source.archiveType = getNodeAttribute(archiveNode, "archType")
self.source.archiveMD5 = getNodeAttribute(archiveNode, "md5sum")
patchElts = self.getChildElts("Source/Patches")
if patchElts:
self.source.patches = [PatchInfo(p) for p in patchElts]
buildDepElts = self.getChildElts("Source/BuildDependencies")
if buildDepElts:
self.source.buildDeps = [DepInfo(d) for d in buildDepElts]
historyElts = self.getAllNodes("History/Update")
self.source.history = [HistoryInfo(x) for x in historyElts]
# As we have no Source/Version tag we need to get
# the last version and release information
# from the first child of History/Update. And it works :)
self.source.version = self.source.history[0].version
self.source.release = self.source.history[0].release
# find all binary packages
packageElts = self.getAllNodes("Package")
self.packages = [PackageInfo(p) for p in packageElts]
def verify(self):
"""Verify PSPEC structures, are they what we want of them?"""
return True
def write(self, filename):
"""Write PSPEC file"""
self.writexml(filename)
class MetaData(SpecFile):
"""This is a superset of the source spec definition"""
def read(self, filename):
SpecFile.read(filename)
distribution = self.getNodeText("Source/Distribution")
distributionRelease = self.getNodeText("Source/DistributionRelease")
architecture = self.getNodeText("Source/Architecture")
installSize = self.getNodeText("Source/InstallSize")
+30
View File
@@ -0,0 +1,30 @@
# generic user interface
import sys
from colors import colorize
def register(_impl):
""" Register a UI implementation"""
ui = _impl
# default UI implementation
class CLI:
def __init__(self, debuggy = True):
self.showDebug = debuggy
def info(self, msg):
sys.stdout.write(colorize(msg, 'blue'))
sys.stdout.flush()
def debug(self, msg):
if showDebug:
sys.stdout.write(msg)
sys.stdout.flush()
def error(self,msg):
sys.stdout.write(colorize(msg, 'red'))
sys.stdout.flush()
# default UI is CLI
ui = CLI()
+69
View File
@@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
# misc. utility functions, including process and file utils
# maintainer: eray and caglar and baris and meren!
import os
import sys
import md5
from ui import ui
class FileError(Exception):
pass
class UtilError(Exception):
pass
# shorthand to check if a file exists
def check_file(file, mode = os.F_OK):
if not os.access(file, mode):
raise FileError("File " + file + " not found")
# check if directory exists, and create if it doesn't
# works recursively
# FIXME: could have a better name
def check_dir(dir):
dir = dir.strip().rstrip("/")
if not os.access(dir, os.F_OK):
os.makedirs(dir)
def clean_dir(top):
"""Remove all content of a directory (top)"""
for root, dirs, files in os.walk(top, topdown=False):
for name in files:
os.remove(os.path.join(root, name))
for name in dirs:
os.rmdir(os.path.join(root, name))
def copy_file(s,d):
check_file(s)
check_dir(os.path.dirname(d))
fs = file(s, 'rb')
fd = file(d, 'wb')
for l in fs:
fd.write(l)
def copy_dir():
raise UtilError("not implemented")
def md5_file(filename):
m = md5.new()
f = file(filename, 'rb')
for l in f:
m.update(l)
return m.hexdigest()
# run a command non-interactively
def run_batch(cmd):
ui.info('running ' + cmd)
a = os.popen(cmd)
lines = a.readlines()
ret = a.close()
ui.debug('return value ' + ret)
successful = ret == None
if not successful:
ui.error('ERROR: executing command: ' + cmd + '\n' + strlist(lines))
return (successful,lines)
# print a list
def strlist(l):
return string.join(map(lambda x: str(x) + ' ', l))
+141
View File
@@ -0,0 +1,141 @@
# -*- coding: utf-8 -*-
# some helper functions for using minidom
import xml.dom.minidom as mdom
class XmlError(Exception):
pass
# static functions
def getNodeAttribute(node, attrname):
for i in range(node.attributes.length):
attr = node.attributes.item(i)
if attr.name == attrname:
return attr.childNodes[0].data
def getNodeText(node):
# get the first child
try:
child = node.childNodes[0]
except IndexError:
return None
except AttributeError: # no node by that name
return None
if child.nodeType == child.TEXT_NODE:
return child.data
else:
raise XmlError("getNodeText: Expected text node, got something else!")
def getChildText(node_s, tagpath):
node = getNode(node_s, tagpath)
if not node:
return None
return getNodeText(node)
def getChildElts(node):
"""get only child elements"""
return filter(lambda x:x.nodeType == x.ELEMENT_NODE, node.childNodes)
def getNode(node, tagpath):
"""returns the *first* matching node for given tag path."""
tags = tagpath.split('/')
# iterative code to search for the path
# get DOM for top node
nodeList = node.getElementsByTagName(tags[0])
if len(nodeList) == 0:
return None # not found
node = nodeList[0] # discard other matches
for tag in tags[1:]:
nodeList = node.getElementsByTagName(tag)
if len(nodeList) == 0:
return None
else:
node = nodeList[0]
return node
def getAllNodes(node, tags):
"""retrieve all nodes that match a given tag path."""
if len(tags) == 0:
return []
nodeList = node.getElementsByTagName(tags[0])
if len(nodeList) == 0:
return []
for tag in tags[1:]:
results = map(lambda x: x.getElementsByTagName(tag),nodeList)
nodeList = []
for x in results:
nodeList.extend(x)
pass # emacs indentation error, keep it here
if len(nodeList) == 0:
return []
return nodeList
# xmlfile class that further abstracts a dom object
class XmlFile(object):
"""A class for retrieving information from an XML file"""
def __init__(self, rootTag):
self.rootTag = rootTag
def readxml(self, fileName):
self.dom = mdom.parse(fileName)
def writexml(self, fileName):
f = file(fileName,'w')
self.dom.writexml(f)
def verifyRootTag(self):
if self.dom.documentElement.tagName != self.rootTag:
raise XmlError("Root tagname not " + self.rootTag + " as expected")
def getNode(self, tagPath):
"""returns the *first* matching node for given tag path."""
self.verifyRootTag()
return getNode(self.dom.documentElement, tagPath)
def getAllNodes(self, tagPath):
"""returns all nodes matching a given tag path."""
self.verifyRootTag()
tags = tagPath.split('/')
return getAllNodes(self.dom.documentElement, tags)
def getChildren(self, tagpath):
""" returns the children of the given path"""
node = self.getNode(tagpath)
return node.childNodes
# get only elements of a given type
# BUG: this doesn't work
def getChildrenWithType(self, tagpath, type):
""" returns the children of the given path, only with given type """
node = self.getNode(tagpath)
return filter(lambda x:x.nodeType == type, node.childNodes)
# get only child elements
def getChildElts(self, tagpath):
""" returns the children of the given path, only with given type """
node = self.getNode(tagpath)
try:
return filter(lambda x:x.nodeType == x.ELEMENT_NODE, node.childNodes)
except AttributeError:
return None
def getChildText(self, tagpath):
node = self.getNode(tagpath)
if not node:
return None
return getNodeText(node)