view gub/commands.py @ 6396:7419e5bfccdb

Fix ghostscript dependency libtiff
author Masamichi Hosoda <trueroad@users.noreply.github.com>
date Sun, 22 Mar 2015 00:33:00 +0900
parents 0d0b40443a74
children
line wrap: on
line source

import glob
import inspect
import os
import pickle
import re
import sys
import traceback
#
from gub import loggedos
from gub import misc
from gub import octal

class SerializedCommand:
    def __init__ (self):
        self.instantiation_traceback = traceback.extract_stack ()
        self.is_checksummed = True
        
    def execute (self, logger):
        raise NotImplemented
    
    def get_source (self):
        return ''.join (traceback.format_list (self.instantiation_traceback))

    def checksum (self, hasher):
        hasher (self.__class__.__name__)

class Nop (SerializedCommand):
    def __init__ (self):
        self.is_checksummed = False
    def execute (self):
        pass
    def checksum (self, hasher):
        pass

class UpdateSourceDir (SerializedCommand):
    def __init__ (self, buildspec):
        self.buildspec = buildspec

    def execute (self, logger):
        buildspec = self.buildspec 
        if not buildspec.source:
            return False
        if not buildspec.source.is_tracking ():
            command = buildspec.expand ('rm -rf %(srcdir)s %(builddir)s %(install_root)s')
            loggedos.system (logger, command)

        if buildspec.source:
            buildspec.source.update_workdir (buildspec.expand ('%(srcdir)s'))

        # TODO: move this to Repository
        if (os.path.isdir (buildspec.expand ('%(srcdir)s'))):
            cmd = buildspec.expand ('chmod -R +w %(srcdir)s')
            loggedos.system (logger, cmd, ignore_errors=True)
           
    def checksum (self, hasher):
        tracking = 'not tracking'
        if self.buildspec.source.is_tracking ():
            tracking  = 'tracking'
        hasher ('UpdateSourceDir(%(tracking)s)' % locals ())
        
class System (SerializedCommand):
    def __init__ (self, c, env={}, ignore_errors=False):
        SerializedCommand.__init__ (self)
        self.command = c
        self.env = env
        self.ignore_errors = ignore_errors
    def __repr__ (self):
        return 'System (%s)' % repr (self.command)
    def checksum (self, hasher):
        hasher (self.command)
        # TODO: use env too.
    def execute (self, logger):
#        return loggedos.system (logger, self.command, **self.kwargs)
        return loggedos.system (logger, self.command, env=self.env,
                                ignore_errors=self.ignore_errors)

class Copy (SerializedCommand):
    def __init__ (self, src, dest):
        self.src = src
        self.dest = dest
    def checksum (self, hasher):
        hasher (self.__class__.__name__)
        hasher (self.src)
        hasher (self.dest)
    def execute (self, logger):
        loggedos.copy2 (logger, self.src, self.dest)

class Link (SerializedCommand):
    def __init__ (self, src, dest):
        self.src = src
        self.dest = dest
    def checksum (self, hasher):
        hasher (self.__class__.__name__)
        hasher (self.src)
        hasher (self.dest)
    def execute (self, logger):
        loggedos.link (logger, self.src, self.dest)

class Symlink (SerializedCommand):
    def __init__ (self, src, dest):
        self.src = src
        self.dest = dest
    def checksum (self, hasher):
        hasher (self.__class__.__name__)
        hasher (self.src)
        hasher (self.dest)
    def execute (self, logger):
        loggedos.symlink (logger, self.src, self.dest)

class Rename (SerializedCommand):
    def __init__ (self, src, dest):
        self.src = src
        self.dest = dest
    def checksum (self, hasher):
        hasher (self.__class__.__name__)
        hasher (self.src)
        hasher (self.dest)
    def execute (self, logger):
        loggedos.rename (logger, self.src, self.dest)

class Mkdir (SerializedCommand):
    def __init__ (self, dir):
        self.dir = dir
    def checksum (self, hasher):
        hasher (self.__class__.__name__)
        hasher (self.dir)
    def execute (self, logger):
        loggedos.makedirs (logger, self.dir)

class Chmod (SerializedCommand):
    def __init__ (self, file, mode):
        self.file = file
        self.mode = mode
    def checksum (self, hasher):
        hasher (self.__class__.__name__)
        hasher (self.file)
        hasher (self.mode)
    def execute (self, logger):
        loggedos.chmod (logger, self.file, self.mode)

