#!/usr/bin/python import os import re import shutil import sys import stat FILES_HDR = [ ('.', 'serf'), ('.', 'serf_bucket_types'), ('.', 'serf_bucket_util'), ('.', 'serf_declare'), ] LIB_FILES = [ ('.', 'context'), ('buckets', 'aggregate_buckets'), ('buckets', 'request_buckets'), ('buckets', 'buckets'), ('buckets', 'simple_buckets'), ('buckets', 'file_buckets'), ('buckets', 'mmap_buckets'), ('buckets', 'socket_buckets'), ('buckets', 'response_buckets'), ('buckets', 'headers_buckets'), ('buckets', 'allocator'), ('buckets', 'dechunk_buckets'), ('buckets', 'deflate_buckets'), ('buckets', 'limit_buckets'), ('buckets', 'ssl_buckets'), ('buckets', 'barrier_buckets'), ('buckets', 'chunk_buckets'), ] TEST_FILES = [ ('test', 'serf_get'), ('test', 'serf_response'), ('test', 'serf_request'), ('test', 'serf_spider'), ] TESTCASES = [ ('test/testcases', 'simple.response'), ('test/testcases', 'chunked-empty.response'), ('test/testcases', 'chunked.response'), ('test/testcases', 'chunked-trailers.response'), ('test/testcases', 'deflate.response'), ] def main(argv): params = {} cmd = None for i in argv[1:]: idx = i.find('=') if idx > 0: start = i.rfind('-', 0, idx) if start > 0: params[i[start+1:idx]] = i[idx+1:].strip() elif not cmd: cmd = i if not cmd: cmd = 'build' func = globals().get('cmd_' + cmd) if not func: print 'ERROR: no such command:', cmd usage() try: func(params) except Exception, e: print e usage() def usage(): ### print something print 'serfmake [cmd] [options]' print 'Commands:' print '\tbuild\tBuilds (default)' print '\tcheck\tRuns test cases' print '\tinstall\tInstalls serf into PREFIX' print '\tclean\tCleans' print 'Options:' print '\t--with-apr=PATH\tprefix for installed APR and APR-util' print '\t\t\t(needs apr-1-config and apu-1-config; will look in PATH)' print '\t--prefix=PATH\tinstall serf into PATH (default: /usr/local)' print 'Quick guide:' print '\tserfmake --prefix=/usr/local/serf --with-apr=/usr/local/apr install' sys.exit(1) def cmd_build(param): builder = Builder(param) builder.build_target(File('.', 'libserf-1', 'la'), False) def cmd_install(param): builder = Builder(param) builder.install_target(File('.', 'libserf-1', 'la'), False) def cmd_check(param): builder = Builder(param) builder.build_target(File('test', 'serf_response', None), False) for dirpath, fname in TESTCASES: case = os.path.join(dirpath, fname) print '== Testing %s ==' % case result = os.system('%s %s' % (os.path.join('test', 'serf_response'), case)) if result: raise TestError(case, result) def cmd_clean(param): clean = [File(dirpath, fname, 'o') for dirpath, fname in LIB_FILES] clean += [File(dirpath, fname, 'lo') for dirpath, fname in LIB_FILES] clean += [File('.', 'libserf-1', 'la')] clean += [File(dirpath, fname, 'o') for dirpath, fname in TEST_FILES] clean += [File(dirpath, fname, 'lo') for dirpath, fname in TEST_FILES] clean += [File(dirpath, fname, None) for dirpath, fname in TEST_FILES] for i in clean: if i.mtime: os.remove(i.fname) class Builder: def __init__(self, params): try: self.apr = APRConfig(params['apr']) self.apu = APUConfig(params['apr']) except: self.apr = APRConfig(None) self.apu = APUConfig(None) try: self.libdir = os.path.join(params['prefix'], 'lib') self.includedir = os.path.join(params['prefix'], 'include') except: self.libdir = '/usr/local/lib' self.includedir = '/usr/local/include' self.load_vars() self.load_deps() def load_vars(self): self.CC = self.apr.get_value('CC', '--cc') self.CFLAGS = self.apr.get_value('CFLAGS', '--cflags') self.CPPFLAGS = self.apr.get_value('CPPFLAGS', '--cppflags') self.LIBTOOL = self.apr.get_value('LIBTOOL', '--apr-libtool') self.LDFLAGS = self.apr.get_value('LDFLAGS', '--ldflags') \ + ' ' + self.apu.get_value('LDFLAGS', '--ldflags') self.INCLUDES = '-I%s -I%s -I%s' % ( '.', self.apr.get_value(None, '--includedir'), self.apu.get_value(None, '--includedir'), ) if os.getenv('EXTRA_INCLUDES'): self.INCLUDES += ' -I' + os.getenv('EXTRA_INCLUDES') self.LIBS = self.apu.get_value(None, '--link-libtool') \ + ' ' + self.apu.get_value(None, '--libs') \ + ' ' + self.apr.get_value(None, '--link-libtool') \ + ' ' + self.apr.get_value(None, '--libs') \ + ' -lz -lssl -lcrypto' self.MODE = 644 def load_deps(self): self.deps = { } hdrs = [File(dirpath, fname, 'h') for dirpath, fname in FILES_HDR] libfiles = [File(dirpath, fname, 'c') for dirpath, fname in LIB_FILES] libobjs = [File(dirpath, fname, 'lo') for dirpath, fname in LIB_FILES] for src, obj in zip(libfiles, libobjs): self._add_compile(src, obj, hdrs) self.hdrs = hdrs lib = File('.', 'libserf-1', 'la') cmd = '%s --silent --mode=link %s %s -rpath %s -o %s %s %s' % ( self.LIBTOOL, self.CC, self.LDFLAGS, self.libdir, lib.fname, ' '.join([l.fname for l in libobjs]), self.LIBS) self._add_dep(lib, libobjs, cmd) # load the test program dependencies now for dirpath, fname in TEST_FILES: src = File(dirpath, fname, 'c') obj = File(dirpath, fname, 'lo') prog = File(dirpath, fname, None) self._add_compile(src, obj, hdrs) cmd = '%s --silent --mode=link %s %s -static -o %s %s %s %s' % ( self.LIBTOOL, self.CC, self.LDFLAGS, prog.fname, lib.fname, obj.fname, self.LIBS) self._add_dep(prog, [lib, obj], cmd) def _add_compile(self, src, obj, hdrs): cmd = '%s --silent --mode=compile %s %s %s %s -c -o %s %s' % ( self.LIBTOOL, self.CC, self.CFLAGS, self.CPPFLAGS, self.INCLUDES, obj.fname, src.fname) self._add_dep(obj, [src] + hdrs, cmd) def _add_dep(self, target, deps, cmd): if target.mtime: for dep in deps: if self.deps.has_key(dep) \ or (dep.mtime and dep.mtime > target.mtime): # a dep is newer. this needs to be rebuilt. break else: # this is up to date. don't add it to the deps[] structure. return # else non-existent, so it must be rebuilt. # register the dependency so this will get built self.deps[target] = deps, cmd def build_target(self, target, dry_run): dep = self.deps.get(target) if not dep: # it's already up to date. all done. return for f in dep[0]: subdep = self.deps.get(f) if subdep: self.build_target(f, dry_run) # build the target now print dep[1] if not dry_run: result = os.system(dep[1]) if result: raise BuildError(dep[1], result) # FALLTHROUGH # it's a dry run. pretend we built the target. del self.deps[target] return 0 def install_target(self, target, dry_run): self.build_target(target, dry_run) # install the target now if not dry_run: try: os.makedirs(self.includedir) os.makedirs(self.libdir) except: pass for f in self.hdrs: shutil.copy(f.fname, self.includedir) cmd = '%s --silent --mode=install %s -c -m %d %s %s' % ( self.LIBTOOL, '/usr/bin/install', self.MODE, target.fname, self.libdir) result = os.system(cmd) if result: raise BuildError(cmd, result) # FALLTHROUGH return 0 class ConfigScript(object): script_name = None locations = [ '/usr/bin', '/usr/local/bin', '/usr/local/apache2/bin', ] def __init__(self, search_dir): if search_dir: self.locations.append(search_dir) self.locations.append(os.path.join(search_dir, 'bin')) for dirname in self.locations: bin = os.path.join(dirname, self.script_name) if os.access(bin, os.X_OK): self.bin = bin break else: raise ConfigScriptNotFound(self.script_name) def get_value(self, env_name, switch): if env_name and os.getenv(env_name): return os.getenv(env_name) return os.popen('%s %s' % (self.bin, switch), 'r').read().strip() class APRConfig(ConfigScript): script_name = 'apr-1-config' class APUConfig(ConfigScript): script_name = 'apu-1-config' class File: def __init__(self, dirpath, fname, ext): if ext: self.fname = os.path.join(dirpath, fname + '.' + ext) else: self.fname = os.path.join(dirpath, fname) try: s = os.stat(self.fname) except OSError: self.mtime = None else: self.mtime = s[stat.ST_MTIME] def __cmp__(self, other): return cmp(self.fname, other.fname) def __hash__(self): return hash(self.fname) class BuildError(Exception): "An error occurred while building a target." class TestError(Exception): "An error occurred while running a unit test." class ConfigScriptNotFound(Exception): def __init__(self, value): self.value = "ERROR: A configuration script was not found: " + value def __str__(self): return self.value if __name__ == '__main__': main(sys.argv)