#!/usr/bin/env python import os import re import shutil import sys import stat FILES_HDR = [ ('.', 'serf'), ('.', 'serf_bucket_types'), ('.', 'serf_bucket_util'), ] LIB_FILES = [ ('.', 'context'), ('.', 'incoming'), ('.', 'outgoing'), ('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'), ('auth', 'auth'), ('auth', 'auth_basic'), ('auth', 'auth_digest'), ('auth', 'auth_kerb'), ('auth', 'auth_kerb_gss'), ] TEST_DEPS = [ ('test', 'CuTest'), ('test', 'test_util'), ('test', 'test_context'), ('test', 'test_buckets'), ('test', 'test_ssl'), ] TEST_HDR_FILES = [ ('test', 'CuTest'), ('test', 'test_serf'), ] TEST_FILES = [ ('test', 'serf_get'), ('test', 'serf_response'), ('test', 'serf_request'), ('test', 'serf_spider'), ('test', 'test_all'), ] 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 = {} commands = [] for arg in argv[1:]: idx = arg.find('=') if idx > 0: start = arg.rfind('-', 0, idx) if start > 0: params[arg[start+1:idx]] = arg[idx+1:].strip() else: func = globals().get('cmd_' + arg) if func: commands.append(func) else: print('ERROR: unknown argument: ' + arg) usage() if not commands: usage() for func in commands: try: func(params) except: print('ERROR: exception:') print(sys.exc_info()[1]) print("") 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-0', 'la'), False) def cmd_install(param): builder = Builder(param) builder.install_target(File('.', 'libserf-0', 'la'), False) def cmd_check(param): builder = Builder(param) for dirpath, fname in TEST_FILES: builder.build_target(File(dirpath, fname, 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("", result) # run the test suite based on the CuTest framework result = os.system(os.path.join('test', 'test_all')) if result: raise TestError(case, result) def cmd_clean(param): targets = [File(dirpath, fname, 'o') for dirpath, fname in LIB_FILES] targets += [File(dirpath, fname, 'lo') for dirpath, fname in LIB_FILES] targets += [File('.', 'libserf-0', 'la')] targets += [File(dirpath, fname, 'o') for dirpath, fname in TEST_FILES] targets += [File(dirpath, fname, 'lo') for dirpath, fname in TEST_FILES] targets += [File(dirpath, fname, None) for dirpath, fname in TEST_FILES] targets += [File(dirpath, fname, 'o') for dirpath, fname in TEST_DEPS] targets += [File(dirpath, fname, 'lo') for dirpath, fname in TEST_DEPS] clean = [file for file in targets if file.mtime] if clean: sys.stdout.write('Cleaning %d files... ' % len(clean)) for i in clean: if i.mtime: os.remove(i.fname) print('done.') else: print('Clean.') class Builder: def __init__(self, params): if 'apr' in params: self.apr = APRConfig(params['apr']) self.apu = APUConfig(params['apr']) else: 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', 'serf-0') except: self.libdir = '/usr/local/lib' self.includedir = '/usr/local/include/serf-0' 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-0', '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 testhdrs = hdrs testhdrs += [File(dirpath, fname, 'h') for dirpath, fname in TEST_HDR_FILES] testdeps = [File(dirpath, fname, 'c') for dirpath, fname in TEST_DEPS] testobjs = [File(dirpath, fname, 'lo') for dirpath, fname in TEST_DEPS] for testsrc, testobj in zip(testdeps, testobjs): self._add_compile(testsrc, testobj, testhdrs) 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) # test_all requires extra dependencies if fname == "test_all": cmd = '%s --silent --mode=link %s %s -static -o %s %s %s %s' % ( self.LIBTOOL, self.CC, self.LDFLAGS, prog.fname, lib.fname, ' '.join([l.fname for l in [obj] + testobjs]), self.LIBS) self._add_dep(prog, [lib, obj] + testobjs, cmd) else: 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 dep in self.deps 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: locations = [search_dir, os.path.join(search_dir, 'bin')] else: locations = self.locations for dirname in 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 __eq__(self, other): return 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) ### ### TODO: ### * obey DESTDIR ### * arfrever says LDFLAGS is passed twice ### * be able to specify libdir and includedir ###