class Remove (SerializedCommand):
    def __init__ (self, file):
        self.file = file
    def checksum (self, hasher):
        hasher (self.__class__.__name__)
        hasher (self.file)
    def execute (self, logger):
        loggedos.remove (logger, self.file)

class Rmtree (SerializedCommand):
    def __init__ (self, file, ignore_errors=False):
        self.file = file
        self.ignore_errors = ignore_errors
    def checksum (self, hasher):
        hasher (self.__class__.__name__)
        hasher (self.file)
        hasher (self.ignore_errors)
    def execute (self, logger):
        loggedos.rmtree (logger, self.file, self.ignore_errors)

class Func (SerializedCommand):
    def __init__ (self, func, *args):
        self.func = func
        self.args = args
    def checksum (self, hasher):
        hasher (self.__class__.__name__)
        hasher (inspect.getsource (self.func))
        hasher (repr (self.args))
    def execute (self, logger):
        func = self.func.__name__
        logger.write_log ('invoking %(func)s ()\n' % locals (), 'command')
        return self.func (logger, *self.args)

class Message (SerializedCommand):
    def __init__ (self, message, message_type):
        self.message = message
        self.message_type = message_type
        self.is_checksummed = False
    def execute (self, logger):
        logger.write_log (self.message, self.message_type)
    def checksum (self, hasher):
        pass

class MapLocate (SerializedCommand):
    def __init__ (self, func, directory, pattern,
                  must_happen=False, silent=False,
                  find_func=misc.locate_files):
        self.func = func
        self.find_func = find_func
        self.directory = directory
        self.pattern = pattern
        self.must_happen = must_happen
        self.silent = silent
    def execute (self, logger):
        files = self.find_func (self.directory, self.pattern)
        message = 'MapLocate[%(directory)s] no files matching pattern: %(pattern)s\n' % self.__dict__
        logger.write_log (message, 'harmless')
        if self.must_happen and files == []:
            raise Exception ('MapLocate failed: ' + message)
        # huh, what is silent?
        func = self.func.__name__
        if self.silent:
            dir = self.directory
            pattern = self.pattern
            count = len (files)
            logger.write_log ('Mapping %(func)s () over %(dir)s/%(pattern)s (%(count)d files)\n' % locals (), 'action')
        for file_name in files:
            if not self.silent:
                logger.write_log ('Applying %(func)s () to %(file_name)s\n' % locals (), 'action')
            self.func (logger, file_name)
    def checksum (self, hasher):
        hasher (self.__class__.__name__)
        hasher (inspect.getsource (self.func))
        hasher (self.directory)
        hasher (self.pattern)

class Dump (SerializedCommand):
    def __init__ (self, *args, **kwargs):
        SerializedCommand.__init__ (self)
        self.args = args
        self.kwargs = kwargs
    def __repr__ (self):
        return 'Dump (%s)' % repr (self.args)
    def checksum (self, hasher):
        string, name = self.args
        hasher (self.__class__.__name__)
        hasher (name)
        try:
            # Aid development by using nicer string in checksum file
            lst = pickle.loads (string)
            human = '\n'.join (map ('='.join, lst))
            hasher (human)
        except:
            hasher (string)
    def execute (self, logger):
        string, name = self.args
        kwargs = self.kwargs
        mode = 'w'
        if sys.version.startswith ('3') and type (string) == bytes:
            mode += 'b'
        if 'mode' in self.kwargs:
            mode = kwargs['mode']
            del kwargs['mode']
        loggedos.dump_file (logger, string, name, mode, **kwargs)

class Substitute (SerializedCommand):
    '''Substitute RE_PAIRS in file NAME.

    If TO_NAME is specified, the output is sent to there.
    '''

    def __init__ (self, *args, **kwargs):
        SerializedCommand.__init__ (self)
        self.args = args
        self.kwargs = kwargs

    def __repr__ (self):
        return 'Substitute (%s)' % repr (self.args)

    def checksum (self, hasher):
        (re_pairs, name) = self.args
        hasher (self.__class__.__name__)
        hasher (name)
        for (src, dst) in re_pairs:
            hasher (src)
            hasher (dst)

    def execute (self, logger):
        (re_pairs, name) = self.args

        loggedos.file_sub (logger, re_pairs, name, **self.kwargs)
        
