| #!/usr/bin/env 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'), |
| ('.', '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 |
| ### |