class Conditional (SerializedCommand):
    def __init__ (self, predicate, true_command, false_command=None):
        SerializedCommand.__init__ (self)
        self.predicate = predicate
        self.true_command = true_command
        self.false_command = false_command
    def checksum (self, hasher):
        hasher (self.__class__.__name__)
        hasher (inspect.getsource (self.predicate))
        if self.true_command:
            self.true_command.checksum (hasher)
        if self.false_command:
            self.false_command.checksum (hasher)
    def execute (self, logger):
        if self.predicate ():
            return self.true_command.execute (logger)
        elif self.false_command:
            return self.false_command.execute (logger)

class ShadowTree (SerializedCommand):
    def __init__ (self, src, dest, soft=False):
        self.src = src
        self.dest = dest
        self.soft = soft
    def execute (self, logger):
        '''Symlink files from SRC in TARGET recursively'''
        loggedos.shadow (logger, self.src, self.dest, self.soft)
    def checksum (self, hasher):
        hasher (self.__class__.__name__)
        hasher (self.src)
        hasher (self.dest)

class Chmod (SerializedCommand):
    def __init__ (self, *args):
        self.args = args
    def execute (self, logger):
        loggedos.chmod (logger, *self.args)
    def checksum (self, hasher):
        hasher (self.__class__.__name__)
        list (map (hasher, list (map (str, self.args))))

class PackageGlobs (SerializedCommand):
    def __init__ (self, root, suffix_dir, globs, dest):
        self.globs = globs
        self.root = root
        self.suffix_dir = suffix_dir
        self.dest = dest

    def checksum (self, hasher):
        hasher (self.__class__.__name__)
        hasher (''.join (self.globs))
        hasher (self.root)
        hasher (self.suffix_dir)
        hasher (self.dest)

    def execute (self, logger):
        root = self.root
        suffix_dir = self.suffix_dir
        dest = self.dest

        globs = list ()
        for f in self.globs:
            f = re.sub ('/+', '/', f)
            if f.startswith ('/'):
                f = f[1:]
            for exp in glob.glob (os.path.join (self.root, f)):
                globs.append (exp.replace (root, './').replace ('//', '/'))
        if not globs:
            globs.append ('no-globs-for-%(dest)s' % locals ())

        _v = logger.verbose_flag ()
        cmd = 'tar -C %(root)s/%(suffix_dir)s --ignore-failed --exclude="*~"%(_v)s -zcf %(dest)s ' % locals ()
        cmd += ' '.join (globs)
        loggedos.system (logger, cmd)

# FIXME
class ForcedAutogenMagic (SerializedCommand):
    def __init__ (self, package):
        self.package = package
        SerializedCommand.__init__ (self)
    def system (self, cmd, logger, env={}):
        env = self.package.get_substitution_dict (env)
        cmd = self.package.expand (cmd, env)
        return loggedos.system (logger, cmd, env=env)
    def checksum (self, hasher):
        hasher (self.__class__.__name__)
        hasher (inspect.getsource (ForcedAutogenMagic.execute))
        hasher (inspect.getsource (misc.path_find))
    def execute (self, logger):
        package = self.package
        autodir = package.expand ('%(autodir)s')
        PATH = package.expand ('%(PATH)s')
        if os.path.exists (os.path.join (autodir, 'autogen.sh')):
            s = open ('%(autodir)s/autogen.sh' % locals ()).read ()
            noconfigure = ' --help'
            if '--noconfigure' in s:
                noconfigure = ' --noconfigure' + noconfigure
            self.system ('cd %(autodir)s && NOCONFIGURE=1 sh autogen.sh %(noconfigure)s' % locals (),
                         logger)
        elif (os.path.exists (os.path.join (autodir, 'bootstrap'))
              and not 'CC=' in open (os.path.join (autodir, 'bootstrap')).read ()):
            self.system ('cd %(autodir)s && ./bootstrap' % locals (), logger)
        elif (os.path.exists (os.path.join (autodir, 'bootstrap.sh'))
              and not 'CC=' in open (os.path.join (autodir, 'bootstrap.sh')).read ()):
            self.system ('cd %(autodir)s && ./bootstrap.sh' % locals (), logger)
        else:
            libtoolize = misc.path_find (PATH, 'libtoolize')
            if libtoolize:
                s = open (libtoolize).read ()
                libtoolize = 'libtoolize --copy --force'
                # --automake is mandatory for libtool-1.5.2x, but breaks with libtool-2.2.x
                # --install is mandatory for libtool-2.2.x, but breaks with libtool-1.5.2x
                # mandatory means: so that config.guess, config.sub are refreshed iso removed
                if '--automake' in s:
                    libtoolize += ' --automake'
                if '--install' in s:
                    libtoolize += ' --install'
                if (os.path.isdir (os.path.join (autodir, 'ltdl'))
                    or os.path.isdir (os.path.join (autodir, 'libltdl'))):
                    libtoolize += ' --ltdl'
                self.system ('rm -rf %(autodir)s/libltdl %(autodir)s/ltdl && cd %(autodir)s && %(libtoolize)s'
                             % locals (), logger)
            aclocal_flags = ''
            for dir in package.aclocal_path ():
                d = package.expand (dir)
                if os.path.exists (d):
                    aclocal_flags += ' -I%(d)s' % locals ()
            headcmd = ''
            configure_ac = ''
            for c in ('configure.in','configure.ac'):
                try:
                    string = open ('%(autodir)s/%(c)s' % locals ()).read ()
                    configure_ac = c
                    m = re.search ('A[CM]_CONFIG_HEADER', string)
                    string = 0   ## don't want to expand string
                    if m:
                        headcmd = 'cd %(autodir)s && autoheader %(aclocal_flags)s' % locals ()
                        break
                except IOError:
                    pass
            if configure_ac:
                self.system ('''
cd %(autodir)s && aclocal %(aclocal_flags)s
%(headcmd)s
cd %(autodir)s && autoconf %(aclocal_flags)s
''' % locals (), logger)
            if os.path.exists ('%(autodir)s/Makefile.am' % locals ()):
                self.system ('cd %(autodir)s && automake --add-missing --copy --foreign' % locals (), logger)

class AutogenMagic (ForcedAutogenMagic):
    def execute (self, logger):
        package = self.package
        if not os.path.exists (package.expand ('%(autodir)s/configure')):
            if (os.path.exists (package.expand ('%(autodir)s/configure.ac'))
                or os.path.exists (package.expand ('%(autodir)s/configure.in'))
                or (not os.path.exists (package.expand ('%(autodir)s/Makefile'))
                    and not os.path.exists (package.expand ('%(srcdir)s/Makefile'))
                    and not os.path.exists (package.expand ('%(builddir)s/Makefile'))
                    and not os.path.exists (package.expand ('%(autodir)s/makefile'))
                    and not os.path.exists (package.expand ('%(srcdir)s/makefile'))
                    and not os.path.exists (package.expand ('%(builddir)s/makefile'))
                    and not os.path.exists (package.expand ('%(srcdir)s/makefile'))
                    and not os.path.exists (package.expand ('%(srcdir)s/SConstruct')))):
                ForcedAutogenMagic.execute (self, logger)

class CreateShar (SerializedCommand):
    def __init__ (self, **kwargs):
        self.kwargs = kwargs
    def checksum (self, hasher):
        hasher (repr (('CreateShar', self.kwargs)))
    def execute (self, logger):
        logger.write_log ('Creating shar file from %s\n' % repr (self.kwargs), 'info') 
        name = self.kwargs['name']
        pretty_name = self.kwargs['pretty_name']
        release = self.kwargs['release']
        shar_file = self.kwargs['shar_file']
        shar_head = self.kwargs['shar_head']
        tarball = self.kwargs['tarball']
        version = self.kwargs['version']
        target_cpu = self.kwargs['target_cpu']

        length = os.stat (tarball)[6]
        base_file = os.path.split (tarball)[1]
        script = loggedos.read_file (logger, shar_head)
        header_length = 0
        _z = misc.compression_flag (tarball)
        header_length = len (script % locals ()) + 1
        used_in_sharhead = '%(base_file)s %(name)s %(pretty_name)s %(version)s %(release)s %(header_length)s %(target_cpu)s %(_z)s'
        used_in_sharhead % locals ()
        loggedos.dump_file (logger, script % locals (), shar_file)
        loggedos.system (logger, 'cat %(tarball)s >> %(shar_file)s' % locals ())
        loggedos.chmod (logger, shar_file, octal.o755)
        loggedos.system (logger, 'rm %(tarball)s' % locals ())